Beginning some FTP development
Today we’re going to do a little bit of TDD on FTP. As I think about turning an automated battlebot loose on my web site, I feel the need to be sure the bot is reasonably bug-free. So we decided to set up localhost FTP and test some code.
The basic test idea is what we might call a “spike test”. We’re just trying to learn a bit about the elements of checking folders and running FTP, thinking about what our program will ultimately want to do. We’ll be following what I believe to be an approach similar to Keith Braithwaite’s “TDD as if you meant it”, writing all the code inside the test class. We’ll extract clsses and methods later, after we get closer to working the real problem.
We imagine two folders, _source
and _target
that we’re going to synchronize with FTP. I just created a folder to put all this in, and two sub-folders of those names. I touched two files in _source
.
First, we test whether the target folder is empty, because later we’re going to test whether it has files in it:
def test_connect_finds_no_files
ftp = ftp_connected_to_target
list = ftp.list('*')
ftp.close
assert_equal(0,list.length)
end
def ftp_connected_to_target
ftp = Net::FTP.new('localhost')
ftp.login(Passwords::USER,Passwords::PASSWORD)
ftp.chdir('programming/test_ftp/_target')
ftp
end
Not much to this. We have a function that returns an FTP instance which is connected to our desired folder. We get a list of the files in that folder and check to see that there aren’t any. This test will fail after the first time we run the tests but for now it works.
Then we upload our files:
def test_upload_files
ftp = ftp_connected_to_target
source = Dir.glob('/Users/ron/programming/test_ftp/_source/*')
source.each do |file_name|
ftp.puttextfile(file_name)
end
ftp.close
target = Dir.glob('/Users/ron/programming/test_ftp/_target/*')
assert_equal(2, target.length)
end
Easy enough, and we decided to check directly that the files are there. We could have used our ftp connection but we felt this was a more solid (and efficient) test. We’re not testing the contents, or even the file names. I confess that we did look in the folder. So sue me.
Running this the second time, the empty folder test fails so we build setup:
def setup
target = Dir.glob('/Users/ron/programming/test_ftp/_target/*')
target.each do |file_name|
File.delete(file_name)
end
end
Now the tests run correctly repeatedly. We realize, though, that we care whether FTP can be used to tell us correctly whether files are transferred. We’re not sure we’l need this but we might. Anyway, we’re here to learn so we write this:
def test_connect_finds_no_files
ftp = ftp_connected_to_target
list = ftp.list('*')
ftp.close
assert_equal(0,list.length)
end
This works as well (of course), leaving us with our whole test file looking like this:
require "minitest/autorun"
require "net/ftp"
require "./passwords"
class Test_FTP < Minitest::Test
def setup
target = Dir.glob('/Users/ron/programming/test_ftp/_target/*')
target.each do |file_name|
File.delete(file_name)
end
end
def test_target_is_empty
target = Dir.glob('/Users/ron/programming/test_ftp/_target/*')
assert_equal(0,target.length)
end
def test_connect_finds_no_files
ftp = ftp_connected_to_target
list = ftp.list('*')
ftp.close
assert_equal(0,list.length)
end
def test_upload_files
ftp = ftp_connected_to_target
source = Dir.glob('/Users/ron/programming/test_ftp/_source/*')
source.each do |file_name|
ftp.puttextfile(file_name)
end
ftp.close
target = Dir.glob('/Users/ron/programming/test_ftp/_target/*')
assert_equal(2, target.length)
end
def ftp_connected_to_target
ftp = Net::FTP.new('localhost')
ftp.login(Passwords::USER,Passwords::PASSWORD)
ftp.chdir('programming/test_ftp/_target')
ftp
end
end
This isn’t bad for the work of 90 minutes, together with the usual chit-chat about the state of the world and having the Ghost of the Halting Problem looking over one’s shoulder.
Our mission was to learn some FTP-related operations and to get them documented with a little testing. We feel we managed that nicely.