やりたいこと
Spring AOPで作った業務横断的な処理を持ったクラスを単体でテストしたい。
Spring AOP の仕組み(ざっくり)と単体テスト時の問題点
Spring AOPはDIを基に成立っており、コンポーネントを使う側がproxy(コンテナに登録したBeanそのものではなく、そのBeanに対しAOPで定義した機能を拡張(enhance)させた)インスタンスをインジェクトすることにより、AOPを実現します。
しかしながら、今回やりたいのは単体テストなので、DIコンテナを起動させず、コンテキストに依存しないでテストをする方法を用意したいと思います。
今回用意したサンプル
引数にnull
があればNullPointerException
を投げる処理をAOPで実装して、TestService
(テスト用クラス)クラスのメソッドに適用させます。(NullCheckアノテーションは単なるマーカーなのでここでは省略します。)
@Aspect
@Component
public class AspectLogic {
@Around("@annotation(NullCheck)")
public Object invoke(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("NullCheck!!");
Stream.of(proceedingJoinPoint.getArgs()).forEach(Objects::requireNonNull);//全引数を取得してObejcts::requireNonNullでチェック
return proceedingJoinPoint.proceed();
}
}
public class TestService {
@NullCheck
public void doSomething(Object arg) {
}
}
AspectJProxyFactoryからproxyを取得する。
Spring DIがBeanからproxyを取得する部分を自前で実装しましょう。AspectJProxyFactory
のインスタンス
に今回作成したAspectなクラスをaddします。
ファクトリから取得したproxyを使ってテストサンプルメソッドを叩くと、AOPで定義した処理が呼ばれ、NullPointerException
が投げられていることが確認できたかと思われます。
public class TestAspectLogic {
@Test
public void testAspect() {
AspectJProxyFactory factory = new AspectJProxyFactory(new TestService());
factory.addAspect(new AspectLogic()); //ここでAspectLogicクラスを適用
TestService proxy = factory.getProxy();
try {
proxy.doSomething(null);
failBecauseExceptionWasNotThrown(NullPointerException.class);
} catch(NullPointerException npe) {
}
}
}
NullCheck!!
まとめ
AOPもしっかりテストを書きましょう。