Kash Farooq's software development blog

.NET Developer

TDD: Using an Action or a Func to test the hard to test

Posted by Kash Farooq on August 24, 2009

More uses for Actions (or Funcs).

Consider the code:

public class SystemUnderTest {
    public void SaveToFile(string data) {
    //
    //Some code
    //
    File.WriteAllText(@"myfile.txt",data);
    //
    //Some code
    //
  }
}

It writes to a file. We could create a unit test that lets the code write to file, then reads the file back in to make sure the file was created properly – but writing to the file system can hardly be considered a unit test.

So, what do we do? How do you test that the data you sent into SaveToFile is being written to a file with the correct filename.

You can pass in an Action. Let’s refactor the code:

public class SystemUnderTest {
  private readonly Action<string,string> writeAction;

  public SystemUnderTest (Action<string,string> writeAction) {
    this.writeAction = writeAction;
  }

  public SystemUnderTest ():this(File.WriteAllText) {}

  public void SaveToFile(string data) {
    //
    //Some code
    //
    writeAction(@"myfile.txt", data);
    //
    //Some code
    //
  }
}

In the constructor, I am passing in an Action that takes two string parameters corresponding to the signature of File.WriteAllText. In the default constructor I can set writeAction to the File.WriteAllText method.

Now, the corresponding test can be written as follows:

[Test]
public void EnsureDataSavedToCorrectFile() {
  string filenameUsed = "";
  string dataSentToFile = "";
  Action<string, string> writeAction = (filename, data) => {
                                                          filenameUsed = filename;
                                                          dataSentToFile = data;
                                                      };
  SystemUnderTest systemUnderTest = new SystemUnderTest(writeAction);
  systemUnderTest.SaveToFile("MyData");

  Assert.That(filenameUsed,Is.EqualTo("myfile.txt"));
  Assert.That(dataSentToFile,Is.EqualTo("MyData"));
}

This technique is similar to Using a Func instead of a Factory.
Note that the method I wanted to mock out returned void. If the method returned, say, bool, then I would need to use a Func rather than an Action.

Also, if you have a class that uses several of these hard to test methods, you will quickly end up with a very long parameter list for your constructor. You should consider introducing a parameter object.

Advertisements

Sorry, the comment form is closed at this time.

 
%d bloggers like this: