Writing just another best practice list about unit testing is easy, but working according these recommendations is a more difficult task. In the project described in A Library to Empirically Estimate Big-O Time Efficiency and Check Results of Analysis in JUnit Tests I followed my own tips. All the source code in this project has been developed with 100% TDD and applied most of the best practices. Just 'Use Parameterized Tests' and 'Structure All Test Cases' has not been applied, because it was not needed.
Always Write Isolated Test Cases
The order of execution has to be independent between test cases. This gives you the chance to rearrange the test cases in clusters (e.g. short-, long-running) and retest single test cases.
Test One Thing Only in One Test Case
Each test should focus on one aspect of an isolated method and/or class. For integration test this goal may be difficult to achieve, but also here trying to focus on single aspects is helping. This means that you usually have just one assert per test case. More than one asserts are always a hit that your test doesn't focus on one aspect or functionality.
Use a Single Assert Method per Test Case
Don't test too many different things at once! If you use just one Assert it's easier. This increases the number of test cases and the single test better to maintain. If one or more tests fail, you may find the root cause in a easier way.
Use a Naming Convention for Test Cases
A clear naming convention like: <Method-Name Under Test>_<Scenario>_<Expected-Outcome>. This helps to avoid comments and increases the maintainability and in the case a test fails you know faster what functionality has been broken.
Use the Arrange-Act-Assert Style or Given-When-Then Style
Use separated blocks ("ARRANGE", "ACT" and "ASSERT") help to improve maintainability and use comments to mark these blocks. An alternative is the ("given", "when" and "then") style. This gives more structure to your unit tests.
Avoid the Expected Exception Tests
Maybe @Test(expected = ArithmeticException.class) doesn't test what it claims to test. Sometimes it happens that the exception is thrown some were else in your code (this happened to me some time ago).
Structure All Test Cases
Like Short-/Long-Running, Integration-/Unit-tests, but don't use test suits to control order of execution. When you have hundreds of test cases you don't like to wait several minutes till all tests are ready. Especially integration test may be slow, so separate them from the unit tests.
Use Descriptive Messages in Assert Methods
Describe the WHY and not the WHAT, not like Assert.assertEquals( a, b, "must be equal"). This helps to avoid to much comments in the test cases and increases the maintainability.
Measure Code Coverage to Find Missing Test Cases
Best indicator to find out what is not tested, but don't be to sure that the code works.
Don't Forget to Refactor the Test Code
Also maintain your test code (especially when after refactoring the code under test).
Limit Use of Mocks
In some cases absolutely necessary, but with better design stubs should be enough.
Use Parameterized Tests
They can help avoid code duplication and the best is the business gives you the data.
|| Oct 21, 2013
|| Markus Sprunck
|| first version
|| Aug 5, 2014
|| Markus Sprunck
|| add additional explanation
| 2.1|| Nov 29, 2014|| Markus Sprunck|| improve description and structure|