【问题标题】:How to test an aspect with SpringBootTest?如何使用 SpringBootTest 测试一个方面?
【发布时间】:2019-11-07 19:47:24
【问题描述】:

我使用 Spring Boot 2.1.6.RELEASE 在 Spring 中创建了一个简单的切面。 它基本上记录了花费在方法上的总时间。

@Aspect
@Component
public class TimeLoggerAspect {

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

  @Around("@annotation(demo.TimeLogger)")
  public Object methodTimeLogger(ProceedingJoinPoint joinPoint) 
          throws Throwable {
    long startTime = System.currentTimeMillis();

    Object proceed = joinPoint.proceed();

    long totalTime = System.currentTimeMillis() - startTime;
    log.info("Method " + joinPoint.getSignature() + ": " + totalTime + "ms");

    return proceed;
  }
}

切面由TimeLogger 注解触发

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface TimeLogger {
}

并且在这样的组件中使用

@Component
public class DemoComponent {
  @TimeLogger
  public void sayHello() {
    System.out.println("hello");
  }
}

Spring Boot 演示应用程序将通过CommandLineRunner 接口的run 方法调用sayHello

@SpringBootApplication
public class DemoApplication implements CommandLineRunner {

  @Autowired
  private DemoComponent demoComponent;

  public static void main(String[] args) {
    SpringApplication.run(DemoApplication.class, args);
  }

  @Override
  public void run(String... args) throws Exception {
    demoComponent.sayHello();
  }
}

为了完整起见,我在build.gradle 中添加了我的修改:为 aop、spring 测试和 jupiter (junit) 添加库。

    compile("org.springframework.boot:spring-boot-starter-aop")

    testCompile("org.springframework.boot:spring-boot-starter-test")
    testCompile("org.junit.jupiter:junit-jupiter-api")
    testRuntime("org.junit.jupiter:junit-jupiter-engine")

运行应用程序将输出(为便于阅读而修剪)

hello
... TimeLoggerAspect : Method void demo.DemoComponent.sayHello(): 4ms

到目前为止,一切都很好。现在我基于@SpringBootTest注解和jupiter创建一个测试。

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;

@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = {DemoComponent.class, TimeLoggerAspect.class})
public class DemoComponentFailTest {

  @Autowired
  private DemoComponent demoComponent;

  @Test
  public void shouldLogMethodTiming() {
      demoComponent.sayHello();
  }
}

在这里我得到了输出

hello

TimeLoggerAspect 没有输出,因为它似乎没有被触发。

是否缺少某些东西来触发测试中的方面?还是有其他方法可以在spring boot中测试方面?

【问题讨论】:

  • 在一些相关的说明中,您是否查看过 Spring PerformanceMonitorInterceptor,它似乎已经完成了您尝试实施的工作?
  • 否则,您是否尝试过使用@SpringBootTest 而不将其限制为特定的类?
  • K.实际情况有点困难,所以我用它作为例子。删除注释上的“类”没有帮助。

标签: java spring-boot aop aspectj


【解决方案1】:

您需要启动一个@SpringBootApplication。但是,它不一定是您在生产中启动应用程序时使用的那个。它可以是仅用于此测试的特殊版本,并且可以在您的测试源根目录中而不是您的源代码中。

@SpringBootApplication
@ComponentScan(basePackageClasses = {DemoComponent.class, TimeLoggerAspect.class})
public class SpringBootTestMain {
    public static void main(String[] args) {
        SpringApplication.run(SpringBootTestMain.class, args);
    }

}

那么在你的测试中,这是你唯一需要列出的类。

@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = SpringBootTestMain.class)
public class DemoComponentFailTest {

【讨论】:

  • 有趣。它有效,但感觉有点不对劲。我什至可以删除@ComponentScan;可能是因为SpringBootTestMain 与其他类在同一个包中。
  • 您可以删除@ComponentScan,但随后您的整个应用程序将启动,因此随着应用程序的增长,它会花费更长的时间。如果存在不相关但不能像数据库或初始加载那样创建的依赖项,那么它将失败。如果您可以启动整个应用程序,只需使用 @SpringBootApplication 并让您的常规应用程序主启动它。我认为您需要它,因为您的测试课上有 @SpringBootTest(classes = {DemoComponent.class, TimeLoggerAspect.class})
【解决方案2】:

另一个似乎可行的解决方案是在classes@SpringBootTest 中添加AnnotationAwareAspectJAutoProxyCreator,尽管我不太确定为什么。

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;

@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = { DemoComponent.class, 
                            TimeLoggerAspect.class,
                            AnnotationAwareAspectJAutoProxyCreator.class })
public class DemoComponentFailTest {

  @Autowired
  private DemoComponent demoComponent;

  @Test
  public void shouldLogMethodTiming() {
      demoComponent.sayHello();
  }
}

【讨论】:

  • 有趣的是,这是唯一对我们有用的方法。
【解决方案3】:

我有类似的问题。我的方面正在监听控制器方法。要激活它,导入 AnnotationAwareAspectJAutoProxyCreator 就可以了:

@RunWith(SpringRunner.class)
@Import(AnnotationAwareAspectJAutoProxyCreator.class) // activate aspect
@WebMvcTest(MyController.class)
public class MyControllerTest {

    ...

}

【讨论】:

  • 正是我需要的!
【解决方案4】:

您必须将@EnableAspectJAutoProxy 与您的文件@Configuration 一起使用@Aspect 声明bean。

@Aspect
@Configuration
@EnableAspectJAutoProxy
public class TimeLoggerAspect {

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

  @Around("@annotation(demo.TimeLogger)")
  public Object methodTimeLogger(ProceedingJoinPoint joinPoint) 
          throws Throwable {
    long startTime = System.currentTimeMillis();

    Object proceed = joinPoint.proceed();

    long totalTime = System.currentTimeMillis() - startTime;
    log.info("Method " + joinPoint.getSignature() + ": " + totalTime + "ms");

    return proceed;
  }
}

我认为这样就可以了。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-01-17
    • 1970-01-01
    • 2019-08-23
    • 2017-05-28
    • 2017-06-22
    • 2019-09-14
    • 1970-01-01
    • 2017-07-24
    相关资源
    最近更新 更多