web-dev-qa-db-fra.com

Tests JUnit pour AspectJ

J'essaie d'écrire des tests Junit pour un aspect personnalisé. Voici l'extrait de classe Aspect:

@Aspect
@Component
public class SampleAspect {

    private static Logger log = LoggerFactory.getLogger(SampleAspect.class);

    @Around("execution(* org.springframework.data.mongodb.core.MongoOperations.*(..)) || execution(* org.springframework.web.client.RestOperations.*(..))")
    public Object intercept(final ProceedingJoinPoint point) throws Throwable {
        logger.info("invoked Cutom aspect");
         return point.proceed();

    }

}

Ainsi, l'aspect ci-dessus intercepte chaque fois que jointpoint correspond au pointcut. Son fonctionne bien.

Mais ma question est de savoir comment tester à l'unité cette classe. J'ai le test Junit suivant:

@Test(expected = MongoTimeoutException.class)
    public void TestWithMongoTemplate() {
        //MongoDocument class
        TestDocument test = new TestDocument();

        ApplicationContext ctx = new AnnotationConfigApplicationContext(TestMongoConfigurationMain.class);
        MongoTemplate mongoTemplate = ctx.getBean(MongoTemplate.class);

        //this call is being intercepted by SampleAspect
        mongoTemplate.save(test);

    }

Ainsi, ma mongoTemplate.save(test) dans Junit est interceptée par SampleAspect car elle correspond à pointcut. Mais comment dois-je m'assurer dans les junits (probablement en affirmant) que mon SampleAspect intercepte quand ce point commun est invoqué?

Je ne peux pas affirmer sur la valeur de retour de intercept() car il ne fait rien de spécial autre que l'exécution de joint point. Donc, mon Junit ne trouve aucune différence, qu'il soit exécuté par aspect ou une exécution régulière basée sur les valeurs de retour.

Tout exemple d'extraits de code sur les tests d'aspect serait formidable s'il était fourni.

16
karthik

Je pense que ce que vous essayez de tester est le tissage d'aspect et la correspondance de points. Veuillez noter que ce serait une intégration plutôt qu'un test unitaire. Si vous voulez vraiment tester uniquement la logique de votre aspect et parce que vous avez quand même marqué la question par "mockito", je vous suggère de faire juste cela: Écrivez un test unitaire et moquez le point de jonction de l'aspect et peut-être ses autres paramètres, le cas échéant. Voici un exemple légèrement plus complexe avec une logique intra-aspect:

classe Java à cibler par aspect:

package de.scrum_master.app;

public class Application {
    public static void main(String[] args) {
        new Application().doSomething(11);
        new Application().doSomething(-22);
        new Application().doSomething(333);
    }

    public void doSomething(int number) {
        System.out.println("Doing something with number " + number);
    }
}

Aspect sous test:

package de.scrum_master.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class SampleAspect {
    @Around("execution(* doSomething(int)) && args(number)")
    public Object intercept(final ProceedingJoinPoint thisJoinPoint, int number) throws Throwable {
        System.out.println(thisJoinPoint + " -> " + number);
        if (number < 0)
            return thisJoinPoint.proceed(new Object[] { -number });
        if (number > 99)
            throw new RuntimeException("oops");
        return thisJoinPoint.proceed();
    }
}

Journal de la console lors de l'exécution de Application.main(..):

Comme vous pouvez le voir, l'aspect passe sur 11, annule -22 et lève une exception pour 333:

execution(void de.scrum_master.app.Application.doSomething(int)) -> 11
Doing something with number 11
execution(void de.scrum_master.app.Application.doSomething(int)) -> -22
Doing something with number 22
execution(void de.scrum_master.app.Application.doSomething(int)) -> 333
Exception in thread "main" Java.lang.RuntimeException: oops
    at de.scrum_master.aspect.SampleAspect.intercept(SampleAspect.aj:15)
    at de.scrum_master.app.Application.doSomething(Application.Java:10)
    at de.scrum_master.app.Application.main(Application.Java:7)

Test unitaire pour l'aspect:

Maintenant, nous voulons vraiment vérifier que l'aspect fait ce qu'il devrait et couvrir tous les chemins d'exécution:

package de.scrum_master.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;

import static org.mockito.Mockito.*;

public class SampleAspectTest {
    @Rule
    public MockitoRule mockitoRule = MockitoJUnit.rule();

    @Mock
    private ProceedingJoinPoint proceedingJoinPoint;

    private SampleAspect sampleAspect = new SampleAspect();

    @Test
    public void testPositiveSmallNumber() throws Throwable {
        sampleAspect.intercept(proceedingJoinPoint, 11);
        // 'proceed()' is called exactly once
        verify(proceedingJoinPoint, times(1)).proceed();
        // 'proceed(Object[])' is never called
        verify(proceedingJoinPoint, never()).proceed(null);
    }

    @Test
    public void testNegativeNumber() throws Throwable {
        sampleAspect.intercept(proceedingJoinPoint, -22);
        // 'proceed()' is never called
        verify(proceedingJoinPoint, never()).proceed();
        // 'proceed(Object[])' is called exactly once
        verify(proceedingJoinPoint, times(1)).proceed(new Object[] { 22 });
    }

    @Test(expected = RuntimeException.class)
    public void testPositiveLargeNumber() throws Throwable {
        sampleAspect.intercept(proceedingJoinPoint, 333);
    }
}

Exécutez maintenant ce test JUnit + Mockito simple afin de tester la logique de l'aspect de manière isolée, et non la logique de câblage/tissage. Pour ce dernier, vous auriez besoin d'un autre type de test.

P.S .: Seulement pour vous, j'ai utilisé JUnit et Mockito. Habituellement, j'utilise simplement Spock et ses capacités de simulation intégrées. ;-)

16
kriegaex