You might have got the impression that refactoring is reserved only for the implementation code. However, when we look the objectives behind refactoring (more readable, optimal, and faster code), they apply as much to specifications as to the implementation code.
The last two specifications have the instantiation of the TicTacToeCollection class repeated. We can move it to a method annotated with @Before. The effect will be the same (the class will be instantiated before each method annotated with @Test is run) and we'll remove the duplicated code. Since the same instantiation will be needed in further specs, removing duplication now will provide even more benefits later on. At the same time, we'll save ourselves from throwing UnknownHostException over and over again:
TicTacToeCollection collection; @Before public void before() throws UnknownHostException { collection = new TicTacToeCollection(); } @Test public void whenInstantiatedThenMongoHasDbNameTicTacToe() { // throws UnknownHostException { // TicTacToeCollection collection = new TicTacToeCollection(); assertEquals(
"tic-tac-toe", collection.getMongoCollection().getDBCollection().getDB().getName()); } @Test public void whenInstantiatedThenMongoHasNameGame() { // throws UnknownHostException { // TicTacToeCollection collection = new TicTacToeCollection(); assertEquals(
"game", collection.getMongoCollection().getName()); }
In many cases, some code needs to be executed before the test class or each method in a class. For this purpose, JUnit has the @BeforeClass and @Before annotations that should be used in the setup phase. The @BeforeClass executes the associated method before the class is loaded (before the first test method is run). @Before executes the associated method before each test is run. Both should be used when there are certain preconditions required by tests. The most common example is setting up test data in the (hopefully in-memory) database. On the opposite end are the @After and @AfterClass annotations, which should be used as the teardown phase. Their main purpose is to destroy the data or state created during the setup phase or by tests themselves. Each test should be independent from others. Moreover, no test should be affected by the others. The teardown phase helps maintain the system as if no test were previously executed.
Now let's do some mocking, spying, and verifying!