如何使用 SpringBootTest 测试方面?

Nat*_*Cox 8 java aop aspectj spring-boot

我使用 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;
  }
}
Run Code Online (Sandbox Code Playgroud)

方面由TimeLogger注释触发

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface TimeLogger {
}
Run Code Online (Sandbox Code Playgroud)

并在这样的组件中使用

@Component
public class DemoComponent {
  @TimeLogger
  public void sayHello() {
    System.out.println("hello");
  }
}
Run Code Online (Sandbox Code Playgroud)

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

@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();
  }
}
Run Code Online (Sandbox Code Playgroud)

为了完整起见,我添加了我的修改build.gradle:为 aop、spring test 和 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")
Run Code Online (Sandbox Code Playgroud)

运行应用程序将输出(为了可读性进行了修剪)

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

Run Code Online (Sandbox Code Playgroud)

到现在为止还挺好。现在我创建一个基于@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();
  }
}
Run Code Online (Sandbox Code Playgroud)

在这里我得到了输出

hello
Run Code Online (Sandbox Code Playgroud)

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

是否缺少触发测试中的方面的东西?或者还有其他方法可以测试spring boot中的方面吗?

Mic*_*ner 11

我有类似的问题。我的方面正在监听控制器方法。要激活它,导入AnnotationAwareAspectJAutoProxyCreator使技巧:

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

    ...

}
Run Code Online (Sandbox Code Playgroud)


Rap*_*sta 8

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

@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;
  }
}
Run Code Online (Sandbox Code Playgroud)

我认为这样就可以了。

  • 这不可能是正确的,仅仅因为您更改了实现而不是测试方法。实现很好,只是在测试时如何启动 spring-aop 魔法工作。 (5认同)

DCT*_*TID 5

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

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

}
Run Code Online (Sandbox Code Playgroud)

然后在您的测试中,这是您需要列出的唯一类。

@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = SpringBootTestMain.class)
public class DemoComponentFailTest {
Run Code Online (Sandbox Code Playgroud)


Nat*_*Cox 5

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

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();
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 有趣的是,这是唯一对我们有用的方法。 (2认同)