简体   繁体   中英

Mocking getter / setter method of Interface with Mockito

I am trying to test my implementation with jUnit and Mockito and I am running into problems. Here is a very simplified example which explains the issue

Interface KeyValueInterface

public interface KeyValueInterface {

    public abstract String getKey();

    public abstract void setKey(String key);

    public abstract String getValue();

    public abstract void setValue(String value);

}

Class KeyValueImpl

public class KeyValueImpl implements KeyValueInterface {

    private String key;
    private String value;

    @Override
    public String getKey() {
        return key;
    }

    @Override
    public void setKey(String key) {
        this.key = key;
    }

    @Override
    public String getValue() {
        return value;
    }

    @Override
    public void setValue(String value) {
        this.value = value;
    }

}

Class with "business logic"

public class ValueFinder {

    public KeyValueInterface findValueForKey(KeyValueInterface keyValue){
        keyValue.setValue("foo");
        return keyValue;
    }

}

jUnit Test class

import static org.junit.Assert.*;

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

public class ValueFinderTest {

    @Test
    public void testNotMocked() {
        KeyValueInterface keyValue = new KeyValueImpl();
        keyValue = (new ValueFinder()).findValueForKey(keyValue);
        assertEquals("foo", keyValue.getValue()); // works fine
    }

    @Test
    public void testMocked1() {
        KeyValueInterface keyValue = Mockito.mock(KeyValueInterface.class);
        keyValue = (new ValueFinder()).findValueForKey(keyValue);
        assertEquals("foo", keyValue.getValue()); // java.lang.AssertionError:
                                                    // expected:<foo> but
                                                    // was:<null>

    }

    @Test
    public void testMocked2() {
        KeyValueInterface keyValue = Mockito.mock(KeyValueInterface.class);
        keyValue = (new ValueFinder()).findValueForKey(keyValue);
        Mockito.when(keyValue.getValue()).thenCallRealMethod();
        Mockito.doCallRealMethod().when(keyValue).setValue(Mockito.any(String.class));
        assertEquals("foo", keyValue.getValue()); // org.mockito.exceptions.base.MockitoException:
                                                    // Cannot call real method
                                                    // on java interface.
                                                    // Interface does not have
                                                    // any implementation!
                                                    // Calling real methods is
                                                    // only possible when
                                                    // mocking concrete classes.

    }

}

My probelm is, that I need to mock KeyValue for technical reasons which are beyond my control. Therefore I cannot just go with method testNotMocked(). Also for technical reasons beyond my control I have to mock the interface (and not the class).

Is there any way to achieve this?

Thanks a lot.

If you were to write the javadoc of the method you're testing, without even knowing what any of the methods of the interface are doing, you would write the following:

/**
 * Sets "foo" as the value of the given keyValue, and returns it
 */

You shouldn't even assume that getValue() returns the value that has been set before. This is certainly not what the mock will do, since the mock doesn't do anything other than what you tell it to do. All you should do is test the contract of your method, without assuming anything about the implementation of the interface. So your test should be

@Test
public void testMocked1() {
    KeyValueInterface keyValue = Mockito.mock(KeyValueInterface.class);
    KeyValueInterface result = (new ValueFinder()).findValueForKey(keyValue);

    // tests that the value has been set to "foo"
    verify(keyValue).setValue("foo");

    // tests that the method returns its argument
    assertSame(keyValue, result);
}

Mock does not know anything about your Impl class. So, just either do verify for setValue or use spy to callreal methods.

if you check the Mockito API for the mock method below you can see that it creates mock object of given class or interface.

public static <T> T mock(java.lang.Class<T> classToMock)

So the error for the first method testMocked1() is a valid one. what you are actually doing there is mocking the impl for that interface indirectly. So when you do that all the methods get mocked and since getValue() returns a String so the default value of String is null so a NULL is getting returned. Use ReflectionUtils like below to set key value directly

ReflectionTestUtils.setField(classObject, key,keyvalue); 

and do below in your method testMocked1()

assertEquals("foo", keyValue.getValue());

Similarly for the 2nd method testMocked2() do the same by using reflectionutils to set the value and use any of the api methods from Mockito

Use when/then to configure your mock. See http://www.baeldung.com/mockito-behavior

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM