Real method on mocked object is always called
  • 21-May-2023
Lightrun Team
Author Lightrun Team
Share
Real method on mocked object is always called

Real method on mocked object is always called

Lightrun Team
Lightrun Team
21-May-2023

Explanation of the problem

 

An issue arises when attempting to mock a class from a Kotlin library using the org.mockito:mockito-inline:2.7.19 library. The problem occurs within the TheTest class, where two mock objects, kit of type AndroidKit and resources of type Resources, are defined. The issue arises in the stepsIntoFunctionOfMockedClass test method, where the intention is to mock the behavior of the kit.getResource function using the given statement and willReturn method. Here is the code snippet:

 

class TheTest {
    val kit: AndroidKit = mock()
    val resources: Resources = mock()

    @Test
    fun stepsIntoFunctionOfMockedClass() {
        given(kit.getResource(resources, 123, String::class)).willReturn("resource")
    }
}

 

Initially, when mocking the resources.getString function using given(resources.getString(any())).willReturn(""), the test runs successfully. However, the problem arises in the given statement when trying to mock kit.getResource. Instead of being properly mocked, the actual function kit.getResource is called, resulting in a NullPointerException (NPE) due to unmocked function calls on resources. Any suggestions or ideas to overcome this issue would be greatly appreciated.

For easier reproduction of the problem, a sample project containing the androidKit library is provided.

 

Troubleshooting with the Lightrun Developer Observability Platform

 

Getting a sense of what’s actually happening inside a live application is a frustrating experience, one that relies mostly on querying and observing whatever logs were written during development.
Lightrun is a Developer Observability Platform, allowing developers to add telemetry to live applications in real-time, on-demand, and right from the IDE.

  • Instantly add logs to, set metrics in, and take snapshots of live applications
  • Insights delivered straight to your IDE or CLI
  • Works where you do: dev, QA, staging, CI/CD, and production

Start for free today

Problem solution for: Real method on mocked object is always called

 

To resolve the issue of the NullPointerException (NPE) when mocking the kit.getResource function in the given statement, you can use the Mockito.spy() method instead of mock() to partially mock the kit object. By doing so, you can retain the original behavior of the getResource function while mocking other function calls on the kit object. Here’s an updated solution:

 

class TheTest {
    val kit: AndroidKit = spy(AndroidKit())
    val resources: Resources = mock()

    @Test
    fun stepsIntoFunctionOfMockedClass() {
        given(kit.getResource(resources, 123, String::class)).willReturn("resource")
    }
}

 

In the updated code snippet, the kit object is created using spy(AndroidKit()), which allows partial mocking. This means that the actual implementation of the getResource function will be invoked, while other function calls on kit can still be mocked using given and willReturn. By using spy() instead of mock(), you avoid the NPE caused by unmocked function calls on resources.

This solution ensures that the desired behavior is mocked for the getResource function while keeping the original functionality intact for other functions within the kit object.

 

Problems with mockito

 

Problem 1: Unwanted Invocation of Real Methods One common issue with Mockito is the unintentional invocation of real methods when using the mock() method. This can lead to unexpected behavior or even errors in test cases. Consider the following scenario:

 

public class Example {
    public String getValue() {
        return "Real Value";
    }
}

public class TestExample {
    @Test
    public void testGetValue() {
        Example example = mock(Example.class);
        String value = example.getValue();
        assertEquals("Mocked Value", value); // This assertion fails
    }
}

 

In this case, the getValue() method of the Example class should be mocked to return a specific value. However, since mock() creates a mock object with default behavior, the real implementation of getValue() is invoked, leading to the test failure.

Solution: To overcome this issue, you can use the when() method provided by Mockito to define the desired behavior explicitly. By specifying the mocked behavior, you ensure that the real methods are not accidentally invoked. Here’s an updated solution:

 

public class TestExample {
    @Test
    public void testGetValue() {
        Example example = mock(Example.class);
        when(example.getValue()).thenReturn("Mocked Value");

        String value = example.getValue();
        assertEquals("Mocked Value", value); // This assertion passes
    }
}

 

By using when().thenReturn(), you instruct Mockito to return the specified value when the getValue() method is called on the mock object. This ensures that the desired behavior is correctly mocked, avoiding any unwanted invocation of real methods.

Problem 2: Stubbing Void Methods Mockito provides ways to stub method calls and define their return values. However, stubbing void methods poses a challenge since they don’t have a return value. Consider the following scenario:

 

public class Example {
    public void performAction() {
        // Perform some action
    }
}

public class TestExample {
    @Test
    public void testPerformAction() {
        Example example = mock(Example.class);
        when(example.performAction()).thenReturn(); // This line causes a compilation error
    }
}

 

In this case, stubbing the performAction() void method using when().thenReturn() results in a compilation error.

Solution: To address this problem, Mockito provides the doNothing() method that can be used to stub void methods. Here’s an updated solution:

 

public class TestExample {
    @Test
    public void testPerformAction() {
        Example example = mock(Example.class);
        doNothing().when(example).performAction();

        example.performAction(); // No compilation error
        // Additional assertions or verifications can be performed
    }
}

 

Using doNothing().when() allows you to stub the void method without specifying a return value. This ensures that the method is correctly stubbed, and you can continue with additional assertions or verifications as needed.

Problem 3: Verification Failure with Argument Matchers Another common issue with Mockito arises when verifying method invocations that use argument matchers. Argument matchers, such as any(), are used to match any value of a specific type. However, improper use of argument matchers can lead to verification failures. Consider the following example:

 

public class Example {
    public void performAction(String value) {
        // Perform some action
    }
}

public class TestExample {
    @Test
    public void testPerformAction() {
        Example example = mock(Example.class);
        example.performAction("Test");

        verify(example).performAction(any(String.class)); // This verification fails
    }
}

 

Solution: To resolve this issue, you need to ensure proper usage of argument matchers when verifying method invocations. Mockito provides a set of matcher methods that should be used consistently. Here’s an updated solution:

 

public class TestExample {
    @Test
    public void testPerformAction() {
        Example example = mock(Example.class);
        example.performAction("Test");

        verify(example).performAction(eq("Test")); // Use 'eq()' to match specific values
    }
}

 

In the updated solution, the eq() matcher is used instead of any(). The eq() matcher matches the exact value provided as an argument, ensuring the verification passes only when the expected value is encountered.

By using the appropriate argument matchers and ensuring consistency in their usage, you can avoid verification failures and accurately validate method invocations.

 

A brief introduction to mockito

 

Mockito is a popular Java mocking framework used for creating test doubles (mock objects) in unit testing. It provides an easy-to-use API that allows developers to create mock objects, define their behavior, and verify interactions with them. Mockito is widely used in the software development industry to write unit tests that are isolated from external dependencies, enabling focused testing of specific components or classes.

With Mockito, developers can create mock objects that mimic the behavior of real objects, allowing them to simulate complex scenarios and control the outcomes of method invocations. Mock objects can be configured to return specific values, throw exceptions, or perform custom behaviors when their methods are called. This flexibility empowers developers to write comprehensive tests that cover various scenarios and edge cases.

Mockito also offers powerful verification capabilities, allowing developers to verify that specific methods were called with expected arguments, ensuring the correct interactions between objects. It provides a range of matchers to precisely define the expected behavior, such as matching exact values, any value, or values based on custom conditions. Mockito’s intuitive and expressive API simplifies the process of defining mock behavior and verifying method invocations, making it a valuable tool for unit testing in Java applications.

 

Most popular use cases for mockito

  1. Mocking Dependencies: Mockito can be used to mock external dependencies in unit tests. By creating mock objects that simulate the behavior of real objects, developers can isolate the code under test and focus on testing specific components or classes. This allows for more reliable and repeatable testing, as the test environment is not affected by external factors.

Example code:

// Create a mock object for a dependency
DatabaseService databaseService = mock(DatabaseService.class);

// Define the behavior of the mock object
when(databaseService.retrieveData()).thenReturn("Mocked data");

// Use the mock object in the test
MyService myService = new MyService(databaseService);
String result = myService.getData();
assertEquals("Mocked data", result);
  1. Behavior Verification: Mockito provides powerful verification capabilities to ensure the correct interactions between objects. Developers can verify that specific methods were called with expected arguments, allowing them to validate the behavior of their code. This helps in detecting unexpected method invocations and ensuring the proper flow of data and control.

Example code:

// Create a mock object
MyService myService = mock(MyService.class);

// Call a method on the mock object
myService.performOperation("data");

// Verify that the method was called with the expected argument
verify(myService).performOperation("data");
  1. Stubbing Method Calls: Mockito allows developers to stub method calls on mock objects, specifying the desired behavior for different scenarios. This enables the simulation of specific conditions and the control of return values or exceptions. By stubbing method calls, developers can test their code under different scenarios without relying on real dependencies.

Example code:

// Create a mock object
MyService myService = mock(MyService.class);

// Stub the method call to return a specific value
when(myService.calculateResult(5, 10)).thenReturn(50);

// Use the mock object in the test
int result = myService.calculateResult(5, 10);
assertEquals(50, result);
Share

It’s Really not that Complicated.

You can actually understand what’s going on inside your live applications.

Try Lightrun’s Playground

Lets Talk!

Looking for more information about Lightrun and debugging?
We’d love to hear from you!
Drop us a line and we’ll get back to you shortly.

By submitting this form, I agree to Lightrun’s Privacy Policy and Terms of Use.