Search code examples
javaunit-testingmockingspring-boot-test

Using mokito.when for method with String and Class<T> arguments


I have a method which I would like to create some unit test (this method warping

applicationContext.getEnvironment().getProperty(key, class)

for consul integration)

Long story short:

public class DynamicProperties {

 public <T> T getEnvironmentProperty(String key, Class<T> cls) {
        return cls.cast(applicationContext.getEnvironment().getProperty(key,cls));
    }
}

when testing some other class which using the DynamicProperties class, as the following:

   @Test
    void testA() {
        //before
        when(dynamicProperties.getEnvironmentProperty(eq(KEY_A), Boolean.class)).thenReturn(true);
        when(dynamicProperties.getEnvironmentProperty(eq(KEY_B), Long.class)).thenReturn(0l);

        //when
        archivedSensorsService.testMethod();

        //than
        verify(...)
    }

KEY_A KEY_B are public static final strings I'm getting the following error:

This exception may occur if matchers are combined with raw values:
    //incorrect:
    someMethod(anyObject(), "raw String");
When using matchers, all arguments have to be provided by matchers.
For example:
    //correct:
    someMethod(anyObject(), eq("String by matcher"));

When trying the following:

 @Test
        void testA() {
            //before
            when(dynamicProperties.getEnvironmentProperty(eq(KEY_A), anyObject())).thenReturn(true);
            when(dynamicProperties.getEnvironmentProperty(eq(KEY_B), anyObject())).thenReturn(0l);
    
            //when
            archivedSensorsService.testMethod();
    
            //than
            verify(...)
        }

Getting the following error:

org.mockito.exceptions.misusing.PotentialStubbingProblem: 
Strict stubbing argument mismatch. Please check:
 - this invocation of 'getEnvironmentProperty' method:
    dynamicProperties.getEnvironmentProperty(
    null,
    null
);
    -> at com.xxx.xxx ionNotBeenTriggered(..)
 - has following stubbing(s) with different arguments:
    1. dynamicProperties.getEnvironmentProperty(
    null,
    null
);

Any suggestions?


Solution

  • The problem is caused by mixing mocking with and without argument matchers. If you use an argument matcher to one of the mocked method arguments, you have to use a matcher for all of them. You can read more here.

    I've created a simple project on GitHub with a solution - you can check it if you like, but here's the snippet:

    @Test
    void withoutEq() {
        DynamicProperties dynamicProperties = mock(DynamicProperties.class);
        when(dynamicProperties.getEnvironmentProperty(KEY_A, Boolean.class))
                .thenReturn(true);
        when(dynamicProperties.getEnvironmentProperty(KEY_B, Long.class))
                .thenReturn(1L);
    
        assertAll(
                () -> assertTrue(dynamicProperties.getEnvironmentProperty(KEY_A, Boolean.class)),
                () -> assertEquals(1, dynamicProperties.getEnvironmentProperty(KEY_B, Long.class))
        );
    }
    
    @Test
    void withEq() {
        DynamicProperties dynamicProperties = mock(DynamicProperties.class);
        when(dynamicProperties.getEnvironmentProperty(eq(KEY_A), eq(Boolean.class)))
                .thenReturn(true);
        when(dynamicProperties.getEnvironmentProperty(eq(KEY_B), eq(Long.class)))
                .thenReturn(1L);
    
        assertAll(
                () -> assertTrue(dynamicProperties.getEnvironmentProperty(KEY_A, Boolean.class)),
                () -> assertEquals(1, dynamicProperties.getEnvironmentProperty(KEY_B, Long.class))
        );
    }
    

    As you can see - one of the methods uses eq matcher for both of the method's arguments and the other one uses none. Both tests pass.

    In your case

    when(dynamicProperties.getEnvironmentProperty(eq(KEY_A), anyObject())).thenReturn(true);
    

    this mock did not cause "matcher combined with raw values" error, because eq and anyObject are both argument matchers, but

    when(dynamicProperties.getEnvironmentProperty(eq(KEY_A), Boolean.class)).thenReturn(true);
    

    has a matcher (eq) and a simple object (Boolean.class without eq).