用于链接呼叫的模拟或存根

时间:2011-10-28 08:34:19

标签: java unit-testing mocking mockito

protected int parseExpire(CacheContext ctx) throws AttributeDefineException {
    Method targetMethod = ctx.getTargetMethod();
    CacheEnable cacheEnable = targetMethod.getAnnotation(CacheEnable.class);
    ExpireExpr cacheExpire = targetMethod.getAnnotation(ExpireExpr.class);
    // check for duplicate setting
    if (cacheEnable.expire() != CacheAttribute.DO_NOT_EXPIRE && cacheExpire != null) {
        throw new AttributeDefineException("expire are defined both in @CacheEnable and @ExpireExpr");
    }
    // expire time defined in @CacheEnable or @ExpireExpr
    return cacheEnable.expire() != CacheAttribute.DO_NOT_EXPIRE ? cacheEnable.expire() : parseExpireExpr(cacheExpire, ctx.getArgument());
}

这是要测试的方法,

Method targetMethod = ctx.getTargetMethod();
CacheEnable cacheEnable = targetMethod.getAnnotation(CacheEnable.class);

我必须模拟三个CacheContext,Method和CacheEnable。 有什么想法让测试用例更简单吗?

4 个答案:

答案 0 :(得分:119)

Mockito can handle chained stubs

Foo mock = mock(Foo.class, RETURNS_DEEP_STUBS);

// note that we're stubbing a chain of methods here: getBar().getName()
when(mock.getBar().getName()).thenReturn("deep");

// note that we're chaining method calls: getBar().getName()
assertEquals("deep", mock.getBar().getName());

AFAIK,链中的第一个方法返回一个mock,它被设置为在第二个链式方法调用上返回你的值。

Mockito的作者指出,这应该仅用于遗留代码。否则更好的做法是将行为推送到CacheContext中,并提供完成工作所需的任何信息。您从CacheContext中提取的信息量表明您的班级有feature envy

答案 1 :(得分:3)

我发现JMockit更容易使用而且完全切换到它。查看使用它的测试用例:

https://github.com/ko5tik/andject/blob/master/src/test/java/de/pribluda/android/andject/ViewInjectionTest.java

在这里,我模拟了活动基类,它完全来自Android SKD 存根。使用JMockit,您可以模拟最终,私有,抽象或其他任何东西。

在您的测试用例中,它看起来像:

public void testFoo(@Mocked final Method targetMethod, 
                    @Mocked  final CacheContext context,
                    @Mocked final  CacheExpire ce) {
    new Expectations() {
       {
           // specify expected sequence of infocations here

           context.getTargetMethod(); returns(method);
       }
    };

    // call your method
    assertSomething(objectUndertest.cacheExpire(context))

答案 2 :(得分:3)

我建议让你的测试用例更简单,就是重构你的方法。

任何时候我发现自己在测试一个方法时遇到了麻烦,这对我来说是一种代码味道,我想问为什么很难测试。如果代码难以测试,则可能难以使用和维护。

在这种情况下,这是因为你有一个深层次的方法链。也许传入ctx,cacheEnable和cacheExpire作为参数。

答案 3 :(得分:2)

以防万一,如果您使用的是Kotlin。 MockK并没有说说链接是一种不好的做法,它很容易让您执行this

val car = mockk<Car>()

every { car.door(DoorType.FRONT_LEFT).windowState() } returns WindowState.UP

car.door(DoorType.FRONT_LEFT) // returns chained mock for Door
car.door(DoorType.FRONT_LEFT).windowState() // returns WindowState.UP

verify { car.door(DoorType.FRONT_LEFT).windowState() }

confirmVerified(car)