Макет или заглушка для прикованного вызова
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.
Есть ли идея сделать тест более простым?
Ответы
Ответ 1
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, говорит о том, что у вашего класса есть зависть к функциям.
Ответ 2
Я обнаружил, что 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))
Ответ 3
Мое предложение упростить ваш тестовый пример - реорганизовать ваш метод.
В любое время, когда у меня возникают проблемы с тестированием метода, это запах кода для меня, и я спрашиваю, почему это трудно проверить. И если код трудно тестировать, его, вероятно, трудно использовать и поддерживать.
В этом случае это потому, что у вас есть цепочка методов, которая имеет несколько уровней в глубину. Возможно, перейдите в ctx, cacheEnable и cacheExpire в качестве параметров.
Ответ 4
На всякий случай, если вы используете Kotlin. MockK ничего не говорит о цепочке, являющейся плохой практикой, и легко позволяет вам сделать это.
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)