макет или заглушка для цепного вызова - PullRequest
55 голосов
/ 28 октября 2011
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 ]

124 голосов
/ 28 октября 2011

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, говорит о том, что у вашего класса зависть к функциям .

3 голосов
/ 28 октября 2011

Я предлагаю упростить ваш контрольный пример - реорганизовать ваш метод.

Каждый раз, когда у меня возникают проблемы с тестированием метода, для меня это запах кода, и я спрашиваю, почему это сложно протестировать. И если код трудно тестировать, его, вероятно, сложно использовать и поддерживать.

В данном случае это потому, что у вас есть цепочка методов, которая проходит на несколько уровней. Возможно, передайте в качестве параметров ctx, cacheEnable и cacheExpire.

3 голосов
/ 28 октября 2011

Я обнаружил, что JMockit проще в использовании и полностью переключился на него. Смотрите контрольные примеры, используя его:

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

Здесь я макетирую базовый класс Activity, который идет от 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 голосов
/ 24 января 2019

На всякий случай, если вы используете 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)
...