2014-10-17 116 views
2

我想用jUnit和Mockito测试我的实现,并且遇到问题。这是说明问题嘲笑Mockito接口的getter/setter方法

接口KeyValueInterface

public interface KeyValueInterface { 

    public abstract String getKey(); 

    public abstract void setKey(String key); 

    public abstract String getValue(); 

    public abstract void setValue(String value); 

} 

类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()。同样出于技术原因,我不得不嘲笑界面(而不是类)。

有什么办法可以达到这个目的吗?

非常感谢。

回答

2

如果你写你的测试,甚至不知道什么任何的接口的方法正在做的方法的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

模拟不知道任何关于您的Impl类。因此,要么verifysetValue或使用间谍呼叫方法。

0

如果选中API的Mockito的模拟方法下面你可以看到,它创建一个给定类或接口的模拟对象。

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

所以第一个方法testMocked1()的错误是有效的。你实际上在做的是间接地嘲笑那个接口的impl。所以当你这样做时,所有的方法都会被模拟,因为getValue()返回一个String,所以String的默认值为null,所以返回NULL。使用ReflectionUtils像下面为第二个方法testMocked2设置键值直接

ReflectionTestUtils.setField(classObject, key,keyvalue); 

,并在你的方法下面做testMocked1()

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

类似的()通过使用reflectionutils设定值做同样的并使用Mockito的任何API方法

+0

谢谢,你的意思是来自Spring框架的ReflectionTestUtils? – Paul 2014-10-17 09:09:25

+0

是的,但总体思路应该是相同的 – vikeng21 2014-10-17 11:26:29