AspectJ in automated testing - some practical examples

- (), Allure Framework , @Step. , , Spring Guice.





.





- - , . , . . .





, Aspect Advice, .





@Step - . , - @Pointcut , Advice- :





  • Advice, (@Before);





  • , Advice (@After, @AfterReturning);





  • Advice, ( ) (@Around).





, , “” , . , .





Advice - , - - log.





Allure Framework, ( ) , AOP. Allure- - @Step . , , . . , , - .





, - , Allure, . , Lombok, AspectJ Allure . Allure , , . .





Gradle, Maven .





, AspectJ :





compile 'org.aspectj:aspectjrt:1.9.2'
compile ‘org.aspectj:aspectjweaver:1.9.2
      
      



io.qameta.allure:allure-gradle:2.8.1, “aspectjweaver”, .. Allure :





allure {
    version = '2.8.1'
    autoconfigure = true
    allureJavaVersion = '2.13.7'
    aspectjVersion = '1.9.2'
    configuration = 'compile'
}
      
      



, , GitHub.





. , log- , .





, , Google, - ( - - - ).





@Test
    public void allureLogTest() {
        GoogleSteps.openSearchPage();
        GoogleSteps.searchFor("java");
        String resultStats = GoogleSteps.getResultStats();
        MatcherAssert.assertThat(
                "    ",
                resultStats,
                Matchers.equalTo("-")
        );
    }
      
      



, .





(, Selenide, ), , .





@Step, , Allure. .





public class GoogleSteps {
    @Step("    ")
    @Attachment
    public static String getResultStats() {
        return Selenide.$("#result-stats").text();
    }

    @Step("   : {0}")
    public static void searchFor(String request) {
        Selenide.$("[name='q']").setValue(request);
        Selenide.$("[name='btnK']").click();
    }

    @Step("  ")
    public static void openSearchPage() {
        Selenide.open("https://www.google.ru/");
    }
}
      
      



. - , java - @Step.





@Aspect
public class AllureLogAspect {
    private static final Logger log = LoggerFactory.getLogger(AllureLogAspect.class);
}
      
      



pointcut, . , , , io.qameta.allure.Step:





    @Before("@annotation(io.qameta.allure.Step) && execution(* *(..))")
    public void beforeStep(JoinPoint joinPoint) {
        String stepName = getStepName(joinPoint);
        log.info("BEGIN: " + stepName);
    }
      
      



@Before , . (JoinPoint), . :





    private String getStepName(JoinPoint joinPoint) {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Map<String, Object> parametersMap = getParametersMap(joinPoint);
        Method method = methodSignature.getMethod();
        Step step = method.getAnnotation(Step.class);
        String stepName = step.value();

        return Optional.of(stepName)
                .filter(StringUtils::isNoneEmpty)
                .map(it -> processNameTemplate(it, parametersMap))
                .orElse(methodSignature.getName());
    }
      
      



,  





        return Optional.of(stepName)
                .filter(StringUtils::isNoneEmpty)
                .map(it -> processNameTemplate(it, parametersMap))
                .orElse(methodSignature.getName());
      
      







 





return stepName;
      
      



. , .





?





beforeStep, . .





@AfterReturning:





    @AfterReturning(
            pointcut = "@annotation(io.qameta.allure.Step) && execution(* *(..))",
            returning = "result"
    )
    public void afterStep(JoinPoint joinPoint, Object result) {
        String stepName = getStepName(joinPoint);
        log.info("END: " + stepName);
        if(result != null) {
            log.info("RESULT: " + result);
        }
    }
      
      



, - . , .





. /resources/META-INF - aop-ajc.xml, :





<aspectj>
    <aspects>
        <aspect name="org.example.aop.aspects.AllureLogAspect"/>
    </aspects>
</aspectj>
      
      



, , Allure. .





Selenide, listener- . . , HTTP- OpenAPI- Swagger-. AOP Allure. . - SQL- .









, . , 2-3 . , .





checkRandom(), 0 9 , desiredValue:





public class CommonSteps {
    @Step(",    = {0}")
    @WithRetries(20)
    public static void checkRandom(int desiredValue) {
        Random random = new Random();
        int i = random.nextInt(10);
        MatcherAssert.assertThat(
                " ",
                i,
                Matchers.equalTo(desiredValue)
        );
    }
}
      
      



( ).





, @WithRetries. , , 3 :





@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface WithRetries {
    int value() default 3;
}
      
      



:





@Aspect
public class WithRetriesAspect {

   private static final ThreadLocal<Boolean> processingWrapper = ThreadLocal.withInitial(() -> false);
    private static final Logger log = LoggerFactory.getLogger(WithRetriesAspect.class);

    public static Boolean isProcessing() {
        return processingWrapper.get();
    }
...
}
      
      



, pointcut @Around. , @WithRetries.





    @Around("@annotation(org.example.aop.annotations.WithRetries) && execution(* *(..))")
    public Object handleRetries(final ProceedingJoinPoint joinPoint) throws Throwable {
        processingWrapper.set(true);
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        WithRetries annotation = method.getAnnotation(WithRetries.class);
        int retryCount = annotation.value();
        Throwable storedException = null;
        Object result = null;
        boolean processed = false;
        int i = 0;
        while (!processed && i < retryCount) {
            try {
                result = joinPoint.proceed();
                processed = true;
            } catch(Throwable throwable) {
                log.warn(" №" +i+":\r\n"+throwable.toString());
                storedException = throwable;
            }
            i++;
        }
      
      



, . 





Advice . , storedException. , - . - , , .





       processingWrapper.set(false);

        if(!processed) {
            assert storedException != null;
            throw storedException;
        }

        return result;
      
      



/resources/META-INF/aop-ajc.xml





<aspectj>
    <aspects>
        <aspect name="org.example.aop.aspects.AllureLogAspect"/>
        <aspect name="org.example.aop.aspects.WithRetriesAspect"/>
    </aspects>
</aspectj>
      
      



- , , . 





. :





@Aspect
public class MatcherAssertAspect {
    private static final Logger log = LoggerFactory.getLogger(MatcherAssertAspect.class);
}
      
      



pointcut .





pointcut , assertThat:





    @Pointcut("execution(* assertThat(..))")
    void inMethods() {
    }
      
      



pointcut - ,   org.hamcrest.MatcherAssert:





    @Pointcut("execution(* org.hamcrest.MatcherAssert.*(..))")
    void inMatcherAssert() {
    }
      
      



pointcut :





    @Pointcut("execution(public * *(..))")
    void anyPublic() {
    }
      
      



pointcut- assertThat org.hamcrest.MatcherAssert.





Advice @Arround:





    @Around("anyPublic() && inMatcherAssert() && inMethods()")
    public Object handleAssert(final ProceedingJoinPoint joinPoint) throws Throwable {
        Object result = null;
        try {
            result = joinPoint.proceed();
        } catch (Throwable throwable) {
            if (WithRetriesAspect.isProcessing()) {
                throw throwable;
            } else {
                log.warn(throwable.toString());
            }
        }
        return result;
    }
      
      



, .





:





    @Test
    void errorsSkippingTest() {
        MatcherAssert.assertThat("test", true, Matchers.equalTo(false));
        MatcherAssert.assertThat("test2", 1, Matchers.equalTo(2));
        MatcherAssert.assertThat("test3", "olol", Matchers.equalTo("pishpish"));
    }
      
      



/resources/META-INF/aop-ajc.xml





<aspectj>
    <aspects>
        <aspect name="org.example.aop.aspects.AllureLogAspect"/>
        <aspect name="org.example.aop.aspects.MatcherAssertAspect"/>
        <aspect name="org.example.aop.aspects.WithRetriesAspect"/>
    </aspects>
</aspectj>
      
      



- Flaky-. , ( , ). , .





Allure - @Flaky. , , . . , StackTrace , .





, - - , , Kibana, .. , Kibana . , , . AOP.





: .





P.S. . VK, FB, Instagram Telegram-, Maxilect.








All Articles