Maybe the biggest thing since the rise of object oriented programming is test driven development. It’s also as hard to “get”. To help you on your journey, here some tips:
- Have patience. Good unit testing need to be learned (like OOP, also see “TDD, What a bitch”). Use code-reviews of good unit-tests to learn good practices.
- Again, have patience. Don’t skip unit-testing after some time. If you need to change tests often try to find the reason why and solve it. In many cases it’s actually a design issue (see next point). Everything falls with cleanly separated (and therefore testable) layers and DRY code. The need to reduce the dependencies for testing using mocks/stubs. “Duplicate code always represents a missing abstraction.” Robert C. Martin in Clean Code Tips
- Group tests by the user-story they are testing. This ensures that you test the right thing and everything that belongs together is in one file. Use user-stories to define what feature is needed. Use BDD-Specs to describe the details of how a feature should behave. A test is simply a specification translated to code.
- Tests should not depend on each other. Each test should be able to run correctly without any other test to be executed beforehand.
- Tests need to be short (no scrolling). A better name for unit tests might be microtests. They just check one thing per test case. This helps to precisely identify where the bug is located (no need for debugging). It also helps to avoid brittle tests ( = tests that need to change often). If tests are too long, there is a good chance that the code under test has too many responsibilities and need to be split.
- Run microtests as often as possible (best after each compile). Therefore make tests fast. Don’t test the database if avoidable. Don’t test more then one component at a time. More complicated, longer running tests (usually integration tests) can be run over night using an continuous integration server.
- Microtests can be used to speed up implementation (“Need for speed”). They are a replacement for starting the whole system to check out whether the implementation works correctly. No monkey-testing no more!
- While writing tests, think of everything that might get wrong (e.g. test whether code throws exception on invalid input, …). Many people tend check only whether the code behave well then it gets correct input. However code should always behave correctly (e.g. not crash) independent on the input it gets. This helps to write more robust code (again less debugging).
- Write tests before writing the code. It helps to be more clear about the requirements and help to focus the programming efforts. In software development typing is never the bottleneck. Thinking is. Tests make development faster because they help to focus: “How TDD and Pairing Increase Production” and “They’re Called Microtests”
- Let the tests drive the design of your API. Also see “Unit tests: An API design tool“
- If you like to read offline, a really good book on TDD is called “The Art Of Unit Testing” written by Roy Osherove (the creator of TypeMock)
- Another great one is “growing object-oriented software, guided by tests”
- Some great articles about BDD on Dan North’s blog (the creator of BDD):
- To make unit testing feasible, the system have to be structured clearly:
- Keep everything DRY (dont repeat yourself). Don’t tolerate _any_ code duplication.
- Keep the domain-model (business logic) free of any implementation details (dependencies on UI, Persistence, Infrastructure (e.g. IoC) , etc). Eric Evans is promoting this with DDD (Domain-driven-development). His book “Domain-Driven Design: Tackling Complexity in the Heart of Software” is a must read.
- For each technical concern create an abstraction (UI is one, Persistence is another, …).
- “10 Papers Every Programmer Should Read” by Michael Feathers
- Udi Dahan also has some great ideas on DDD. Be sure to watch his presentation “Making Roles Explicit”. More information are available on his blog: