web-dev-qa-db-fra.com

maquette ou tronçon pour appel chaîné

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());
}

c'est la méthode à tester, 

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

Je dois me moquer de trois CacheContext, Method et CacheEnable . Avez-vous une idée pour rendre le scénario de test beaucoup plus simple?

48
jilen

Mockito peut gérer les tronçons chaînés :

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());

Autant que je sache, la première méthode de la chaîne renvoie un modèle, qui est configuré pour renvoyer votre valeur lors du deuxième appel de méthode chaînée.

Les auteurs de Mockito notent que cela devrait ne doit être utilisé que pour du code hérité}. Sinon, une meilleure chose à faire est d'insérer le comportement dans votre CacheContext et de fournir toutes les informations nécessaires pour effectuer le travail lui-même. La quantité d'informations que vous extrayez de CacheContext suggère que votre classe a envie de fonctionnalités .

111
Lunivore

J'ai trouvé JMockit plus facile à utiliser et y est complètement passé. Voir les cas de test l’utilisant:

https://github.com/ko5tik/andject/blob/master/src/test/Java/de/pribluda/Android/andject/ViewInjectionTest.Java

Ici, je me moque de la classe de base de l'activité, qui vient d'Android SKD et complètement Stubbed. Avec JMockit, vous pouvez vous moquer des choses finales, privées, abstraites ou autres.

Dans votre test, cela ressemblerait à ceci:

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

Ma suggestion pour simplifier votre scénario de test est de reformuler votre méthode.

Chaque fois que j'ai de la difficulté à tester une méthode, c'est une odeur de code pour moi et je demande pourquoi est-ce difficile de tester. Et si le code est difficile à tester, il est probablement difficile à utiliser et à maintenir. 

Dans ce cas, c'est parce que vous avez une chaîne de méthodes qui va à plusieurs niveaux. Peut-être transmettre ctx, cacheEnable et cacheExpire en tant que paramètres. 

3
Doug R

Juste au cas où vous utilisez Kotlin. MockK ne dit pas que l'enchaînement est une mauvaise pratique et vous permet facilement de faire ceci .

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)
0
yuranos87