2014-10-17 144 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