1. Introduction

Software testing is a crucial stage in the software development lifecycle. It helps us evaluate, identify, and improve our software’s quality.

Testing frameworks help us with predefined tools that facilitate this process. In this article, we’re going to talk about JUnit, Mockito, how they help us, and the differences between the two frameworks.

2. What Is JUnit?

JUnit is one of the most widely used unit-testing frameworks and JUnit 5 is its latest generation. This version focuses on Java 8 and above, and enables various styles of testing.

JUnit 5 runs test cases using assertions, annotations, and test runners. The framework is primarily targeted for unit testing. It focuses mainly on methods and classes, isolated from other elements of the project.

For runtime, JUnit 5 requires Java 8 (or higher). However, code that is already compiled using previous versions of the JDK can still be tested.

Here is how a generic JUnit test class and method looks like:

public class JunitVsMockitoUnitTest {
    @Test
    public void whenUsingJunit_thenObjectCanBeInstantiated() {
        InstantiableClassForJunit testableClass = new InstantiableClassForJunit();

        assertEquals("tested unit", testableClass.testableComponent());
    }
}

In the example above we’ve created a simple InstantiableClassForJunit class with a single method that returns a String. Furthermore, looking at the class in Github, we’ll see that for the assertEquals() method we imported the Assertions class. This is a clear example that we’re using JUnit 5 (Jupiter) and not an older version.

3. What Is Mockito?

Mockito is a Java-based framework developed by Szczepan Faber and friends. It proposes a different approach (than traditional mocking libraries using expect-run-verify). We ask questions about interactions after execution.

This approach also means Mockito mocks usually don’t need expensive setup beforehand. Furthermore, it has a slim API and mocking can be started quickly. There is a single kind of mock and only one way of creating mocks.

Some other important features of Mockito are:

  • ability to mock concrete classes and interfaces
  • little annotation syntax sugar – @Mock
  • clear error messages pointing to code line
  • ability to create custom argument matchers

Here’s how the previous code looks with Mockito added:

@ExtendWith(MockitoExtension.class)
public class JunitVsMockitoUnitTest {
    @Mock 
    NonInstantiableClassForMockito mock;

    // the previous Junit method

    @Test
    public void whenUsingMockito_thenObjectNeedsToBeMocked() {
        when(mock.nonTestableComponent()).thenReturn("mocked value");

        assertEquals("mocked value", mock.nonTestableComponent());
    }
}

Looking at the same class we notice that we’ve added an annotation (@ExtendWith) over the class name. There are other ways to enable Mockito but we won’t go into detail here.

Next, we’ve mocked an instance of the needed class using the @Mock annotation. Finally, in the test method, we’ve used the when().thenReturn() construct.

It’s good to remember to use this construct before the assert method. This lets Mockito know that when the specific method of the mocked class is called, it should return the mentioned value. In our case, this is “mocked value” instead of “some result”, what it would normally return.

4. Differences Between JUnit and Mockito

4.1. Test Case Structure

JUnit creates its structure using annotations. For example, we use @Test above a method to signal that is a test method. Or @ParametrizedTest to signal that the test method will run multiple times with different parameters. We use @BeforeEach, @AfterEach, @BeforeAll, and @AfterAll to signal that we want that method to be executed before or after one or all test methods.

On the other hand, Mockito helps us with these annotated methods. Mockito provides methods to create mock objects, configure their behavior (what they return), and verify certain interactions that took place (if the method was called, how many times, with what type of parameter, etc.).

4.2. Testing Scope

As we’ve mentioned before, we use JUnit for unit testing. This means we create the logic to test individual components (methods) in isolation. Next, we use JUnit to run the testing logic and confirm the test outcome.

On the other hand, Mockito is a framework that helps us generate objects (mocks) of certain classes and control their behavior during testing. Mockito is more about the interactions during testing rather than actually doing the testing itself. We can use Mockito to simulate external dependencies during testing.

For example, we’re supposed to receive an answer from an endpoint. Using Mockito we mock that endpoint and decide its behavior when we call it during the test. This way we don’t have to instantiate that object anymore. Furthermore, sometimes we can’t even instantiate that object without having to refactor it.

4.3. Test Doubles

The design of JUnit focuses on the concrete implementation of objects and test doubles. The latter means the implementation of fakes, and stubs (not mocks). Test doubles mimic real dependencies but have limited behavior specific to the objective we’re trying to accomplish.

On the other hand, Mockito works with dynamic objects (mocks created using reflection). When we use these mocks we personalize and control their behavior to suit our testing needs.

4.4. Test Coverage

JaCoCo is a test coverage framework that works with JUnit, not Mockito. This is another clear example of the differences between the two frameworks.

Mockito is a framework that is used within JUnit. JaCoCo (or other code coverage libraries) can only be used with a testing framework.

4.5. Object Mocking

With Mockito, we can specify expectations and behaviors for mock objects, speeding up the creation of desired testing scenarios.

JUnit focuses more on individual component testing using assertions. It does not have built-in mocking functionalities.

5. Conclusion

In this article, we’ve taken a look at two of the most popular testing frameworks in the Java ecosystem. We’ve learned that JUnit is the main testing facilitator. It helps us create the structure and appropriate environment.

Mockito is a framework that complements JUnit. It helps us test individual components by mocking elements that otherwise would be very complicated to instantiate or couldn’t be created at all. Furthermore, Mockito helps us control the output of these elements. Finally, we can also check if the desired behavior took place, and how many times.

As always, the code is available over on GitHub.