模拟或存根链接调用
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()); }
那是testing的方法,
Method targetMethod = ctx.getTargetMethod(); CacheEnable cacheEnable = targetMethod.getAnnotation(CacheEnable.class);
我必须嘲笑三个CacheContext,方法和CacheEnable。 有什么想法让testing案例更简单吗?
Mockito 可以处理链式存根 :
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,链中的第一个方法返回一个模拟,它被设置为在第二个链式方法调用中返回你的值。
Mockito的作者指出,这只能用于遗留代码 。 否则,更好的做法是将行为推送到您的CacheContext中,并提供执行工作所需的任何信息。 您从CacheContext提取的信息量表明您的课程具有令人羡慕的function 。
我发现JMockit更容易使用ans完全切换到它。 使用它查看testing用例:
在这里,我嘲笑来自Android SKD的Activity基类,它完全被删除。 随着JMockit,你可以嘲笑事情是最终的,私人的,抽象的或任何其他。
在你的testing用例中,它看起来像:
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))
我的build议使你的testing用例更简单,就是重构你的方法。
每当我发现自己在testing方法时遇到麻烦,对我来说这是一种代码味道,我问为什么很难testing。 如果代码很难testing,可能很难使用和维护。
在这种情况下,这是因为你有一个方法链深入几个层次。 也许传递ctx,cacheEnable和cacheExpire作为参数。