gpt4 book ai didi

java - 如何将执行切入点与第三方maven依赖一起使用?

转载 作者:行者123 更新时间:2023-12-01 10:44:40 25 4
gpt4 key购买 nike

我正在尝试开发一个 AspectJ 方面,它将自动吞下 Selenium 抛出的任何 ElementNotVisibleExceptionStaleElementReferenceException(RuntimeException 的子类) - Java的WebDriver实例(包括WebDriver的多个子类——ChromeDriverFirefoxDriver等)

基本上,在非 AOP 上下文中使用 Selenium 处理 ENVESERE 异常的标准建议解决方案就是重试。然后再次。如果有必要的话,再一次。

像这样的东西可以在函数范式中工作:

public void tryWhileStale(Runnable r)
{
int n = 0;
while(n < 5)
{
try
{
r.run();
break;
}
catch(StaleElementReferenceException | ElementNotVisibleException e){}
n++;
Thread.sleep(2000);
}
throw new RuntimeException("Timed out retrying");
}

然后,稍后使用WebDriver时:

tryWhileStale(() -> driver.findElement(By.xpath(...)).click());

但是,这增加了相当多的额外输入(以及意外忘记 tryWhileStale() 包装器的可能性),这是我想避免的。

我不想下载 selenium-java 的副本、编辑源代码并重建,因为我直接从公共(public) Maven 存储库中提取 Selenium。

我希望 AspectJ 能够弄清楚如何做到这一点,因此我做了一些研究,并意识到我需要一个带有 execution 切入点的 around 建议。如果我使用 call 而不是 execution,它将成功触发,但不会吞掉异常。其原因对我来说很神秘,因为从我编写的方面的代码流来看,它似乎会捕获在proceed()调用中抛出的任何内容。

但是 execution() 切入点也不起作用!这是因为 AspectJ 正在编织 my 类,但不是编织 Selenium-Java,即使我的 pom.xml 中有一个 weaveDependency!更重要的是,call() 仅当您的 类被编织时才起作用,而 execution() 仅当您的类被编织时才起作用。 >调用是编织的。显然,如果您同时编织了您的类和第三方类,那么任何一种都可以工作。

有没有办法在不完全放弃 AOP 或 Maven 的情况下做到这一点?下面是我的代码,据称如果我能够编织 selenium-java,它应该可以工作:

@Aspect
class MyAspect {

@Around("execution (WebElement *.findElement(By))")
public Object around(ProceedingJoinPoint pjp)
{
Object f = null;
int n = 0;
do
{
try
{
System.err.println("Before " + this.toString());
f = pjp.proceed();
System.err.println("After " + this.toString());
return f;
}
catch(Throwable t)
{
try { Thread.sleep(5000); } catch(InterruptedException ie) { break; }
System.err.println("Waiting 5 seconds because of " + t.getClass().getSimpleName());
}
n++;
} while(n < 5);
System.err.println("Gave up waiting");
return null;
}
}

最佳答案

我有点好奇,并设置了一个示例项目,其中包含一个方面拦截 call(WebElement WebDriver+.findElement(*)) (与您的方法类似)以及 call(void WebElement+ .click())。我用了sample page with an inline frame (iframe) from W3schools为了模拟一些 WebDriver 异常,例如 NoSuchElementExceptionStaleElementReferenceException。如果您只需将焦点从主框架切换到 iframe 并尝试访问前者的元素(反之亦然),这很容易。

我的示例方面不会等待 n 秒,而是迭代主 iframe 和所有 iframe,以便在该上下文中重新发出原始调用。根据您的需要调整示例代码应该很容易。

哦顺便说一句,我使用了原生 AspectJ 语法,而不是基于注释的语法。希望您不要介意,我发现原生语法更具表现力和优雅。

构建项目的 Maven POM:

pom.xml包含一些额外的插件

  • 构建一个包含 AspectJ 运行时和所有其他依赖项的单个可执行 JAR(one-jar)。对于分发和运行编译的程序非常方便。
  • 我还包含了exec-maven,以便您能够通过mvn cleancompile exec:java轻松运行您的程序。
<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>de.scrum-master.stackoverflow</groupId>
<artifactId>selenium-aspectj-retry</artifactId>
<version>1.0-SNAPSHOT</version>

<name>Selenium auto-retry via AspectJ</name>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.source-target.version>1.8</java.source-target.version>
<aspectj.version>1.8.7</aspectj.version>
<main-class>de.scrum_master.app.Application</main-class>
</properties>

<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<source>${java.source-target.version}</source>
<target>${java.source-target.version}</target>
<!-- IMPORTANT -->
<useIncrementalCompilation>false</useIncrementalCompilation>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.8</version>
<configuration>
<!--<showWeaveInfo>true</showWeaveInfo> -->
<source>${java.source-target.version}</source>
<target>${java.source-target.version}</target>
<Xlint>ignore</Xlint>
<complianceLevel>${java.source-target.version}</complianceLevel>
<encoding>${project.build.sourceEncoding}</encoding>
<!--<verbose>true</verbose> -->
<!--<warn>constructorName,packageDefaultMethod,deprecation,maskedCatchBlocks,unusedLocals,unusedArguments,unusedImport</warn> -->
</configuration>
<executions>
<execution>
<!-- IMPORTANT -->
<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>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.4.0</version>
<configuration>
<mainClass>${main-class}</mainClass>
</configuration>
</plugin>
<plugin>
<groupId>org.dstovall</groupId>
<artifactId>onejar-maven-plugin</artifactId>
<version>1.4.4</version>
<executions>
<execution>
<goals>
<goal>one-jar</goal>
</goals>
</execution>
</executions>
<configuration>
<onejarVersion>0.96</onejarVersion>
<mainClass>${main-class}</mainClass>
<attachToBuild>true</attachToBuild>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
</plugin>
<!--
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
-->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<configuration>
<mainClass>${main-class}</mainClass>
<cleanupDaemonThreads>false</cleanupDaemonThreads>
</configuration>
</plugin>
<plugin>
<groupId>org.dstovall</groupId>
<artifactId>onejar-maven-plugin</artifactId>
<configuration>
<mainClass>${main-class}</mainClass>
</configuration>
</plugin>
</plugins>
</build>

<pluginRepositories>
<pluginRepository>
<id>OneJAR googlecode.com</id>
<url>http://onejar-maven-plugin.googlecode.com/svn/mavenrepo</url>
</pluginRepository>
</pluginRepositories>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>2.48.2</version>
</dependency>
<dependency>
<groupId>io.github.bonigarcia</groupId>
<artifactId>webdrivermanager</artifactId>
<version>1.3.0</version>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
</dependency>
<dependency>
<groupId>io.github.bonigarcia</groupId>
<artifactId>webdrivermanager</artifactId>
</dependency>
</dependencies>

<organization>
<name>Scrum-Master.de - Agile Project Management</name>
<url>http://scrum-master.de</url>
</organization>

</project>

Java 驱动程序应用程序:

如您所见,应用程序持有仅第二个切入点所需的 WebDriver 引用(第一个切入点不需要它,它可以通过 target() 找到它> 绑定(bind))。 Application 类还实现了 Closeable,这使我们能够在 main 方法中使用 try with resources,确保当 Application 实例超出范围时,驱动程序将自动关闭。

package de.scrum_master.app;

import io.github.bonigarcia.wdm.ChromeDriverManager;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;

import java.io.Closeable;
import java.io.IOException;

public class Application implements Closeable {
private final WebDriver driver;

public Application() {
ChromeDriverManager.getInstance().setup();
driver = new ChromeDriver();
}

@Override
public void close() {
driver.quit();
}

public WebDriver getDriver() {
return driver;
}

public void doSomething() {
driver.get("http://www.w3schools.com/tags/tryit.asp?filename=tryhtml5_input_type_hidden");

// Button in main frame
WebElement button = driver.findElement(By.className("seeResult"));
// Text field in iframe
driver.findElement(By.name("fname"));
// Text area in main frame
driver.findElement(By.id("textareaCode"));
// Hidden input field in main frame
driver.findElement(By.name("bt"));
// Hidden input field in iframe
WebElement hiddenCountryField = driver.findElement(By.name("country"));

// Click button in main frame. This *refreshes* the iframe, making all existing
// references to elements therein (e.g. 'hiddenCountryField') stale
button.click();

// Get value of hidden input field after iframe refresh
System.out.println(driver.findElement(By.name("country")).getAttribute("value"));

// This alternative would *not* work because the aspect cannot repair a reference
// to an element which is gone forever because the iframe was refreshed
// System.out.println(hiddenCountryField.getAttribute("value"));

// Click submit button in iframe (triggers both advices)
driver.findElement(By.cssSelector("input[type=submit]")).click();
}

public static void main(String[] args) {
try (Application application = new Application()) {
application.doSomething();
}
}
}

方面:

package de.scrum_master.aspect;

import de.scrum_master.app.Application;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;

public aspect IFrameSwitcher {
WebElement around(WebDriver driver, By by) :
!within(IFrameSwitcher) &&
call(WebElement WebDriver+.findElement(*)) &&
target(driver) &&
args(by)
{
System.out.println(thisJoinPoint + " -> " + by);
WebElement webElement;
try {
System.out.print(" Trying main frame -> ");
driver.switchTo().defaultContent();
webElement = proceed(driver, by);
System.out.println("OK");
return webElement;
}
catch (RuntimeException e) {
System.out.println(e.getClass().getSimpleName());
for (WebElement iframe : driver.findElements(By.tagName("iframe"))) {
try {
System.out.print(" Trying iframe " + iframe.getAttribute("id") + " -> ");
driver.switchTo().frame(driver.findElement(By.id("iframeResult")));
webElement = proceed(driver, by);
System.out.println("OK");
return webElement;
}
catch (RuntimeException e2) {
System.out.println(e2.getClass().getSimpleName());
e = e2;
}
}
throw e;
}
}

void around(Application application, WebElement webElement) :
within(Application) &&
call(void WebElement+.click()) &&
this(application) &&
target(webElement)
{
System.out.println(thisJoinPoint + " -> " + webElement);
WebDriver driver = application.getDriver();
try {
System.out.print(" Trying main frame -> ");
driver.switchTo().defaultContent();
proceed(application, webElement);
System.out.println("OK");
}
catch (RuntimeException e) {
System.out.println(e.getClass().getSimpleName());
for (WebElement iframe : driver.findElements(By.tagName("iframe"))) {
try {
System.out.print(" Trying iframe " + iframe.getAttribute("id") + " -> ");
driver.switchTo().frame(driver.findElement(By.id("iframeResult")));
proceed(application, webElement);
System.out.println("OK");
return;
}
catch (RuntimeException e2) {
System.out.println(e2.getClass().getSimpleName());
e = e2;
}
}
throw e;
}
}
}

控制台日志:

在这里您可以看到两个切入点中的哪一个何时触发,尝试失败后如何重新启动等等。

call(WebElement org.openqa.selenium.WebDriver.findElement(By)) -> By.className: seeResult
Trying main frame -> OK
call(WebElement org.openqa.selenium.WebDriver.findElement(By)) -> By.name: fname
Trying main frame -> NoSuchElementException
Trying iframe google_ads_iframe_/16833175/TryitLeaderboard_0 -> OK
call(WebElement org.openqa.selenium.WebDriver.findElement(By)) -> By.id: textareaCode
Trying main frame -> OK
call(WebElement org.openqa.selenium.WebDriver.findElement(By)) -> By.name: bt
Trying main frame -> OK
call(WebElement org.openqa.selenium.WebDriver.findElement(By)) -> By.name: country
Trying main frame -> NoSuchElementException
Trying iframe google_ads_iframe_/16833175/TryitLeaderboard_0 -> OK
call(void org.openqa.selenium.WebElement.click()) -> [[ChromeDriver: chrome on XP (5ab9e5f25d169bbc941ab1b08b346c50)] -> class name: seeResult]
Trying main frame -> OK
call(WebElement org.openqa.selenium.WebDriver.findElement(By)) -> By.name: country
Trying main frame -> NoSuchElementException
Trying iframe google_ads_iframe_/16833175/TryitLeaderboard_0 -> OK
Norway
call(WebElement org.openqa.selenium.WebDriver.findElement(By)) -> By.cssSelector: input[type=submit]
Trying main frame -> NoSuchElementException
Trying iframe google_ads_iframe_/16833175/TryitLeaderboard_0 -> OK
call(void org.openqa.selenium.WebElement.click()) -> [[ChromeDriver: chrome on XP (5ab9e5f25d169bbc941ab1b08b346c50)] -> css selector: input[type=submit]]
Trying main frame -> StaleElementReferenceException
Trying iframe google_ads_iframe_/16833175/TryitLeaderboard_0 -> OK

我希望这有帮助。享受吧!

关于java - 如何将执行切入点与第三方maven依赖一起使用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34255171/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com