web-dev-qa-db-ja.com

AspectJのJUnitテスト

カスタムアスペクト用のJunitテストを作成しようとしています。アスペクトクラススニペットは次のとおりです。

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

    }

}
_

したがって、上記のアスペクトは、ジョイントポイントがポイントカットに一致するたびにインターセプトします。うまく動作します。

しかし、私の質問は、そのクラスを単体テストする方法です。次のJunitテストがあります。

_@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);

    }
_

したがって、JunitのmongoTemplate.save(test)は、ポイントカットと一致するため、SampleAspectによってインターセプトされています。しかし、そのジョイントポイントが呼び出されたときにSampleAspectがインターセプトしていることを(おそらくアサートすることによって)junitsでどのように確認する必要がありますか?

intercept()からの戻り値をアサートすることはできません。ジョイントポイントを実行する以外に特別なことは何もしないからです。そのため、アスペクトで実行されているか、戻り値に基づいて定期的に実行されているかにかかわらず、私のJunitは違いを見つけることができません。

アスペクトテストに関するコードスニペットの例は、提供されればすばらしいでしょう。

16
karthik

テストしようとしているのは、アスペクト織りとポイントカットマッチングだと思います。それは単体テストではなく統合であることに注意してください。とにかく「mockito」で質問にタグ付けしたために、アスペクトロジックを本当にユニットテストしたい場合は、それを行うことをお勧めします。アスペクト内ロジックを使用した、もう少し複雑な例を示します。

アスペクトのターゲットとなるJavaクラス:

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

テスト対象のアスペクト:

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

Application.main(..):の実行時のコンソールログ

ご覧のとおり、アスペクトは11を通過し、-22を否定し、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)
_

アスペクトの単体テスト:

ここで、アスペクトが本来すべきことを実行し、すべての実行パスをカバーすることを本当に検証したいのです。

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

次に、この単純なJUnit + Mockitoテストを実行して、アスペクトロジックを単独でテストしますnotワイヤリング/ウィービングロジック。後者の場合、別のタイプのテストが必要になります。

追伸:あなただけにJUnitとMockitoを使用しました。通常、私はSpockとその組み込みのモック機能を使用します。 _;-)_

16
kriegaex