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.