Mockito Basic

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

  1. Whats Is Mockito?

    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

  2. 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.

  3. Basic functions

    Here we just show a quick demo for verify something:

    //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:

    //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);
    
    1. If we want to enable the use of annotations with Mockito tests, we need do this:

      Annotate the JUnit test with a MockitoJUnitRunner:

      @RunWith(MockitoJUnitRunner.class)
      public class MockitoAnnotationTest {
          ...
      }
      

      or enable Mockito annotations programmatically by invoking MockitoAnnotations.openMocks():

      @Before
      public void init() {
          MockitoAnnotations.openMocks(this);
      }
      
    2. mock()/@mock: Creates a mock with some non-standard settings.

      @mock can subsctitute mock() to create and inject mocked instances.

      @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());
      }
      
      @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());
      }
      
    3. spy()/@spy(): Creates a spy of the real object. The spy calls real methods unless they are stubbed.

      @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());
      }
      
    4. @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.

      @Mock
      List mockedList;
      
      @Captor 
      ArgumentCaptor argCaptor;
      
      @Test
      public void whenUseCaptorAnnotation_thenTheSam() {
          mockedList.add("one");
          Mockito.verify(mockedList).add(argCaptor.capture());
      
          assertEquals("one", argCaptor.getValue());
      }
      
    5. @injectMcok: creates an instance of the class and injects the mocks that are created with the @Mock (or @Spy) annotations into this instance.

      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;
          }
      }
      
      @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 and thenReturn method. Lastly, using @InjectMocks Mockito will put that Player into Game.

      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:

      @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();
        }
      
        // ...
      
    6. verify()

      List<String> mockedList = mock(MyList.class);
      mockedList.size();
      verify(mockedList).size();
      
    7. configure behavior: When/then

      public class MyList extends AbstractList<String> {
      
          @Override
          public String get(final int index) {
              return null;
          }
          @Override
          public int size() {
              return 1;
          }
      }
      
      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:

      MyList listMock = Mockito.mock(MyList.class);
      doReturn(false).when(listMock).add(anyString());
      
      boolean added = listMock.add(randomAlphabetic(6));
      assertThat(added).isFalse();
      
    8. Exception throwing

      First, if our method return type is not void, we can use when().thenThrow():

      @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.

  1. What is Mocking

  2. What's the difference between faking, mocking, and stubbing?

  3. Mockito

  4. Mockito Tutorial

  5. verify cookbook