Java 链式调用的模拟或存根
这是测试的方法Java 链式调用的模拟或存根,java,unit-testing,mocking,mockito,Java,Unit Testing,Mocking,Mockito,这是测试的方法 protected int parseExpire(CacheContext ctx) throws AttributeDefineException { Method targetMethod = ctx.getTargetMethod(); CacheEnable cacheEnable = targetMethod.getAnnotation(CacheEnable.class); ExpireExpr cacheExpire = targetMet
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());
}
我必须模拟三个CacheContext、Method和CacheEnable。
有什么办法可以使测试用例更简单吗?我发现完全切换到它后,使用ans会更容易。请参阅使用它的测试用例: 在这里,我模拟了活动基类,它来自AndroidSKD,并且完全 存根。有了JMockit,你可以模拟最终的、私有的、抽象的或其他的东西 在您的测试用例中,它看起来像:
Method targetMethod = ctx.getTargetMethod();
CacheEnable cacheEnable = targetMethod.getAnnotation(CacheEnable.class);
莫基托:
AFAIK,链中的第一个方法返回一个mock,它被设置为在第二个链式方法调用中返回您的值
Mockito的作者指出,这应该只用于遗留代码。否则,更好的做法是将行为推送到您的缓存上下文中,并提供执行工作所需的任何信息。您从CacheContext中获取的信息量表明您的类具有。我建议简化测试用例,重构您的方法 每当我发现自己在测试一个方法时遇到困难,我就会闻到代码的味道,我会问为什么很难测试。如果代码很难测试,那么很可能很难使用和维护
在本例中,这是因为您有一个深入到多个层次的方法链。可能将ctx、cacheEnable和cacheExpire作为参数传入 以防您使用Kotlin。MockK没有说链接是一种不好的做法,而且很容易让你这么做
val car=mockk()
每个{car.door(DoorType.FRONT_LEFT).windowState()}返回windowState.UP
car.door(DoorType.FRONT_LEFT)//返回门的链接模拟
car.door(DoorType.FRONT_LEFT).windowState()//返回windowState.UP
验证{car.door(DoorType.FRONT_LEFT).windowState()}
确认(汽车)
考虑您的答案,但我无论如何都在使用mockito。请注意,JMockit有一个专门用于链式调用的注释:@Cascading
。此外,在这种情况下,如果对模拟方法的调用不需要验证,您可能希望使用非严格预期
而不是预期
。谢谢,我错过了此注释;)简化我的单元测试,但是这些字段在运行时来自aop上下文,因此很难简化环境。您可以通过模拟AOP字段注入将字段模拟到对象中。或者你可以使用去封装技术初始化带有模拟实例的验证字段你能提供一个@doug的例子吗?好吧,Szczepan创建了Mockito,因为他看到我和其他一些人用手而不是用EasyMock推出了我们自己的模拟,并决定mocks应该更适合BDD,所以显然我更喜欢Mockito!但他用EasyMock来做这件事,因此,是的,EasyMock很棒。我们站在巨人的肩膀上……如果其中一个链返回泛型类型,这就不起作用。还有其他人面临过这个问题吗?恋物癖嫉妒的定义被移到这里:@Magnilex它实际上在官方来源中。如中所示,源代码。“请注意,在大多数情况下,模拟返回模拟是错误的。”另外:警告:对于常规的干净代码,很少需要此功能!留给遗留代码。Mocking a mock返回mock,返回mock,(…),返回违反德米特定律或模拟值对象(众所周知的反模式)的有意义的提示。(大部分在第1393行)。@RuifengMa我从这个文档中猜测它将是@Mock(answer=RETURNS\u DEEP\u STUBS)-试试看,让我们知道!是的,很棒的事情,真的有助于减少样板代码的数量。
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))
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());
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)