Skip to content

Unit Testing

ljacqu edited this page Nov 29, 2015 · 5 revisions

Unit Testing

Principles

Unit tests are for testing a unit: a particular class' methods are tested (ideally) in isolation to ensure that the logic contained within the class corresponds to our expectations. In contrast, integration tests would verify that two or more components interact correctly.

Unit tests have no guaranteed order in which they are run – neither for the order of the methods within a class, nor for the order of the test classes themselves. Unit tests should not rely on previous tests to be set up. This is typically the case if a test works fine while running all tests but fails when run in isolation.

Technologies

We use JUnit, Hamcrest and Mockito for unit testing. JUnit is the general test framework to set up and run tests with; Hamcrest provides powerful matchers to test results with; Mockito enables us to mock a class, i.e. to use the same interface of a class but to provide it with custom functionality to simulate a certain situation. This facilitates unit testing: we don't need to provide an actual implementation of another class that may be required.

Conventions

Tests for a particular class should be in a class with the class name and "Test" appended to it, e.g. to test the class Player create a class PlayerTest. JavaDoc is not required for tests that are (reasonably) self-explanatory. Start test methods with should and the expected behavior, e.g. shouldKickPlayer() if this is the expected end result.

While we don't use behavior-driven development, the typical format of the tests are still suitable and convenient for us. Each test is made up of three parts, namely given, when and then:

  • given instantiates and defines the necessary objects and values to run the test (e.g. to produce the desired condition to test).
  • when is the command to test. Typically one line with a method call of the class that is being tested.
  • then verifies the result

Example

Note that the following is a made up test and doesn't correspond to any existing classes in AuthMe. Given the following method:

public static boolean isLoggedIn(CommandSender sender) {
  if (sender == null || !(sender instanceof Player)) {
    return false;
  }
  return Status.LOGGED_IN.equals(((Player) sender).getStatus());
}

You may want to test that the method returns false if the sender is not an instance of Player, e.g. if the instance is Console.

@Test
public void shouldReturnFalseForNonPlayerSender() {
  // given
  CommandSender sender = Mockito.mock(Console.class);

  // when
  boolean result = Verifier.isLoggedIn(sender);

  // then
  assertThat(result, equalTo(false));
}

Now we want to test that a player is considered logged in if it returns the correct Status. This example shows the power of Mockito: we don't need to instantiate an actual player, where we might encounter issues to set the status to the desired one (e.g. if there is no setter for that method).

@Test
public void shouldReturnTrueForLoggedInPlayer() {
  // given
  Player player = Mockito.mock(Player.class);
  given(player.getStatus()).willReturn(Status.LOGGED_IN);

  // when
  boolean result = Verifier.isLoggedIn(sender);

  // then
  assertThat(result, equalTo(true));
}
Clone this wiki locally