-
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
How to write good tests
Crafting tests for our software is good. But really, achieving good tests nowadays is something just as important.
Give some love to the test code by following some opinionated principles:
To do that apply merciless refactoring, just as one will do for production code. Otherise it will be like creating the dreaded legacy code on the test side. If tests cannot be easily refactored so is the production code, hence leading to legacy code. Always follow the route of the brave refacorer.
Avoid coding a tautology (where e.g. the test code generates things using the same regexp that is used in the parser)
Generally speaking one do not want to duplicate the logic between tests and code. So replicating a regexp or something else in the test is not an option.
In this case thinking about testing input stimulus / output result helps (f(input) -> output), for example if the code is supposed to process a template,
don't add values, test against a computed result.
// use
Assertions.assertThat(processTemplate("param1", "param2")).isEqualTo("this is 'param1', and this is 'param2'"));
// instead of
Assertions.assertThat(processTemplate("param1", "param2")).isEqualTo("this is '%s', and this is '%s'", param1, param2));Cover as much of the range as possible, to show positive cases, and especially erroneous code paths.
Usually this is best achievable when practicing Test Driven Developement, one can identify at design time what may break. Don't be too shy about coding a simple test case for a stupid things, you never know when or how this code will be used or even modified.
One area one could investigate is mutation testing with tools such as PIT.
This is not a hardline, but crossing this line may have repercussions!
-
If the code mock a third party lib, and upon some upgrade of the third library, the logic changes a bit, but the suite suite will execute just fine, all because it's mocked. So later on, thinking everything is good to go, the buildwall is green after all, the software is deployed and ...Boom !
-
It may be a sign that the current design is not decoupled enough with this third party library.
-
Also one issue is that the third party lib might be complex and need a lot of mocks to even work properly. That leads to overly specified tests, complex fixtures, which in itself comprises the compact and readable goal. Or to test to don't test enough given the complexity to mock the external system.
Instead, the most common way is to create wrappers around the external lib/system, though one should fear the abstraction leak where too much low level API, concepts or exceptions, goes beyond the boundary of the wrapper. And in order to verify this integration with the third party, then write integration tests, and make them as compact and readable as possible as well.
Some people have already written on the matter and experienced pain when mocking a type they didn't own :
- http://davesquared.net/2011/04/dont-mock-types-you-dont-own.html
- http://www.markhneedham.com/blog/2009/12/13/tdd-only-mock-types-you-own
- http://blog.8thlight.com/eric-smith/2011/10/27/thats-not-yours.html
- http://stackoverflow.com/questions/1906344/should-you-only-mock-types-you-own
If everything is mocked, are we really testing the production code? Don't hesitate to not mock!
Why one would even want to do that?
Because instantiating the object is too painful !? => not a valid reason.
If it's too difficult to create new fixtures, it is a sign the code may need sone serious refactoring. What other choices do we have, create builders for your value objects, they're several tools for that : IDE plugins, Lombok, etc... Once can also create meaningful factory methods in the test classpath.
abstract class CustomerCreations {
public static Customer customer_with_a_single_item_in_the_basket() {
// long init sequence
}
}Mockito is about focusing about interactions between objects, which is the most essential part of the Object Oriented Programming (or messaging).
Seriously this book is a must read, it goes from the ground to a full featured application. The authors explain many side of the development and how to achieve testing at different stage of the project lifecycle.
If unsure about something there's always a mailing list with a bunch of brilliant guys.