I’ve been doing either post-code unit testing or full TDD since around 2009 (apart for 1 year at a more rough-and-ready development house) and there are some patterns that I’ve picked up that I think produce a suite of tests that is easy to maintain and provide good coverage.
I’d hesitate to claim any one of these patterns as anything I’ve created, it’s just the distillation of ideas that I’ve come across that I’ve found useful to repeatedly apply. By sharing them I hope you might find some of them useful as well (also I can point my team at this article). It is probably worth a shout out to Waseem Qureshi as I suspect some of them came from his architecture work at Haymarket.
Use NUnit & Moq
NUnit has a huge body of knowledge out there and with NUnit3 support of async it’s just very, very easy to use. Moq gives you type-safe mocking and can be picked up very quickly.
Have one unit test project in each solution.
- You have a single results set to run metrics against in a CI environment.
- It’s trivial to find and run all the tests if you’re new to the project or making a quick change.
- Note: Long running / integration tests may need a different assembly (see open questions), automation tests definitely will.
Mirror your namespace/project structure in this project. Fixtures should be called <ClassName>Tests
Or at least pick a common naming suffix – my preference is for “Tests”.
- If you do this from the beginning you’ll rarely write two test fixtures for the same code (seen it happen)
- Any fixture naming pattern should have a suffix for easy searching / visual scanning.
- You can find the test fixture for any class very quickly with Ctrl+Shift+T and typing the current class name (you do have ReSharper don’t you?)
Name your tests <MethodUnderTest>_<Situation>_<ExpectedResult>[_MoreExpected]
- It is incredibly clear to see what the method under test is supposed to do – and what is wrong if the test fails.
- Usually typing out the situation makes you think of another couple of test cases – which you can add immediately in the same form.
Use SetUp to create a clean instance with mocked dependencies
Individual tests should then set up how those mocks should behave and the data they need. Useful along with factory methods below.
- Keeping SetUp just for a clean testable instance and putting data/mock behaviour in the test means that tests are independently available to change – any data or mock behaviour that you set up will have to be different for some tests or you’re not testing everything!
This may cause problems with parallel test running in future versions of NUnit. However your fixtures should still be independent of each other so fixtures can run in parallel.
Sometimes you want to write some longer running tests – should they go into a separate “IntegrationTests” project or be tagged as long running and avoided during most test runs?
Code coverage – is it worth it?