Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to test an aspect with SpringBootTest?

I created a simple aspect in Spring using Spring Boot 2.1.6.RELEASE. It basically logs the total time spent on a method.

@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;
  }
}

the aspect is triggered by a TimeLogger annotation

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

and is used in a component like this

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

A spring boot demo application will invoke sayHello via the run method of the CommandLineRunner interface.

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

For completeness, I add my modifications in build.gradle: adding libraries for aop, spring test and 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")

Running the application will output (trimmed for readability)

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

So far, so good. Now I create a test based on @SpringBootTest annotation and 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();
  }
}

and here I get the output

hello

No output from the TimeLoggerAspect, since it seems it is not being triggered.

Is something missing to trigger the aspect in the test? Or are there other ways of testing the aspect in spring boot?

like image 422
Natan Cox Avatar asked Jun 25 '19 23:06

Natan Cox


People also ask

What does SpringBootTest annotation do?

@SpringBootTest is a primary annotation to create unit and integration tests in Spring Boot applications. The annotation enables additional features such as custom environment properties, different web environment modes, random ports, TestRestTemplate and WebTestClient beans.

Is SpringBootTest a unit test?

Well, this is not a unit test. When we use the @SpringBootTest annotation, Spring loads up an application context for the test. In practice, we have started the whole application only to autowire the OrderService into the test.

What kind of testing can be done in spring test module?

The core items for the testing are contained in the modules called spring-boot-test and the configuration is provided by the modules called spring-boot-test-autoconfigure. We can simply use the spring-boot-starter-test in pom. xml and transitively pull all the required dependencies in a Spring application.


2 Answers

I had similar problem. My Aspect is listening on controller methods. To get it activated, importing the AnnotationAwareAspectJAutoProxyCreator made the trick:

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

    ...

}
like image 70
Michael Hegner Avatar answered Oct 18 '22 19:10

Michael Hegner


You have to put @EnableAspectJAutoProxy with your file @Configuration that declares the bean with @Aspect.

@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;
  }
}

I think that will do the work.

like image 32
Raphael Costa Avatar answered Oct 18 '22 18:10

Raphael Costa