Stub and mock in unit test with Mockito2

Tram Ho

Mock and Stub are the foundation for quick and simple unit test . Mock is useful in case you have a dependency on an external system, reading files time-consuming, unreliable database connections, or sending emails after each test

Unlike integration or funtional testing , when the entire system is tested, unit test focus on a single class . Everything else should be a simple class or mock . In this article, we will focus on unit tests using Mockito2 – a dominant mocking framework in testing with Java .

Location of Unit Test in Testing Pyramid

It is at the bottom of the Testing Pyramid .

  • Test 1 single class
  • Only need the source code of the application without a specific build .
  • Fast
  • Not affected by external systems, eg web service, database
  • Perform little or no I / O , e.g. no real database connection …

These tests are a key component of the entire test suite , and include a large number of test cases . Most have confusion between unit tests and integration tests , service tests , system tests or functional tests .

This distinction is very important, a test case like write database or read JSON from web service is not a unit test. It can become a unit test if you mock the database or external web service. Unit tests and integration tests should be handled differently.

Necessity of mock and stub

Mocking is the act of removing external dependencies from unit tests to create a controlled environment around it. Normally, we mock all other classes to interact with the tested class. Common components should often be mocked :

  • Database connection
  • Web services
  • Class that executes slowly
  • Class has side effects
  • Class with undefined behavior

Mock and stub are fake Java class replace the external dependencies as mentioned above. These fake classes will be instructed ( instruction ) before they can operate at your disposal.

  • The stub is a fake class with values ​​returned according to the actions that have been programmed in the direction you target. It will be injected into the class that is being tested to give you absolute control over what needs to be tested as input. For example, a stub can be a database connection that allows you to mimic any scenario without the need for a real database .
  • Mock is a fake class that can be tested after finishing a test of the interaction of the class being tested. For example: You can verify a method is executed or not and how many times it is executed. Fill the shape of the mock with the side effect classes that need to be tested, such as the class sending email or sending data to an external service.

Basic Stubbing with Mockito

Suppose we have several classes as shown below

CustomerReader class reads customer data from database through EntityManager , how to write test for this class ?

A naive solution is to pre-populate a real database with some customer data and conduct tests . However, this has a lot of problems. Firstly, it creates a hard dependency on the running database . Second, you need to add a step to create test data. In this example, it might work, as in practice it should not be at all.

The best solution for a real unit test is to completely eliminate database dependence. We will Stub a database connection and direct (trick) the class that it is executing with a real EntityManager , while EntityManager is just a Mockito Stub . This way, we have complete control over what is executed or returned by the database connection without having to handle an actual database .

The steps above:

  • Create a sample customer, we use the real class because it’s simply a POJO class, no need to mock it.
  • Next, mock the EntityManager object by calling the mock() function (you can use the @Mock annotation)
  • Extremely important step in the next line of code. It defines (direction) what happens when the find() function is called from entityManager . Here, we set up that sample customer created in step 1 will be returned when id = 1L . From this point on, the CustomerReader class will not fully know that EntityManager is fake. It just calls findFullName (step 4) and receives the sample customer without caring about Mockito below it all.

This test case satisfies all the requirements of a unit test , it is not external dependent, just Java code , fast and completely decisive, absolutely no database.

When-Then mantra in Mockito

Use simple stubbing directives

Will help a lot for your unit test cases . Depending on the application, this may be the only Mockito (spell) feature you need.

When you have multiple test methods , it makes sense to move the mock to a single new site and only distinguish its behavior for the individual test words.

In the previous example, you may have realized that the CustomerReader class was incorrect, because it did not handle null cases, for example the database ID field does not exist in the database . Although we can copy-paste unit tests , it is better to arrange the code with a common test method .

In this example, we move the mock and common code into the setup method , all the code in this method will run before running each test method . The only difference between test methods is when directive .

Basic Mocking with Mockito

Let’s see if a mock case is needed to replace a stub .

This class has 2 external dependencies , and uses constuctor injection .

In fact, InvoiceStorage is a web service that connects to external CRM systems, which works slowly. As is well known, a unit test never uses the actual web service .

EmailSender class is also an external system from a third-party that provides emai functionality. So, we have to mock it.

However, one problem is that when you try to write test cases for these classes, there is nothing to asserted . The method we want to test notifyIfLate is a void method that doesn’t return anything. So how to test it?

In this case we will focus on the side effect of the code . Here, sending an email is a side effect . Email is only sent when outstanding invoice customer appears. Mockito provides a verify directive for testing side effects .

As above, we Stub InvoiceStorage class with when-then syntax . Both test methods do not use the JUnit assert statement . Instead, we use the verify directive to test the mock after each run, and pass the test case if a method is called with the specified arguments .

In test method 2 , we test the case of the sendEmail method not being called. Therefore, times(0) with 0 is the number of times the sendEmail method has called. Default times = 1 will be ignored (in test method 1 ).

Note that the Mock can still be Stubed when needed. You might think that the mock is the superset of the stub . That’s why Mockito called both Mock .

Verify arguments with Argument Captor

In the previous example, we simply verify a method was called or not. Sometimes we need more details, in addition to the method being called or not, we verify the argument of the method to be tested .

Mockito provides us with the ArgumentCaptor to support the method argument checking:

In the simple example above, we use an ArgumentCaptor and define it as the holder of an Integer class . Then, in the verify directive , we use the captor by calling the capture() method .

At this point, when the unit test is complete, the captor will contain the exact argument sent to the MathUtils mock when the add() method is called. We can extract the instance of the argument by calling getValue() or the entire argument with getAllValues() .

Note that the argument can be any complex Java object (eg, nested class, data structure / list …). Mockito can capture without any problems, and you can verify them the way you want.

Forming Dynamic Responses for Mocks

Mockito ‘s next power allows you to be able to custom reponse from a mock depending on the argument of the function call. This is an advanced technique and is only needed for some very specific cases in unit tests . If selected, it is best to return predefined results via mock / stub (like the previous examples) to make your test easier to read. Dynamic response should only be used as a last resort.

Dynamic Manipulation of Arguments

You can see immediately that unit testing for this class is a bit difficult. Although DAO ‘s logic is very basic, the problem is that when a customer is stored using the persist() method , its database ID is sent to the logger . For this hypothetical example, the code will work fine in the real system, because the database will assign the ID to the object as soon as the customer is saved. However, how can we manage this process in unit tests? Because the persist method doesn’t return an argument , we can’t mock it with when-then directive . However, Mockito still has a solution:

The above unit test is based on doAnswer-when directive . Mockito allows us to override to answer any method by implement Answer interface . This interface has only one method that allows us to access the argument passed from the unit test .

In the case in the example above, we know that the argument is a customer . We fetch the customer and set the database ID to 123L , or whatever value you want. We also tend to bind result Mockito answer to anyone any argument type Customer . Here, the persist method argument is not created by us, but is created by the test class , so we cannot create an object whose test data matches it. However, with Mockito doAnswer directive, we don’t need to know anything in advance, because we change them at runtime .

Dynamic Responses Based on Arguments

A more practical example, in which the answer of a mock depends on the argument.

For example, the transmission class 1 customer list and save on UserRepository . For each customer , an event type REGISTRATION is released.

We need to test the massRegister method because register is private . In theory, we could pass a list of only 1 customer in the unit test case. But in reality, it’s still best to try with a list containing a huge list of customers . The code may be simple and there is absolutely no error case testing, but in actual systems there may be some consistency checks before the customer is registered . An actual unit tests ( realistic unit test ) should transmit a large list of customer with different issues, so that all the test can be assessed during unit testing.

Suppose we want to test a list of 20 customers . When the saveRepository method returns the argument , we can theoretically use the when-then directive 20 times to direct it to the exact output to be sent.

In the test class , two things to note:

  1. In setup , we override the saveCustomer method with the Answer interface . Fetch 2 arguments with anyString() matcher , create a Customer instance and fill in the necessary information.
  2. Accordingly, for any size of test data, the UserRepository mock will always return the correct response for the tested class.

Note that the unit test is written so that the size of the input data is actually irrelevant. We can extend test data from 20 to 100 or 1000 customers , but the mocking and verification code will remain unchanged. This does not happen if we manually set a response for each specific customer .

Reference :


Share the news now

Source : Viblo