Wednesday, April 26, 2023

How to use Mockito to Unit test Java Program with JUnit? Example Tutorial

Hello guys, if you are writing unit test your Java application then you would have definitely come across JUnit and Mockito, two of the essential unit testing framework  for Java developers. While JUnit offers support to run your test, setup and teardown and also assertion to check the expected and actual output. Mockito complements JUnit by offering you a mock based testing approach. Mock is nothing but a test double which can return a configured value when a certain method is called. It also provides ability to check if a particular method is called or not. By using Mock object or test double you can check if your code is behaving properly or not in different scenarios. This is also the key difference between JUnit and Mockito, JUnit provides infrastructure to run your test while Mockito provide infrastructure to created Mock objects. 

If you are wondering why do we need Mock object? can't we test with actual object then let me tell you that its not always possible to test with actual object without involving external dependencies like Database, or an external System. At that time, using Mock allows you to write unit test and unblock your development. 

For example, recently I was developing a REST API client which calls an external service to retrieve discount coupon for products. If I have to write test for this client then I need the URL of external service to connect to their test environment which may or may not be ready. In order to overcome that problem, I can simply mock the HttpClient class which is used to connect to the third party service. 

I can configure my Java 11 HttpClient to a dummy URL and then I can return the different kind of response to trigger different workflow in my application. For example, by using mock instance of HttpClient, I can return a successful response with code 200, an error response using 400 or even internal server error with 500 response code.  

This gives me ability to test how my application behave in different scenarios. That's the power of Mockito, even with the actual object its not easy to mimic all these scenario and requires a lot of setup and effort for complete testing. That's why Java developer love Mockito, it help them to write better tests. 

Btw, if you are serious about Unit testing and just looking for best online courses to learn unit testing in Java then you can also checkout this list of best JUnit and Mockito courses and if you like books, you can also read these best JUnit and Mockito books to learn both JUnit and Mockito in depth. 




How to use Mockito in Java? Example Tuotrial

Now that you know what is Mockito and what does Mockito offer as well as when to use Mockito its good time to write code and see how easy or difficult it is to write Java unit tests using Mockito framework and library.  

In this example, we have a GreetingService which return greeting message in different language at different time when you call it. For example, when you first call, it can return Namaste which is Hello in Hindi, similarly it can return greetings in Japanese also and we will use Mockito to configure this behavior. 

HelloWorld.java

/**
 * Mockito is a leading mock framework for testing Java classes and interfaces.
 * In this Java program, we will see how we can use Mockito to test our Java
 * class, Helloworld, which greet with different message at different time.
 */
public class HelloWorld {

    private final GreetingService greetingService;

    public HelloWorld(GreetingService service) {
        this.greetingService = service;
    }

    public String greet() {
        return greetingService.getMessage();
    }
}

interface GreetingService {
    public String getMessage();
}


HelloWorldTestWithMockito.java

import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;

import org.junit.Test;
import org.mockito.Mockito;

public class HelloWorldTestWithMockito {
 
    private final HelloWorld helloWorldTest;
    private final GreetingService mockGreetingService;
 
    private final String greetInHindi = "Namaste !!";
    private final String greetInEnglish = "Hello !!";
    private final String greetInJapanese = "Moshi Moshi !!";


    /*
     * Creating and Setting up mock objects
     */
    public HelloWorldTestWithMockito() {
   
        this.mockGreetingService = mock(GreetingService.class);

        Mockito.when(mockGreetingService.getMessage())
                .thenReturn(greetInHindi)
                .thenReturn(greetInEnglish)
                .thenReturn(greetInJapanese);

        this.helloWorldTest = new HelloWorld(mockGreetingService);
    }

    /*
     * Real testing starts now
     */
    @Test
    public void greet() {
     
        final String firstMessage = helloWorldTest.greet();
        final String secondMessage = helloWorldTest.greet();
        final String thirdMessage = helloWorldTest.greet();
     
        assertEquals(greetInHindi, firstMessage);
        assertEquals(greetInEnglish, secondMessage);
        assertEquals(greetInJapanese, thirdMessage);
     
        verify(mockGreetingService, times(2)).getMessage();
    }
}


If you look closely, we have used a couple of Mockito methods in this program. First we have used Mockito.mock() to create a mock object of GreetingService class. In most program you will see just mock() because your IDE will use static import to import all static method from Mockito library. This will also make you to write less code and your code will also be more readable. 

Once we got the Mock object you can configure it using when() and then() method. the when() method takes an argument which is an event like when getMessage() of GreetingService is called then you can return pre-configured object like greetInHindi in our case. In short, by using when() and then() method in Mockito you can sophistically configure your Mock object to mimic different scenarios under testing. 

After that we have also used verify() method which is used for verification. By using verify() method you can double check which method of your class is called and how many times. For example, in our case we verified that getMessage() of GreetingService is called twice. If its not called twice then this test will fail and you will come to know about it, quite convenient? No?




Important points about Mockito Testing

In order to create Mock object, there are a couple of requirement which your cla    ss need to follow. For example, your testing class must be public, which is not a requirement with JUnit but Mockito will not allow you to run if your test class is not public as shown in below error. 

java.lang.Exception: Class HellworldTestWithMockito should be public
 at org.junit.runners.model.FrameworkMethod.validatePublicVoid(FrameworkMethod.java:91)
 at org.junit.runners.model.FrameworkMethod.validatePublicVoidNoArg(FrameworkMethod.java:70)
 at org.junit.runners.ParentRunner.validatePublicVoidNoArgMethods(ParentRunner.java:133)
 at org.junit.runners.BlockJUnit4ClassRunner
.validateTestMethods(BlockJUnit4ClassRunner.java:186)
 at org.junit.runners.BlockJUnit4ClassRunner
.validateInstanceMethods(BlockJUnit4ClassRunner.java:166)
 at org.junit.runners.BlockJUnit4ClassRunner
.collectInitializationErrors(BlockJUnit4ClassRunner.java:104)
 at org.junit.runners.ParentRunner.validate(ParentRunner.java:355)


Now this is to verify that whether Mockito actually record number of invocation to the method. I have put timed(2) to tell Mockito that this method should be called twice, since in code we are calling it three times. Mockito reports it as error :

org.mockito.exceptions.verification.TooManyActualInvocations:
greetingService.getMessage();
Wanted 2 times:
-> at HelloWorldTestWithMockito.greet(HelloWorldTestWithMockito.java:48)
But was 3 times. Undesired invocation:
-> at HelloWorld.greet(HelloWorld.java:23)

 at HelloWorldTestWithMockito.greet(HelloWorldTestWithMockito.java:48)
 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
 at java.lang.reflect.Method.invoke(Unknown Source)
 at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)


Let's do one more test, in second call let's compare with first message. This should be reported by JUnit

org.junit.ComparisonFailure: expected:<[Hello] !!> but was:<[Namaste] !!>
 at org.junit.Assert.assertEquals(Assert.java:115)
 at org.junit.Assert.assertEquals(Assert.java:144)
 at HelloWorldTestWithMockito.greet(HelloWorldTestWithMockito.java:45)


Similarly, your class also need a no-argument and default constructor and should not be final if you want to mock that class for testing. So make sure that if you want to mock your class then 
  • It must be public
  • It must not be final
  • and It must have a no-argument constructor
And, here is a simple diagram which explains the difference between an actual object and mock object in Java:

How to use Mockito to Unit test Java Program with JUnit? Example Tutorial


That's all about how to use Mockito in Java with JUnit. There is no doubt that Mockito is a great library and will help you to write better unit test. In many cases, it wasn't even possible to write JUnit test case without Mockito, hence a good knowledge of Mockito goes a long way for Java programmers. It will not only help you to write better and more sophisticated unit tests but also to become a better Java developer who is pro at unit testing.


Other JUnit and Interview Questions resources you like it

Thanks for reading this article and Java Mockito tutorial. If you like this tutorial then please share with your friends and colleagues. If you have any questions or doubts about Mockito then feel free to ask, love to help.

P. S. - And, If you are new to Java, particular on JUnit and unit testing and looking for some free JUnit courses to start with, then you can also take a look at these free JUnit and Mockito courses on Udemy. This course is completely free and more than 5000+students have already joined this course, you can join it too to learn unit testing essential in Java for free. 

No comments:

Post a Comment