Pattern
Interesting question. First of all - my ultimate test pattern configured in IDE:
@Test
public void shouldDoSomethingWhenSomeEventOccurs() throws Exception
{
//given
//when
//then
}
I am always starting with this code (smart people call it BDD).
In given
I place test setup unique for each test.
when
is ideally a single line - the thing you are testing.
then
should contain assertions.
I am not a single assertion advocate, however you should test only single aspect of a behavior. For instance if the the method should return something and also has some side effects, create two tests with same given
and when
sections.
Also the test pattern includes throws Exception
. This is to handle annoying checked exceptions in Java. If you test some code that throws them, you won't be bothered by the compiler. Of course if the test throws an exception it fails.
Setup
Test setup is very important. On one hand it is reasonable to extract common code and place it in setup()
/@Before
method. However note that when reading a test (and readability is the biggest value in unit testing!) it is easy to miss setup code hanging somewhere at the beginning of the test case. So relevant test setup (for instance you can create widget in different ways) should go to test method, but infrastructure (setting up common mocks, starting embedded test database, etc.) should be extracted. Once again to improve readability.
Also are you aware that JUnit creates new instance of test case class per each test? So even if you create your CUT (class under test) in the constructor, the constructor is called before each test. Kind of annoying.
Granularity
First name your test and think what use-case or functionality you want to test, never think in terms of:
this is a Foo
class having bar()
and buzz()
methods so I create FooTest
with testBar()
and testBuzz()
. Oh dear, I need to test two execution paths throughout bar()
- so let us create testBar1()
and testBar2()
.
shouldTurnOffEngineWhenOutOfFuel()
is good, testEngine17()
is bad.
More on naming
What does the testGetBuzzWhenFooIsNullAndFizzIsNonNegative
name tell about the test? I know it tests something, but why? And don't you think the details are too intimate? How about:
@Test shouldReturnDisabledBuzzWhenFooNotProvidedAndFizzNotNegative`
It both describes the input in a meaningful manner and your intent (assuming disabled buzz is some sort of buzz
status/type). Also note we no longer hardcode getBuzz()
method name and null
contract for Foo
(instead we say: when Foo
is not provided). What if you replace null
with null object pattern in the future?
Also don't be afraid of 20 different test methods for getBuzz()
. Instead think of 20 different use cases you are testing. However if your test case class grows too big (since it is typically much larger than tested class), extract into several test cases. Once again: FooHappyPathTest
, FooBogusInput
and FooCornerCases
are good, Foo1Test
and Foo2Test
are bad.
Readability
Strive for short and descriptive names. Few lines in given
and few in then
. That's it. Create builders and internal DSLs, extract methods, write custom matchers and assertions. The test should be even more readable than production code. Don't over-mock.
I find it useful to first write a series of empty well-named test case methods. Then I go back to the first one. If I still understand what was I suppose to test under what conditions, I implement the test building a class API in the meantime. Then I implement that API. Smart people call it TDD (see below).
Recommended reading: