使用Java8 lambda在mocked类上进行参数匹配

时间:2017-09-01 05:07:35

标签: java unit-testing lambda mockito

我是单元测试的类接受一个依赖项并在该依赖项上调用一个函数。该函数将复杂对象作为其参数并生成结果。现在,我想模拟依赖关系,并根据传入的参数返回一些内容。下面给出了一个简化的工作版本。

我可以在n_0方法中使用Java 8 lambda表达式来消除when类吗?像下面评论的代码(不编译)。

ArgHasNext

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

修改 好吧,也许我过度简化了这个例子。在实际情况中,依赖项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; } } 方法返回在测试方法返回之前在func中处理的分页查询结果。根据{{​​1}},我希望依赖项SUT在第一次调用时返回第1页结果,第二次返回第2页结果。我只能在模拟返回基于传递给函数调用的参数的不同值时执行此操作。这可能在Mockito使用lambdas吗?

3 个答案:

答案 0 :(得分:3)

  

现在,我想模拟依赖项,并根据传入的参数返回一些内容。

你不应该首先这样做。 任何机会让你的模拟返回定义良好的常量值。

原因是您应该尽可能简化测试代码,以降低测试失败的风险,因为测试代码是错误的。

您的问题的解决方案可能是mockitos Answer interface:

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();

答案 1 :(得分:2)

我认为你正在走错了兔子洞。含义:模拟规范是 mock 规范。他们不应该任何其他。

我的意思是:我建议您编写如下测试:

@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方法调用该方法。

换句话说:不要花费大量的时间和精力来制定“聪明”的嘲弄规范。而是专注于编写简单的直接测试。

当你可以在没有对传递给模拟调用的参数进行复杂分析的情况下编写测试时,那么就去找那个解决方案。

答案 2 :(得分:2)

回答我自己的问题。我过于专注于根据传入的参数使模拟返回不同的东西,因为使用不同的复杂参数对象多次调用mock方法。

解决方案是在后续调用中返回不同的结果,使用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");
    }
}