简体   繁体   English

用Mockito模拟接口的getter / setter方法

[英]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. 我试图用jUnit和Mockito测试我的实现,我遇到了问题。 Here is a very simplified example which explains the issue 这是一个非常简单的例子,它解释了这个问题

Interface KeyValueInterface 接口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 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 jUnit测试类

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. 我的问题是,由于技术原因,我需要模拟KeyValue,这是我无法控制的。 Therefore I cannot just go with method testNotMocked(). 因此我不能只使用方法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: 如果您要编写正在测试的方法的javadoc,甚至不知道接口的任何方法在做什么,那么您将编写以下内容:

/**
 * 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. 您甚至不应该假设getValue()返回之前设置的值。 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. Mock对你的Impl课程一无所知。 So, just either do verify for setValue or use spy to callreal methods. 因此,只需要verify setValue或使用spy来调用real方法。

if you check the Mockito API for the mock method below you can see that it creates mock object of given class or interface. 如果您检查下面的模拟方法的Mockito API,您可以看到它创建给定类或接口的模拟对象。

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

So the error for the first method testMocked1() is a valid one. 所以第一个方法testMocked1()的错误是有效的。 what you are actually doing there is mocking the impl for that interface indirectly. 你实际上在做什么是间接地模仿该接口的impl。 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. 因此,当您这样做时,所有方法都会被模拟,因为getValue()返回一个String,因此String的默认值为null,因此返回NULL。 Use ReflectionUtils like below to set key value directly 使用如下的ReflectionUtils直接设置键值

ReflectionTestUtils.setField(classObject, key,keyvalue); 

and do below in your method testMocked1() 并在你的方法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 类似地,对于第二种方法,testMocked2()通过使用reflectionutils设置值并使用Mockito的任何api方法来做同样的事情。

Use when/then to configure your mock. 使用/然后配置您的模拟。 See http://www.baeldung.com/mockito-behavior 请参阅http://www.baeldung.com/mockito-behavior

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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