简体   繁体   English

将 Selenium 与 TestNg 一起使用,TestListenerAdapter 使测试混合在一起,驱动程序似乎在测试类之间共享

[英]Using Selenium with TestNg, TestListenerAdapter is getting tests mixed, the driver seems to be getting shared between test classes

I am running parallel tests using selenium (selenium-server-standalone-2.47.1.jar) grid with TestNg, kicked off by ant, and using a TestListenerAdapter.我正在使用带有 TestNg 的 selenium (selenium-server-standalone-2.47.1.jar) 网格运行并行测试,由 ant 启动,并使用 TestListenerAdapter。 Screenshots are taken in the listener's 'onTestFailure' method.在监听器的“onTestFailure”方法中截取屏幕截图。 The problem is that the listener seems to get crossed up about which driver it should be using, and sometimes takes a screenshot of the wrong browser window, or fails altogether if the driver that it thinks it should be using has already quit.问题是侦听器似乎对它应该使用哪个驱动程序感到困惑,有时会截取错误的浏览器窗口的屏幕截图,或者如果它认为应该使用的驱动程序已经退出,则完全失败。

When the tests start, TestNg's @BeforeTest and the TestListenerAdapter's 'onTestStart' methods are running on the same thread, but when the test fails, the TestListenerAdapter's 'onTestFailure' method appears to be running on a separate thread.测试开始时,TestNg 的 @BeforeTest 和 TestListenerAdapter 的 'onTestStart' 方法在同一个线程上运行,但是当测试失败时,TestListenerAdapter 的 'onTestFailure' 方法似乎在单独的线程上运行。 It seems like the threads are getting crossed/shared somehow, but I can't figure out why.似乎线程正在以某种方式交叉/共享,但我不知道为什么。

Here is some skeleton code, any assistance greatly appreciated.这是一些骨架代码,非常感谢任何帮助。

Base Test Class:基础测试类:

public class baseClassTests{

    protected AutomationUtils au;
    protected DriverUtils du;

    @BeforeTest(alwaysRun = true)
    @Parameters({ "selenium.OS", "selenium.browser" })
    public void beforeTest(String OS, String browser) {

        //these call simple private methods to know where to set up the driver
        String port = getPort(OS, browser);
        String host = getHost(OS);

        //make a driver utility object here, this makes a driver
        du = new DriverUtils(browser, host, port);

        //pass this driver utility object to another class of utilities
        //this 'AutomationUtils' class gets a RemoteWebDriver ('driver') by calling driver=du.getDriver();
        //the 'AutomationUtils' class is then the one that does all of the 'driver.findBy...' etc
        au = new AutomationUtils(du);
    }


    @BeforeMethod(alwaysRun = true)
    public void beforeMethod(Method m, ITestResult tr) {
        du.deleteCookies();
        testNgTestName = m.getName();
        print("Method: "+testNgTestName + "   Thread: "+Thread.currentThread().hashCode());
        //set the attribute of the ITestResult object so we can use the same object in the listener
        tr.setAttribute("du", du);
        tr.setAttribute("au", au);
    }

}

Listener class监听类

public class AmSimpleTestListener extends TestListenerAdapter {

    private DriverUtils driveU;
    private AutomationUtils AutoU;
    private RemoteWebDriver driver;
    private RemoteWebDriver augmentedDriver;
    private String methodName;
    private String browser;
    private String browserVersion;
    String testClass;




    @Override
    public void onTestStart(ITestResult tr) {
        //pick up the correct driver utility object from the test class/method we are in
        driveU = (DriverUtils) tr.getAttribute("du");
        AutoU = (AutomationUtils) tr.getAttribute("au");
        driver = du.getDriver();
        augmentedDriver = (RemoteWebDriver) new Augmenter().augment(driver);
        methodName = tr.getName();
        testClass=tr.getTestClass();  //sort of, I actually parse it up a bit
        browser = driveU.getBrowser();
        browserVersion = driveU.getBrowserVersion();
        print("Method: "+methodName + "   Thread: "+Thread.currentThread().hashCode());
    }

    @Override
    public void onTestFailure(ITestResult tr) {
       print("Method: "+tr.getName() + "   Thread: "+Thread.currentThread().hashCode());
        try{
            writeScreenshotFile();
        }
        catch (Exception e){
            Out.error("Unable to take screen shot");
            e.printStackTrace();
        }
    }


    private String writeScreenshotFile() {
        if (driver != null && driver.getSessionId() != null) {
            File scrShot = ((TakesScreenshot) augmentedDriver).getScreenshotAs(OutputType.FILE);
            File localPathToScreenShot = new File("/path/to/base/directory/"+testClass+"/"+methodName+".png");
            try {
                FileUtils.copyFile(scrShot, localPathToScreenShot);
            } catch (Exception e) {
                Out.error("Couldn't write screenshot to file");
            }
            return localPathToScreenShot.getAbsolutePath();
        }
        return "Could not get path.";
    }

}

DriverUtils class makes/supplies the driver DriverUtils 类制作/提供驱动程序

public class DriverUtils {

    private RemoteWebDriver driver;
    private int timeout;
    private String browserVersion;
    private String browser
    private DesiredCapabilities caps;

    public DriverUtils(String browser, String host, String port) {
        String hostUrl = "http://" + host + ":" + port + "/wd/hub";
        this.browser=browser;
        //do some stuff here to set capabilties
        driver = new RemoteWebDriver(new URL(hostUrl), caps);
        browserVersion = driver.getCapabilities().getVersion();
    }

    public RemoteWebDriver getDriver() {
        return driver;
    }

    public AmBrowser getBrowser() {
        return browser;
    }

    public String getBrowserVersion() {
        return browserVersion;
    }


    public void quitDriver() {
        driver.quit();
    }

    public void deleteCookies(){
        driver.manage().deleteAllCookies();
    }


}


public class AutomationUtils extends BaseClassUtils {

    public AutomationUtils(DriverUtils driverUtils) {
        //pass it up to the base class utils (this is different than base class tests, above)
        //do this so the driver can be accessed by other utility classes as well
        super(driverUtils);
    }

    //All sorts of methods here to find elements, login, blah blah everything that is done with a driver object
}


public class BaseClassUtils { //this is a different class than BaseClassTests

    //make the driver a protected object so all utility classes can access as nec.
    protected final RemoteWebDriver driver;

    public BaseClassUtils(DriverUtils driverUtils) {
         driver = driverUtils.getDriver();
    }

}

Tests are run via ant.测试通过 ant 运行。

<suite name="Dev2 for debugging" parallel="tests" thread-count="10">-- tests here </suite>

After tinkering this for a while, I came to the conclusion that there were two things that seemed to help tremendously.在对此进行了一段时间的修补后,我得出的结论是,有两件事似乎对我有很大帮助。

  1. eliminate the listener, and take all screenshots in the @AfterMethod消除监听器,并在@AfterMethod中截取所有截图
  2. Move the @Before/After Method/Test methods into the child classes, but simply call methods in the parent to do all the work.@Before/After Method/Test methods移动到子类中,但只需调用父类中的方法即可完成所有工作。

Another thing I noticed is that for #2, TestNG is supposed to run the parent @Before methods then the child @Before methods;我注意到的另一件事是,对于#2,TestNG 应该运行父@Before方法,然后运行子@Before方法; and then at the end run the child '@After' methods and then the parent @After methods.然后最后运行子 '@After' 方法,然后运行父@After方法。
I ran a series of simple tests, I found that all before/after methods were not being run, so for the few cases where I was using @Before and @After methods in both parent and child, I consolidated.我运行了一系列简单的测试,我发现所有的 before/after 方法都没有运行,所以对于我在父子节点中使用@Before@After方法的少数情况,我合并了。

Things seem to run much better now, the driver does not get confused, and screenshots are being attached to the correct browser/test.现在事情似乎运行得更好了,驱动程序不会感到困惑,并且屏幕截图正在附加到正确的浏览器/测试中。

Try using a ThreadLocal for RemoteWebDriver so it can handle parallel runs:尝试为 RemoteWebDriver 使用 ThreadLocal,以便它可以处理并行运行:

public class DriverUtils {
    private static ThreadLocal<RemoteWebDriver> driver = new ThreadLocal<~>();
    private int timeout;
    private String browserVersion;
    private String browser
    private DesiredCapabilities caps;

    public DriverUtils(String browser, String host, String port) {
        String hostUrl = "http://" + host + ":" + port + "/wd/hub";
        this.browser=browser;
        //do some stuff here to set capabilties
        driver.set(new RemoteWebDriver(new URL(hostUrl), caps));
        browserVersion = getDriver().getCapabilities().getVersion();
    }

    public RemoteWebDriver getDriver() {
        return driver.get();
    }

    public AmBrowser getBrowser() {
        return browser;
    }

    public String getBrowserVersion() {
        return browserVersion;
    }


    public void quitDriver() {
        getDriver().quit();
    }

    public void deleteCookies(){
        getDriver().manage().deleteAllCookies();
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM