1 Background
I started using Mockito for couple of months since I started my career as a backend developer. While I could write some tests, they are mostly originated from others and modified to meet my needs.
I wish I could take some time to go over the official site or docs to have a basic conceps of this test framework. It's Chinese National Day Holiday so I have plenty to time do the learning.
2 Mockito
-
Whats Is Mockito?
It's a mocking framework.(I just wondering why it's describe as a testing framework, maybe it only focus on mocking and doesn't support test engine.)
Mocking is something that creating objects simulating real objects.
The difference between mocking and stubbing is that:
Mocking is similar to a stub, but with verification added in. The purpose of a mock is to make assertions about how your system under test interacted with the dependency.
Stubbing is a replacement of dependencies. You do not have to deal with pendency directly. The purpose of a stub is to get your system under test into a specific state
-
Features And Motivatiosn
Mockito offers simpler and more intuitive approach: you ask questions about interactions after execution
Mockito offers simpler and more intuitive approach: you ask questions about interactions after execution
Mockito has very slim API, almost no time is needed to start mocking. There is only one kind of mock, there is only one way of creating mocks. Just remember that stubbing goes before execution, verifications of interactions go afterwards. You'll soon notice how natural is that kind of mocking when TDD-ing java code.
-
Basic functions
Here we just show a quick demo for verify something:
```java //Let's import Mockito statically so that the code looks clearer import static org.mockito.Mockito.*;
//mock creation List mockedList = mock(List.class);
//using mock object mockedList.add("one"); mockedList.clear();
//verification verify(mockedList).add("one"); verify(mockedList).clear(); ```
Stubbing:
```java //You can mock concrete classes, not just interfaces LinkedList mockedList = mock(LinkedList.class);
//stubbing when(mockedList.get(0)).thenReturn("first"); when(mockedList.get(1)).thenThrow(new RuntimeException());
//following prints "first" System.out.println(mockedList.get(0));
//following throws runtime exception System.out.println(mockedList.get(1));
//following prints "null" because get(999) was not stubbed System.out.println(mockedList.get(999));
//Although it is possible to verify a stubbed invocation, usually it's just redundant //If your code cares what get(0) returns, then something else breaks (often even before verify() gets executed). //If your code doesn't care what get(0) returns, then it should not be stubbed. verify(mockedList).get(0); ```
-
If we want to enable the use of annotations with Mockito tests, we need do this:
Annotate the JUnit test with a MockitoJUnitRunner:
java @RunWith(MockitoJUnitRunner.class) public class MockitoAnnotationTest { ... }
or enable Mockito annotations programmatically by invoking MockitoAnnotations.openMocks():
java @Before public void init() { MockitoAnnotations.openMocks(this); }
-
mock()/@mock: Creates a mock with some non-standard settings.
@mock can subsctitute mock() to create and inject mocked instances.
```java @Test public void whenNotUseMockAnnotation_thenCorrect() { List mockList = Mockito.mock(ArrayList.class);
mockList.add("one"); Mockito.verify(mockList).add("one"); assertEquals(0, mockList.size()); Mockito.when(mockList.size()).thenReturn(100); assertEquals(100, mockList.size());
} ```
```java @Mock List<String> mockedList;
@Test public void whenUseMockAnnotation_thenMockIsInjected() { mockedList.add("one"); Mockito.verify(mockedList).add("one"); assertEquals(0, mockedList.size());
Mockito.when(mockedList.size()).thenReturn(100); assertEquals(100, mockedList.size());
} ```
-
spy()/@spy(): Creates a spy of the real object. The spy calls real methods unless they are stubbed.
```java @Spy List<String> spiedList = new ArrayList<String>();
@Test public void whenUseSpyAnnotation_thenSpyIsInjectedCorrectly() { spiedList.add("one"); spiedList.add("two");
Mockito.verify(spiedList).add("one"); Mockito.verify(spiedList).add("two"); assertEquals(2, spiedList.size()); Mockito.doReturn(100).when(spiedList).size(); assertEquals(100, spiedList.size());
} ```
-
@captor:ArgumentCaptor allows us to capture an argument passed to a method to inspect it. This is especially useful when we can't access the argument outside of the method we'd like to test.
```java @Mock List mockedList;
@Captor ArgumentCaptor argCaptor;
@Test public void whenUseCaptorAnnotation_thenTheSam() { mockedList.add("one"); Mockito.verify(mockedList).add(argCaptor.capture());
assertEquals("one", argCaptor.getValue());
} ```
-
@injectMcok: creates an instance of the class and injects the mocks that are created with the
@Mock
(or@Spy
) annotations into this instance.```java class Game {
private Player player; public Game(Player player) { this.player = player; } public String attack() { return "Player attack with: " + player.getWeapon(); }
}
class Player {
private String weapon; public Player(String weapon) { this.weapon = weapon; } String getWeapon() { return weapon; }
} ```
```java @RunWith(MockitoJUnitRunner.class) class GameTest {
@Mock Player player; @InjectMocks Game game; @Test public void attackWithSwordTest() throws Exception { Mockito.when(player.getWeapon()).thenReturn("Sword"); assertEquals("Player attack with: Sword", game.attack()); }
} ```
Mockito will mock a Player class and it's behaviour using
when
andthenReturn
method. Lastly, using@InjectMocks
Mockito will put thatPlayer
intoGame
.Notice that you don't even have to create a
new Game
object. Mockito will inject it for you.Here is a example for inject in a spy:
```java @RunWith(MockitoJUnitRunner.class) public class GameTest {
@Mock Player player;
@Spy List<String> enemies = new ArrayList<>();
@InjectMocks Game game;
@Test public void attackWithSwordTest() throws Exception { Mockito.when(player.getWeapon()).thenReturn("Sword");
enemies.add("Dragon"); enemies.add("Orc"); assertEquals(2, game.numberOfEnemies()); assertEquals("Player attack with: Sword", game.attack());
} }
class Game {
private Player player;
private List<String> opponents;
public Game(Player player, List<String> opponents) { this.player = player; this.opponents = opponents; }
public int numberOfEnemies() { return opponents.size(); }
// ... ```
-
verify()
java List<String> mockedList = mock(MyList.class); mockedList.size(); verify(mockedList).size();
-
configure behavior: When/then
```java public class MyList extends AbstractList<String> {
@Override public String get(final int index) { return null; } @Override public int size() { return 1; }
} ```
```java MyList listMock = Mockito.mock(MyList.class); when(listMock.add(anyString())).thenReturn(false);
boolean added = listMock.add(randomAlphabetic(6)); assertThat(added).isFalse(); ```
or configure return behavior for mock in an alternative way:
```java MyList listMock = Mockito.mock(MyList.class); doReturn(false).when(listMock).add(anyString());
boolean added = listMock.add(randomAlphabetic(6)); assertThat(added).isFalse(); ```
-
Exception throwing
First, if our method return type is not void, we can use when().thenThrow():
```java @Test(expected = NullPointerException.class) public void whenConfigNonVoidRetunMethodToThrowEx_thenExIsThrown() { MyDictionary dictMock = mock(MyDictionary.class); when(dictMock.getMeaning(anyString())) .thenThrow(NullPointerException.class);
dictMock.getMeaning("word");
} ```
Notice that we configured the getMeaning() method — which returns a value of type String — to throw a NullPointerException when called.
-
3 Summary
Here I thought it would take days to finish the learning, however, it only takes about 2 hours for me to quickly go through the docs. There are many things I don't know or understand and I only write something here that may be useful for my current job. I belive this artical may be modified as my experience accumulating. More practices will be reflected.