运行多个测试时,如何将失败的(java)selenium 测试的正确驱动程序传递给 testng ITestListener?

问题描述 投票:0回答:1

我有一个

TestListener
类,它实现了
ITestListener
,它负责在selenium测试失败时拍照并保存html。

我的硒测试有一个父类

BaseTest
,它有助于执行常见任务,例如启动每个驱动程序。

各重要区域的内容:

TestListener

public class TestListener implements ITestListener {
    WebDriver driver=null;
    ITestContext context = null;
    String filePath = "artifacts/";

    @Override
    public void onTestFailure(ITestResult result) {
        ITestContext context = result.getTestContext();
        driver = (WebDriver) context.getAttribute("WebDriver");
        System.out.println("***** Error "+result.getName()+" test has failed *****");
        String methodName=result.getName().toString().trim();
        String currentTime = getCurrentTime();
        saveScreenShot(methodName, currentTime);
        savePageSource(methodName, currentTime);
        saveConsoleLog(methodName, currentTime);
    }
...

BaseTest
(每个测试单独调用此)

    private void testngSetup(ITestContext context){
        context.setAttribute("WebDriver", driver);
        TestRunner runner = (TestRunner) context;
        runner.setOutputDirectory(artifactLocation);
    }

pom.xml

        <plugin>
          <artifactId>maven-surefire-plugin</artifactId>
          <version>3.2.5</version>
          <configuration>
            <suiteXmlFiles>
              <suiteXmlFile>testng.xml</suiteXmlFile>
            </suiteXmlFiles>
            <useSystemClassLoader>false</useSystemClassLoader>
          </configuration>
        </plugin>

testng.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="esm4hpc">
    <listeners>
        <listener class-name="selenium.listeners.TestListener"/>
    </listeners>
    <test name="sample-Test" verbose="2" parallel = "classes">
        <packages>
            <package name=".*"/>
        </packages>
    </test>
</suite>

我找到了这个线程(How to get the current class driver in ItestListener),它建议按照以下方式更改在

ITestListener
中获取驱动程序:
(WebDriver)result.getTestClass().getRealClass().getDeclaredField("driver").get(result.getInstance())
,但它会抛出
java.lang.NoSuchFieldException : driver

public class TestListener implements ITestListener {
    WebDriver driver=null;
    ITestContext context = null;
    String filePath = "artifacts/";

    @Override
    public void onTestFailure(ITestResult result) {
        ITestContext context = result.getTestContext();
        driver = (WebDriver)result.getTestClass().getRealClass().getDeclaredField("driver").get(result.getInstance());
        System.out.println("***** Error "+result.getName()+" test has failed *****");
        String methodName=result.getName().toString().trim();
        String currentTime = getCurrentTime();
        saveScreenShot(methodName, currentTime);
        savePageSource(methodName, currentTime);
        saveConsoleLog(methodName, currentTime);
    }
...

我还尝试将

parallel
中的
testng.xml
标签设置为 false,但似乎没有帮助。 我也尝试过单独列出每个测试类,但它也返回了错误的图片。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="esm4hpc">
    <listeners>
        <listener class-name="selenium.listeners.TestListener"/>
    </listeners>
    <test name="sample-Test" verbose="2" parallel = "false">
        <classes>
            <class name="selenium.esm4hpc.AuthenticationTest"/>
            <class name="selenium.esm4hpc.FileEditorTest"/>
            <class name="selenium.esm4hpc.FileManagerTest"/>
            <class name="selenium.esm4hpc.JobTest"/>
            <class name="selenium.esm4hpc.ProjectTest"/>
        </classes>
    </test>
</suite>

唯一有帮助的似乎是将每个测试类移动到不同的测试用例中:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="esm4hpc">
    <listeners>
        <listener class-name="selenium.listeners.TestListener"/>
    </listeners>
    <test name="AuthenticationTest" verbose="2" parallel = "false">
        <classes>
            <class name="selenium.esm4hpc.AuthenticationTest"/>
        </classes>
    </test>
    <test name="FileEditorTest" verbose="2" parallel = "false">
        <classes>
            <class name="selenium.esm4hpc.FileEditorTest"/>
        </classes>
    </test>
    <test name="FileManagerTest" verbose="2" parallel = "false">
        <classes>
            <class name="selenium.esm4hpc.FileManagerTest"/>
        </classes>
    </test>
    <test name="JobTest" verbose="2" parallel = "false">
        <classes>
            <class name="selenium.esm4hpc.JobTest"/>
        </classes>
    </test>
    <test name="ProjectTest" verbose="2" parallel = "false">
        <classes>
            <class name="selenium.esm4hpc.ProjectTest"/>
        </classes>
    </test>
</suite>

但这会导致结果文件需要单独生成,导致调试失败的情况变得很麻烦。

selenium-webdriver testng gui-testing testng.xml
1个回答
0
投票

让我们从清理一些细节开始。

  • ITestContext
    是 TestNG 指向
    <test>
    标签的 API 方式(来自套件术语)
  • ITestResult
    是 TestNG 的 API 方式,用于指向特定
    @Test
    或配置方法的执行结果。
  • 可以有一种或多种测试方法属于同一个
    ITestContext
    。因此,一个
    ITestContext
    中可以包含一个或多个
    ITestResult
    对象。
  • @Test
    方法中,如果您调用
    org.testng.Reporter.getCurrentTestResult()
    ,您将始终可以访问当前运行的测试方法的结果。
  • ITestResult.setAttribute()
    方法可让您将属性附加到特定测试方法,类似于
    ITestContext.setAttribute()
    允许您在
    <test>
    标签级别附加属性,而
    ISuite.setAttribute()
    <suite>
    标签级别附加属性。

要解决您遇到的问题,您需要执行以下操作:

  • 在您的测试监听器中,更改行
    driver = (WebDriver) context.getAttribute("WebDriver");
    driver = (WebDriver) testResult.getAttribute("WebDriver");
  • 修改负责实例化 Webdriver 的基类方法,并将其设置为当前运行测试的
    ITestResult
    对象,作为使用其
    setAttribute()
    方法的属性。您需要确保通过
    @Test
    方法显式调用设置方法,或者通过其
    run()
    方法隐式调用设置方法(如果您的基类正在实现 TestNG 提供的
    IHookable
    接口)

确保共享 WebDriver 实例以及在测试类和侦听器中访问它时绝对线程安全的最简单方法是执行以下示例中所示的操作。

import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.testng.ITestListener;
import org.testng.ITestResult;
import org.testng.Reporter;

import java.util.Optional;

public class WebDriverLifeCycleManager implements ITestListener {

    public static final String DRIVER = "driver";

    public static RemoteWebDriver driver() {
        return Optional.ofNullable(Reporter.getCurrentTestResult().getAttribute(DRIVER))
                .map(it -> (RemoteWebDriver) it)
                .orElseThrow(() -> new IllegalArgumentException("Could not find driver"));
    }

    @Override
    public void onTestStart(ITestResult result) {
        ChromeDriver driver = new ChromeDriver();
        result.setAttribute(DRIVER, driver);
    }

    @Override
    public void onTestSuccess(ITestResult result) {
        cleanupDriverReference(result);
    }

    @Override
    public void onTestFailure(ITestResult result) {
        //Add logic to capture screenshot etc.,
        cleanupDriverReference(result);
    }

    @Override
    public void onTestFailedButWithinSuccessPercentage(ITestResult result) {
        cleanupDriverReference(result);
    }

    @Override
    public void onTestSkipped(ITestResult result) {
        cleanupDriverReference(result);
    }

    private void cleanupDriverReference(ITestResult result) {
        Optional.ofNullable(result.getAttribute(DRIVER))
                .map(it -> (RemoteWebDriver) it)
                .ifPresent(RemoteWebDriver::quit);
        result.removeAttribute(DRIVER);
    }
}

这是一个显式使用上述侦听器的测试类(您可以将其更改为使用自动连接侦听器的服务加载方法,如文档中所述)

import org.testng.annotations.Listeners;
import org.testng.annotations.Test;

@Listeners(WebDriverLifeCycleManager.class)
public class ThreadSafeWebDriverSample {

    @Test
    public void test() {
        WebDriverLifeCycleManager.driver().get("https://www.google.com");
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.