1. ホーム
  2. java

[解決済み] Spring MVCアプリケーションでAspectをテストする方法

2022-02-18 22:38:51

質問

Spring MVCアプリケーションで、すべてのコントローラメソッドで例外をキャッチするためにAspectを使用しています。

@Component
@Aspect
public class ControllerExceptionAspect {

    private Logger logger;

    public ControllerExceptionAspect() {
       logger = Logger.getLogger(ControllerExceptionAspect.class);
    }

    public ControllerExceptionAspect(Logger logger) {
       this.logger = logger;
    }

    // Catching all exceptions from all methods in all controllers classes

    @AfterThrowing(pointcut = "execution(* com.my.package..controller..*(..))", throwing = "exception")
    public void afterThrowingAdvice(Exception exception) {
       logger.error("CONTROLLER ASPECT: EXCEPTION IN METHOD -> " +    
       exception.getClass());
    }
}

Aspectは正常に動作していますが、残念ながらテストができません。何度も試してみましたが、コントローラで例外をシミュレートした後、Aspectメソッドが呼び出されたかどうかをキャッチする方法を得ることができません。

@SuppressWarnings("ALL")
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextHierarchy({
        @ContextConfiguration(classes = RootConfig.class),
        @ContextConfiguration(classes = WebConfig.class)
})
public class ControllerExceptionAspectTest {

    @Autowired
    ApplicationContext applicationContext;

    @Test
    public void testControllerExceptionAspectGetsExecutedWhenExceptionOccures(){
        HomeController homeController = (HomeController)applicationContext.getAutowireCapableBeanFactory().getBean("homeController");
        try{homeController.callMethod("00000");}
        catch (Exception e){}
        ControllerExceptionAspect controllerExceptionAspect = (ControllerExceptionAspect)applicationContext.getAutowireCapableBeanFactory().getBean("controllerExceptionAspect");
        // HOW TO CATCH THAT ASPECT METHOD WAS CALLED???
    }
}

解決方法は?

アスペクト自体をユニットテストするのではなく、作成した設定(アスペクトのポイントカット)をテストしたいということだと思います。残念なことに、それを実現する簡単な方法はないのです。

ネットのアドバイスに従って、ログをキャッチするなどの工夫をするとよいでしょう。正直なところ、私は、アスペクトが呼び出されたことを本当にテストする必要がある場合にのみ、アスペクトの期待される動作をテストします。ログを取るのであれば、私はそれを行いません。dbに何かを設定する場合(またはその他の副作用)には、その値がdbにあるかどうかを確認します。それが統合テストの杜撰なところです。

もし、本当に、本当に、あなたが望む方法でアスペクトをテストしなければならないのなら、与えられたコードと似たようなものを書くことができます。しかし、通常の(テストではない)実行時のSpringの設定は、Springコンテキストに存在するVerifierインターフェースのダミー実装が必要であることを忘れないでください。

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(Config.class)
public class AspectTesting {

    @Autowired
    ServiceWithAspect service;

    @Autowired
    Verifier verifyingAspect;

    @Test
    public void test() {
        // given
        boolean condition = false;

        // when
        try {
            service.doit();
        } catch (Exception swallow) {}

        // then
        try {
            condition = ((VerifyingAspect) ((Advised) verifyingAspect).getTargetSource().getTarget()).wasExecuted();
        } catch (Exception swallow) {}

        // then
        Assert.assertTrue(condition);
    }
}

@Configuration
@EnableAspectJAutoProxy
@ComponentScan("aspects")
class Config {
}

@Component
class VerifyingAspect implements Verifier {

    private boolean executed = false;

    public boolean wasExecuted() {
        return executed;
    }

    @Override
    public void invoked() {
        executed = true;
    }
}

@Service
class ServiceWithAspect {
    public void doit() {
        throw new RuntimeException();
    }
}

@Component
@Aspect
class TestedAspect {

    @Autowired
    Verifier verifier;

    @AfterThrowing(pointcut = "execution(* *(..))", throwing = "exception")
    public void afterThrowingAdvice(Exception exception) {
        // your aspect logic here
        verifier.invoked();
    }
}

interface Verifier {
    void invoked();
}