开发者

Learning TDD with a simple example

I'm attempting to learn TDD but having a hard time getting my head around what / how to test with a little app I need to write.

The (simplified somewhat) spec for the app is as follows:

It needs to take from the user the location of a csv file, the location of a word document mailmerge template and an output location.

The app w开发者_StackOverflow社区ill then read the csv file and for each row, merge the data with the word template and output to the folder specified.

Just to be clear, I'm not asking how I would go about coding such an app as I'm confident that I know how to do it if I just went ahead and started. But if I wanted to do it using TDD, some guidance on the tests to write would be appreciated as I'm guessing I don't want to be testing reading a real csv file, or testing the 3rd party component that does the merge or converts to pdf.

I think just some general TDD guidance would be a great help!


I'd start out by thinking of scenarios for each step of your program, starting with failure cases and their expected behavior:

  • User provides a null csv file location (throws an ArgumentNullException).

  • User provides an empty csv file location (throws an ArgumentException).

  • The csv file specified by the user doesn't exist (whatever you think is appropriate).

Next, write a test for each of those scenarios and make sure it fails. Next, write just enough code to make the test pass. That's pretty easy for some of these conditions, because the code that makes your test pass is often the final code:

public class Merger { 
    public void Merge(string csvPath, string templatePath, string outputPath) {
        if (csvPath == null) { throw new ArgumentNullException("csvPath"); }
    }
}

After that, move into standard scenarios:

  • The specified csv file has one line (merge should be called once, output written to the expected location).

  • The specified csv file has two lines (merge should be called twice, output written to the expected location).

  • The output file's name conforms to your expectations (whatever those are).

And so on. Once you get to this second phase, you'll start to identify behavior you want to stub and mock. For example, checking whether a file exists or not - .NET doesn't make it easy to stub this, so you'll probably need to create an adapter interface and class that will let you isolate your program from the actual file system (to say nothing of actual CSV files and mail-merge templates). There are other techniques available, but this method is fairly standard:

public interface IFileFinder { bool FileExists(string path); }

// Concrete implementation to use in production
public class FileFinder: IFileFinder {
    public bool FileExists(string path) { return File.Exists(path); }
}

public class Merger {
    IFileFinder finder;
    public Merger(IFileFinder finder) { this.finder = finder; }
}

In tests, you'll pass in a stub implementation:

[Test]
[ExpectedException(typeof(FileNotFoundException))]
public void Fails_When_Csv_File_Does_Not_Exist() {

    IFileFinder finder = mockery.NewMock<IFileFinder>();
    Merger      merger = new Merger(finder);
    Stub.On(finder).Method("FileExists").Will(Return.Value(false));

    merger.Merge("csvPath", "templatePath", "outputPath");
}


Simple general guidance:

  • You write unit tests first. At the beginning they all fail.
  • Then you go into the class under test and write code until tests related to each method pass.
  • Do this for every public method of your types.

By writing units test you actually specify the requirements but in another form, easy to read code.

Looking to it from another angle: when you receive a new black boxed class and unit tests for it, you should read the unit tests to see what the class does and how it behaves.

To read more about unit testing I recommend a very good book: Art Of Unit Testing

Here are a couple links to articles on StackOverflow regarding TDD for more details and examples:

  • Link1
  • Link2


To be able to unit test you need to decouple the class from any dependencies so you can effectively just test the class itself.

To do this you'll need to inject any dependencies into the class. You would typically do this by passing in an object that implements the dependency interface, into your class in the constructor.

Mocking frameworks are used to create a mock instance of your dependency that your class can call during the test. You define the mock to behave in the same way as your dependency would and then verify it's state at the end of the test.

I would recommend having a play with Rhino mocks and going through the examples in the documentation to get a feel for how this works.

http://ayende.com/projects/rhino-mocks.aspx

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜