I'm trying to combine all these tools together. Here are the details and some screenshots of my project. I need post-compile time weave my source code.
Annotation:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Click {}
Aspect:
@Aspect
public class ClickAspect {
@Pointcut("@annotation(com.selenium.aspect.support.annotation.Click)")
public void clickAnn() {}
@Around("clickAnn()")
public void clickOnAction(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("Clicking on a button");
joinPoint.proceed();
}
}
Page:
public class Page {
@FindBy(id = "menu-item-40489")
private WebElement search;
public Page(WebDriver driver) {
PageFactory.initElements(driver, this);
}
@Click
public void clickOnSearch() {
search.click();
}
}
Test class:
public class SeleniumAspectSupportTest {
private WebDriver driver;
private Page page;
@BeforeTest
public void initDriver() {
System.setProperty("webdriver.chrome.driver", "src/main/resources/chromedriver.exe");
driver = new ChromeDriver();
driver.manage().window().maximize();
driver.manage().timeouts().pageLoadTimeout(40, TimeUnit.SECONDS);
driver.manage().timeouts().implicitlyWait(120, TimeUnit.SECONDS);
page = new Page(driver);
}
@AfterTest
public void dropDriver() {
driver.close();
}
@Test
public void simpleTest() {
driver.get("https://www.baeldung.com");
page.clickOnSearch();
System.out.println("DONE");
}
}
pom.xml:
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.2</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.2</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>1.9.2</version>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>7.4.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>3.141.59</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>11</source>
<target>11</target>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.11</version>
<configuration>
<complianceLevel>11</complianceLevel>
<source>11</source>
<target>11</target>
</configuration>
<executions>
<execution>
<phase>process-sources</phase>
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
aop.xml:
<?xml version="1.0" encoding="UTF-8"?>
<aspectj>
<aspects>
<aspect name="com.selenium.aspect.support.controller.ClickAspect"/>
</aspects>
</aspectj>
The advise is not working, however the test passes. I've tried different aspectj library versions. What have I missed to make it work?
CodePudding user response:
There are several things which could be improved in your POM, aspect and test code. But the root cause of your problem is that the aspect is first woven into the test class by AspectJ Maven Plugin, but then overwritten by Maven Compiler Plugin due to a rather cumbersome Maven Compiler default configuration, causing it to recompile everything. Therefore, you need to add this to your Maven Compiler configuration:
<!-- IMPORTANT -->
<useIncrementalCompilation>false</useIncrementalCompilation>
I You can find an explanation in my answer here.
Other things you should improve:
- Upgrade to a more recent AspectJ Maven version supporting Java 11
- Upgrade AspectJ dependencis to 1.9.7
- Get rid of the
aspectjweaver
dependency andaop.xml
, because you already use compile-time weaving and do not need a load-time weaving configuration on top of that. Either this or that, bot not both. - Make
aspectjtools
a Maven Compiler dependency rather than a dependency for the Maven module itself. You only need the compiler inside the plugin, not during runtime. There,aspectjrt
is enough. - Refine your pointcut to only include
execution()
joinpoints, because otherwise each aspect advice will be triggered twice, once forexecution
and once forcall
. You can see this on the console when running the test. - Activate
<showWeaveInfo>true</showWeaveInfo>
for AspectJ Maven. Then you can also see the previous problem, i.e. doubly woven advice, more clearly during compile time. - Enhance the test to also click away the cookie banner, which was necessary in my case in order to even be able to click the search button during the test. It also adds a second test case for
@Click
triggering the aspect. - In my case I had to quit the driver, not just close it. Otherwise, I would end up with dozens of
chromedriver.exe
background processes.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>SO_AJ_TestNGSelenium_69744638</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<aspectj.version>1.9.7</aspectj.version>
</properties>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>7.4.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>3.141.59</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>11</source>
<target>11</target>
<!-- IMPORTANT -->
<useIncrementalCompilation>false</useIncrementalCompilation>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.14.0</version>
<configuration>
<complianceLevel>11</complianceLevel>
<source>11</source>
<target>11</target>
<showWeaveInfo>true</showWeaveInfo>
</configuration>
<executions>
<execution>
<phase>process-sources</phase>
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>${aspectj.version}</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project>
package com.selenium.aspect.support.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Click {}
package org.example;
import com.selenium.aspect.support.annotation.Click;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;
public class Page {
@FindBy(className = "css-47sehv")
private WebElement cookiesAgree;
@FindBy(id = "menu-item-40489")
private WebElement search;
public Page(WebDriver driver) {
PageFactory.initElements(driver, this);
}
@Click
public void clickOnCookiesAgree() {
cookiesAgree.click();
}
@Click
public void clickOnSearch() {
search.click();
}
}
package org.example;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class ClickAspect {
@Pointcut("@annotation(com.selenium.aspect.support.annotation.Click) && execution(* *(..))")
public void clickAnn() {}
@Around("clickAnn()")
public void clickOnAction(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println(joinPoint " -> Clicking on a button");
joinPoint.proceed();
}
}
package org.example;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
import java.util.concurrent.TimeUnit;
public class SeleniumAspectSupportTest {
private WebDriver driver;
private Page page;
@BeforeTest
public void initDriver() {
System.setProperty("webdriver.chrome.driver", "src/main/resources/chromedriver.exe");
driver = new ChromeDriver();
driver.manage().window().maximize();
driver.manage().timeouts().pageLoadTimeout(40, TimeUnit.SECONDS);
driver.manage().timeouts().implicitlyWait(120, TimeUnit.SECONDS);
page = new Page(driver);
}
@AfterTest
public void dropDriver() {
driver.close();
driver.quit();
}
@Test
public void simpleTest() {
driver.get("https://www.baeldung.com");
page.clickOnCookiesAgree();
page.clickOnSearch();
System.out.println("DONE");
}
}
The console log should read:
execution(void org.example.Page.clickOnCookiesAgree()) -> Clicking on a button
execution(void org.example.Page.clickOnSearch()) -> Clicking on a button