The key to reduce this time is writing maintainable unit tests - and the following rules should help here:
(Note that this is work in progress – I will edit rules and add new ones any time)
Unit Test Design Rules
Every test method should have exactly one goal
- Before writing a test, think about what exactly you want to test
- Don't handle different test cases with a single test method - use multiple methods instead
- Every test must have at least one assertion
The name of the test method should reflect its purpose
- The method name should be a summary of the test case
- With TestNG, you may use @Test(description) instead
- And, please: don't prefix methods with "test"…
A test should be easy to read
- A test that reads like a specification or DSL is easy to understand and therefore easy to maintain
- Use the GIVEN-WHEN-THEN template
- Encapsulate complex GIVEN and WHEN clauses in private methods
A test should be independent, thread-safe, and free of side effects
- Tests with dependencies or side effects lead to hard-to-find errors
- Tests that require a database must not be data dependent (no assumptions about existing data) and must not leave traces in the database
- Don't ever use TestNG's @Test(dependsOnMethods) attribute
- Some frameworks support parallel test execution – better be prepared!
Don't use inheritance
- Test classes should be final, so that tests cannot be overwritten or reused by subclasses
- You may use abstract superclasses for easier code reuse, but these classes must not contaqin any tests
Use mocking - but use it with care
- Mocking remote interfaces and external resources will improve test performance and stability
- But mocking adds complexity to your test!
- Mock only things that really need to be mocked (don't use all features of your mocking framework just because you can)
Avoid using in-memory-databases for tests
- If your test is dependent of a database (it really is?) then you should use the real target platform - otherwise the test won't have much value
- If your test is database-independent, then you should try to mock all database accesses and run without any database
Tools for Unit Testing
JUnit | Still my favorite unit testing framework - small, simple, stable |
TestNG | Has a lot more features than JUnit (for me, the most important are data providers and parallel test execution), but that comes with great complexity (what looks like a bug in your code or test may turn out to be a bug in the testing framework). I try to stick to JUnit and use TestNG only for complex requirements. |
Hamcrest | This is a great extension of JUnit and TestNG, since the assert statements of these frameworks are very poor (JUnit even uses the "wrong" order of arguments). It greatly increases the readability of assertions (DSL-like). |
FEST | Provides fluent assertions, which are even nicer than Hamcrest (and don’t have those issues with generics) |
Easymock | I use only very basic mocking features, so possibly any mocking framework would suffice. Easymock was the first one I looked at, it has a nice and intuitive API - so I took it without evaluating any other. |