如何使用第三方maven依赖执行切入点?

时间:2015-12-13 19:14:47

标签: java maven selenium aspectj

我正在尝试开发一个AspectJ方面,该方面将自动吞下Selenium-Java的ElementNotVisibleException实例抛出的任何StaleElementReferenceExceptionRuntimeExceptionWebDriver的子类)(包括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的副本,编辑源代码并重建,因为我直接从公共Maven资源库中提取Selenium。

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

execution()切入点也行不通!这是因为AspectJ正在编织我的类,但是没有编织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;
    }
}

1 个答案:

答案 0 :(得分:1)

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

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

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

Maven POM构建项目:

pom.xml 包含一些额外的

插件
  • 构建一个包含AspectJ运行时和所有其他依赖项的单个可执行JAR( one-jar )。分发和运行已编译的程序非常方便。
  • 我还包含 exec-maven ,以便您通过mvn clean compile 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()绑定找到它)。 Application类还实现了Closeable,这使我们能够在main方法中使用尝试使用资源,确保驱动程序将作为{自动关闭} {1}}实例超出范围。

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

我希望这会有所帮助。享受!