Let's Mock it-o!

Let's Mock it-o!

-Let's discover Effective Unit Testing and Mocking in Java Applications

[Unit Test Framework: JUnit and Mocking Framework: Mockito]

What is it?

Unit testing: Unit Testing is a type of software testing where individual units or components of the software are tested using some available tools like JUnit, TestNG, Mocha etc.

Individual units can be methods, functions, classes and other code snippets at a minute unit level.

Mocking: Mocking is a process used in unit testing when the unit being tested has external dependencies using Mockito, PowerMock, Spock etc. The purpose of mocking is to isolate and focus on the code being tested and not on the behavior or state of external dependencies.

Let's take JUnit and Mockito as the tools to understand unit testing and mocking.

Different annotations and keywords to test?

When our application is a Spring-based application or written in Java, JUnit and Mockito provide various annotations for different use cases. Below is a list of these annotations and keywords that are used more frequently, and subsequently, we will explore them in depth.

  1. @SpringBootTest

  2. @ExtendWith(MockitoExtension.class)

  3. @Mock

  4. @Before

  5. @After

  6. @InjectMock

  7. @Test

  8. @WebMvcTest

  9. @Import

  10. @MockBean

  11. @Spy

  12. Mockito.when()

  13. assertNotNull()

  14. assertEquals()

  15. assertTrue()

  16. assertThrows()

  17. MockHttpServletResponse()

  18. when()

What to use and When to use?

  1. @SpringBootTest

    This is provided by the Spring Framework for integration testing of Spring applications and this annotation starts up the Spring application context and provides various utilities for testing Spring components and features.

    @SpringBootTest

    public class MyIntegrationTest {}

    Note:- It is Marked at the class level, and it can be used along with the other configurations like environment, classes, properties, profiles etc.

    For more info: Checkout https://www.baeldung.com/spring-boot-testing

  2. @ExtendWith(MockitoExtension.class)

    This annotation is used at the class level to enable Mockito support for JUnit tests.

    @ExtendWith(MockitoExtension.class)

    public class MyMockitoTest {}

    Note:- It is Marked at the class level and It allows the usage of Mockito annotations, such as @Mock and @Spy, in your test classes.

    For more info: Checkout https://www.baeldung.com/mockito-junit-5-extension

  3. @Mock

    This annotation is used to create a mock object of a class or interface. It is typically used in test classes to define a mock instance that simulates the behavior of a real object.

    @Mock

    private MyService myServiceMock;

    Note:- It is Marked at the filed level and the field is then injected with the mock object.

    For more info: Checkout https://www.baeldung.com/mockito-annotations#mock-annotation

  4. @Before

    It is used to mark a method that should be executed before each test method in a test class. It is commonly used for setup or initialization tasks that need to be performed before each test case.

    @Before

    public void setup() {

    // Perform setup or initialization tasks

    myObject = new MyClass();

    }...

    Note:-It is marked at the method level to indicate that a particular method should be executed before each test method.

  5. @After

    It is to mark a method that should be executed after each test method in a test class. It is commonly used for cleanup tasks or teardown operations that need to be performed after each test case.

    @After

    public void tearDown() {

    // Perform cleanup or teardown tasks

    myObject = null;

    }

    Note:-It is marked at the method level to indicate that a particular method should be executed after each test method.

  6. @InjectMock

    This annotation is used to It is typically used when you want to inject mocked dependencies into the class under test. Mockito automatically injects the mocked dependencies into the fields of the class under test

    @InjectMock

    private MyService myServiceMock;

    Note:- It is Marked at the field level where the mock dependencies should be injected.

    For more info: Checkout https://www.digitalocean.com/community/tutorials/mockito-injectmocks-mocks-dependency-injection

  7. @Test

    This annotation is used to mark a method as a test case. When the test framework runs, it identifies and executes all methods annotated with @Test

    @Test public void myTestMethod() {

    //Test logic goes here

    // Assertions and verifications }

    Note:- It is Marked at the method level where the respective method acts as the test case.

  8. @WebMvcTest

    It is used for testing Spring MVC controllers in a web application. With @WebMvcTest, you can focus your tests on the MVC layer, specifically the controllers, while excluding other parts of the application like service beans or database components.

    @WebMvcTest(MyController.class)

    public class MyControllerTest { ...

    Note:-@WebMvcTest annotation is marked at the class level to indicate that the test class focuses on testing Spring MVC controllers in a web application.

  9. @Import

    This annotation is used to mark the class to import certain configurations or beans to set up, it allows importing the required dependencies explicitly.

    @Import(TestConfiguration.class)

    public class MyTest { // Test methods }

    Note:- It is Marked at the class level, it imports other configuration classes or component definitions into the current configuration class.

  10. @MockBean

    The @MockBean annotation is commonly used in the Spring framework, particularly in the context of integration testing, to create and inject mock objects or dependencies.

    @MockBean

    UserRepository mockRepository;

    Note:- @MockBean annotation is used at the field or method level in the Spring framework.

  11. @Spy

    The @Spy annotation is used in testing frameworks like Mockito (commonly used in Java) to create a partial mock object. It allows you to create a real instance of class while still providing the ability to stub or verify specific behaviors.

    @Spy

    private MyClass myClassSpy;

    Note:- The @Spy annotation is used at the field level to create a partial mock object.

  12. Mockito.when()

    This is used to specify the behavior of a mocked object or a spy object when a specific method is called. It allows you to define the expected return value or behavior for the method being stubbed.

    Mockito.when()

    private MyClass myClassMock;

    @Test

    public void testMethod() { // Stubbing the behavior Mockito.when(myClassMock.doSomething()).thenReturn("Mocked implementation"); ...

    Note:-It is used within the method body to specify the behavior of a mocked object or a spy object.

  13. assertNotNull()

    This method is an assertion method commonly used in testing frameworks to check that an object reference is not null. It verifies that a given object is not null and raises an assertion failure if it is.

    @Test

    public void testMethod() {

    // Create an object or perform some operations

    Object obj = new Object();

    // Assert that the object is not null

    assertNotNull(obj);

    Note:-It is used within the method body to raise an assertion.

  14. assertEquals()

    This method is an assertion method commonly used in testing frameworks to compare two values and assert that they are equal.

    @Test

    public void testMethod() {

    // Set up the expected value int expected = 42;

    // Perform the operation to get the actual value

    int actual = someMethod();

    // Assert that the expected value is equal to the actual value

    assertEquals(expected, actual); ...

    Note:-It is used within the method body to raise an assertion.

  15. assertTrue()

    This method is an assertion method commonly used in testing frameworks to verify that a given condition or expression evaluates to true. It raises an assertion failure if the condition is false.

    @Test

    public void testMethod() {

    // Set up some variables or conditions

    int x = 5;

    boolean flag = true;

    // Assert that the condition is true

    assertTrue(x > 0);

    assertTrue(flag);

    Note:-It is used within the method body to raise an assertion.

  16. assertThrows()

    This method is an assertion method commonly used in testing frameworks to verify that a specific exception is thrown by a given piece of code. It allows you to assert that an exception is thrown during the execution of a particular method or block of code.

    @Test

    public void testMethod() {

    // Define the code that should throw an exception

    Runnable code = () -> {

    throw new IllegalArgumentException("Invalid argument");

    };

    // Assert that the expected exception is thrown assertThrows(IllegalArgumentException.class, code);

    }

    Note:-It is used within the method body to raise an assertion.

  17. MockHttpServletResponse()

    It represents a mock implementation of the HttpServletResponse interface, allows you to simulate and test HTTP responses in your web application.

    @Test

    public void testMethod() {

    // Create an instance of MockHttpServletResponse

    MockHttpServletResponse response = new MockHttpServletResponse();

    // Set the desired attributes or properties of the response response.setStatus(200);

    // Perform assertions on the response properties

    assertEquals(200, response.getStatus());

    Note:-It is used within the method body to create and mock a HTTP servlet response

  18. when()

    This method is used in Mockito to define the behavior of a mocked object when a specific method is called. It allows you to specify the expected return value or behavior for the mocked method.

    @Test

    public void testMethod() {

    // Create a mock object

    MyClass myClassMock = mock(MyClass.class);

    // Define the behavior using when()

    when(myClassMock.doSomething()).thenReturn("Mocked implementation"); .....

    Note:-The when() method is used inside the method body to define the behavior of a mocked object.

Tabulators to wipe out some confusion!

AnnotationPurposeUsage
@MockBeanCreates a mock object of a beanUsed in integration tests with Spring Boot to create a mock implementation of a bean
@InjectMocksInjects mocks into a test classUsed with Mockito to automatically inject mocked dependencies into the target test class
@ImportImports configuration or beansUsed to import additional configurations or beans into the application context during testing
AnnotationPurposeUsage
@AutowiredAutowires dependenciesUsed to automatically inject dependencies into a class (e.g., beans, components, or other autowirable objects)
@MockCreates a mock objectUsed in unit testing to create a mock implementation of a class or interface
@BeanDefines a beanUsed in configuration classes to define and configure beans explicitly

Test/Run the test cases...

  1. Set up your development environment

  2. Write your test cases

  3. Compile your code and tests

  4. Run the tests using your IDE

  5. Run the tests using build tools

  6. Observe the test results

  7. Analyze the test results

Steps 2-7 are iterative and cyclic process, it is done according to the feedback and use case implementation.

Cover your code!

Code coverage is a metric that measures the extent to which your codebase is tested by your unit tests. It indicates the percentage of code lines, branches, or conditions that are executed during your test suite's execution.

Code Coverage is important in unit testing because of identifying untested code, detecting gaps in test cases, improving code quality, refactoring and maintenance.

Recommended and practiced code coverage percentage would be around 75%-85% in the organizations, which means 75%-85% of your codebase is executed by your unit tests. This metric indicates that a significant portion of your code is covered by tests, but there are still some areas that are not tested.

Note:

  1. Based on my frequent technical discussions, the above topics cover the most frequently asked questions. While there may be slight variations, these are commonly encountered FAQs in many companies.

  2. This write-up doesn't include all the annotations and methods present in JUnit and Mockito. Nevertheless, the ones mentioned are commonly used in day-to-day coding activities.