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

时间:2014-10-17 08:34:06

标签: java junit mockito

我正在尝试用jUnit和Mockito测试我的实现,我遇到了问题。这是一个非常简化的例子,解释了这个问题

接口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;
    }

}

使用"业务逻辑"

进行分类
public class ValueFinder {

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

}

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.

    }

}

我的问题是,由于技术原因,我需要模拟KeyValue,这是我无法控制的。因此我不能只使用方法testNotMocked()。另外由于我无法控制的技术原因,我必须模拟界面(而不是类)。

有没有办法实现这个目标?

非常感谢。

4 个答案:

答案 0 :(得分:3)

如果您要编写正在测试的方法的javadoc,甚至不知道接口的任何方法正在做什么,那么您将编写以下内容:

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

您甚至不应该假设getValue()返回之前设置的值。这肯定不是模拟会做的,因为除了你告诉它做的事情之外,模拟没有做任何事情。您应该做的就是测试方法的合同,而不必假设接口的实现。所以你的测试应该是

@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);
}

答案 1 :(得分:1)

Mock对你的Impl课程一无所知。因此,只需为verify执行setValue或使用spy调用real方法。

答案 2 :(得分:0)

如果你检查下面的模拟方法的Mockito API,你会发现它创建了给定类或接口的模拟对象。

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

因此第一个方法testMocked1()的错误是有效的。你实际上在做什么是间接地模仿该接口的impl。因此,当您这样做时,所有方法都会被模拟,因为getValue()返回一个String,因此String的默认值为null,因此返回NULL。使用如下的ReflectionUtils直接设置键值

ReflectionTestUtils.setField(classObject, key,keyvalue); 

并在您的方法testMocked1()

中执行以下操作
assertEquals("foo", keyValue.getValue());

类似于第二种方法testMocked2()通过使用reflectionutils设置值并使用Mockito的任何api方法来做同样的事情

答案 3 :(得分:0)

使用/然后配置您的模拟。 见http://www.baeldung.com/mockito-behavior