Search code examples
javaspringunit-testingjunitspring-aop

how to unit test Aspect annotation


I have defined an Aspect and it will be used when the method is annotated. Please see the sample code below

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

}

@Aspect
@Component
public class PredefinedAspect {
    
    @Before("@annotation(PredefinedCheck)")
    @SneakyThrows
    public void check(JoinPoint joinPoint) {

        ......
        log.debug("hello!!");
    }
}

@Service
public class ActionService {
     
     @PredefinedCheck
     public MyEntity updateMyEntity(AuthenticationJwtToken authToken, EntityUpdateRequest request) {
          ......
     }
}

Now, the question is how can I unit test my PredefinedAspect code? I thought unit testing the updateMyEntity method will trigger it, but it didn't (I debugged and it not hit the break point. Also, the sonarqube doesn't shows the code being covered). Please advise.


Solution

  • Steps

    1. Create under src/test/ root project, SpringBootApplication class, that will start the context(loading/registering the AOP) for test purpose.
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.annotation.ComponentScan;
    
    @SpringBootApplication
    @ComponentScan(basePackageClasses = { PredefinedAspect.class } )
    public class ApplicationTest {
        public void main(String args[]) {
            SpringBootApplication.run(ApplicationTest.class, args);
        }
    }
    
    1. Enable your test with SprintBootTest annotation, with the Application context above.
    @SpringBootTest(classes = ApplicationTest.class)
    
    1. If your project enables some configuration, such Hibernate / Database and you aren't interested test it, exclude with EnableAutoConfiguration annotation. For example:
    @EnableAutoConfiguration(exclude = {
            DataSourceAutoConfiguration.class,
            DataSourceTransactionManagerAutoConfiguration.class,
            HibernateJpaAutoConfiguration.class,
            JpaRepositoriesAutoConfiguration.class
    })
    

    Full example:

    import org.aspectj.lang.JoinPoint;
    import org.junit.jupiter.api.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
    import org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration;
    import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
    import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
    import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.boot.test.mock.mockito.SpyBean;
    import org.springframework.stereotype.Service;
    import org.springframework.test.context.junit4.SpringRunner;
    
    import javax.sql.DataSource;
    
    import static org.mockito.ArgumentMatchers.any;
    import static org.mockito.Mockito.times;
    import static org.mockito.Mockito.verify;
    
    
    @Service
    class ActionServiceFake {
        @PredefinedCheck
        public MyEntity updateMyEntity(AuthenticationJwtToken authToken, EntityUpdateRequest request) {
        }
    }
    
    
    @RunWith(SpringRunner.class)
    @SpringBootTest(classes = ApplicationTest.class)
    @EnableAutoConfiguration(exclude = {
            DataSourceAutoConfiguration.class,
            DataSourceTransactionManagerAutoConfiguration.class,
            HibernateJpaAutoConfiguration.class,
            JpaRepositoriesAutoConfiguration.class
    })
    public class PredefinedAspectTest {
        @SpyBean
        PredefinedAspect predefinedAspect;
    
        @Autowired
        ActionServiceFake actionServiceFake;
    
        @Test
        public void shouldCheckOneTimesBefore() {
            // exercise ...
            actionServiceFake.updateMyEntity(new AuthenticationJwtToken(), new EntityUpdateRequest());
    
            // expectation ...
            verify(predefinedAspect, times(1)).check(any(JoinPoint.class));
        }
    }
    

    You can use the external ActionService class, but, don't forget add it Application Context base classes list.