2017-09-01 25 views
2

我正在進行單元測試的類需要一個依賴項並調用該依賴項的函數。該函數將一個複雜的對象作爲參數,併產生一個結果。現在,我想嘲笑依賴關係,並根據傳入的參數返回一些內容。下面給出了一個簡化的工作版本。在模擬類中使用Java8 lambda進行參數匹配

我可以在when方法中使用Java 8 lambda表達式來消除ArgHasNext類嗎?像下面的註釋代碼(不編譯)。

class ArgHasNext implements ArgumentMatcher<Arg> { 
    public boolean matches(Arg arg) { 
     return arg.hasNext(); 
    } 

    @Override 
    public boolean matches(Object o) { 
     return matches((Arg)o); 
    } 
} 

@RunWith(MockitoJUnitRunner.class) 
public class ArgumentMatcherTest { 

    @Mock 
    private Dependency dep = mock(Dependency.class); 

    @Test 
    public void test() { 

     when(dep.func(argThat(new ArgHasNext()))).thenReturn(true); 
     // when(dep.func(argThat((Arg a) -> a.hasNext()))).thenReturn(true); 
     // when(dep.func(argThat((Arg a) -> !a.hasNext()))).thenReturn(false); 
     Sut sut = new Sut(dep); 
     assertEquals(sut.method(new Arg(true)), "True"); 
     assertEquals(sut.method(new Arg(false)), "False"); 
    } 
} 

class Arg { 
    private boolean hasNext; 

    public Arg(boolean hasNext) { 
     this.hasNext = hasNext; 
    } 

    public boolean hasNext() { 
     return this.hasNext; 
    } 
} 

class Sut { 
    private Dependency dep; 

    public Sut(Dependency dep) { 
     this.dep = dep; 
    } 

    public String method(Arg arg) { 

     if (dep.func(arg)) { 
      return "True"; 
     } 
     else { 
      return "False"; 
     } 
    } 

} 

class Dependency { 
    public boolean func(Arg arg) { 
     if (arg.hasNext()) { 
      return true; 
     } 
     return false; 
    } 
} 

我正在使用Mockito核心版本2.0.54-beta。

編輯 好吧,也許我過分簡化了這個例子。在實際情況中,依賴項func方法返回在被測試方法返回之前在SUT中處理的分頁查詢結果。根據Arg,我希望依賴項func在第一次調用結果時返回第1頁結果,第2次結果返回結果。我只能做到這一點,當我可以根據傳遞給函數調用的參數模擬返回不同的值。這可能在Mockito中使用lambdas?現在

+0

只是想知道:爲什麼你使用的是一個幾年前的2.0測試版?也許:你能舉一個例子,你如何設想在這裏使用lambdas? – GhostCat

+0

@GhostCat查看測試方法中的兩條註釋行。我認爲這兩條線更具表現力(如果它們能奏效)。 ArgumentMatcher派生類可以工作,但並不明顯。 – MvdD

+0

也許我錯過了一些東西,但我認爲你是在錯誤的地方投入你的時間。看到我的答案。 – GhostCat

回答

3

,我想嘲笑的依賴,有它基於傳入的參數返回的東西。

你不應該這樣做擺在首位。 任何機會讓你的嘲笑返回定義好的常量值。

原因是您應該保持測試代碼儘可能簡單,以減少由於測試代碼錯誤而導致測試失敗的風險。


你的問題的解決方案可能是mockitos Answer接口:

doAnswer(new Answer<YourReturnType>(){ 
    public YourReturnType answer(InvocationOnMock invocation) { 
     YourParameterType parameter = (YourParameterType)invocation.getArguments()[0]; 
     // calculate your return value 
     return yourCalculatedReturnValue; 
    } 
}).when(yourMock).theMethod(any(YourParameterType.class)); 

需要明確的是,我的模擬返回一個恆定值。該模擬被多次調用,我希望它第二次返回一個不同的值。 - MvdD

你應該寫過你的問題。解決方案很簡單,因爲它聽起來:

doReturn(VALUE_FOR_FIRST_CALL). 
    thenReturn(VALUE_FOR_SECOND_CALL). 
    thenReturn(VALUE_FOR_ANY_FURTHER_CALL). 
    when(mock).theMethod(); 

或者,如果你喜歡呆在少健談:

doReturn(VALUE_FOR_FIRST_CALL, VALUE_FOR_SECOND_CALL, VALUE_FOR_ANY_FURTHER_CALL). 
    when(mock).theMethod(); 
+0

而不是'新的答案'Lambda會更簡潔。 –

+0

@ A.H。關因爲我也會寫一個lambda,但* anonymous class *表單對於newbee更具描述性...... –

+0

要清楚,我的模擬正在返回一個常數值。該模擬被多次調用,我希望它第二次返回一個不同的值。 – MvdD

2

我想你會走上錯誤的兔子洞。含義:模擬規格是模擬規格。他們不應該什麼其他。

我的意思是:我會建議你寫這樣的測試:

@Test 
public void testForTrue() { 
    when(dep.func(any()).thenReturn(true); 
    Sut sut = new Sut(dep); 
    assertEquals(sut.method(new Arg(true)), "True"); 
} 

@Test 
public void testForFalse() { 
    when(dep.func(any()).thenReturn(false); 
    Sut sut = new Sut(dep); 
    assertEquals(sut.method(new Arg(false)), "False"); 
} 

如果在所有的,我會再重構爲:

private void testFor(boolean depReturnValue, expectedResult) { 
    when(dep.func(any()).thenReturn(depReturnValue); 
    Sut sut = new Sut(dep); 
    assertEquals(sut.method(new Arg(depReturnValue)), expectedResult); 
} 

,並呼籲從@Test方法,該方法如上所述。

換句話說:不要花費大量的時間和精力來製造「智能」嘲笑規格。而是專注於編寫簡單的直接測試。

當你可以編寫一個測試而不需要對傳遞給模擬調用的參數進行復雜的分析 - 然後去解決這個問題。

+0

我可能不完全清楚我的例子。重點是依賴被多次調用,我希望它每次都返回不同的值。我編輯了這個問題來澄清。 – MvdD

+0

但仍然 - 至少根據'sut.method()'的**用法**,您的斷言使用*相同*參數。因此dep.func()被調用與同一個對象,並因此會一直返回相同的值? – GhostCat

+0

是的,再次簡化的例子。實際上,下一個依賴調用的參數是在'sut.method()'內部生成的,並且取決於第一次調用的返回值。 – MvdD

2

回答我自己的問題。由於模擬方法被多次調用了一個不同的複雜參數對象,我太專注於使模擬模型返回不同的東西,具體取決於傳入的參數。

解決方案是在隨後的調用中返回不同的結果,這很簡單,使用Mockito。

@RunWith(MockitoJUnitRunner.class) 
public class ArgumentMatcherTest { 

    @Mock 
    private Dependency dep = mock(Dependency.class); 

    @Test 
    public void test() { 

     // return true on first invocation, false on second. 
     when(dep.func(anyObject())).thenReturn(true).thenReturn(false); 

     Sut sut = new Sut(dep); 
     assertEquals(sut.method(new Arg(true)), "True"); 
     assertEquals(sut.method(new Arg(false)), "False"); 
    } 
}