如何在只有一个 Web 驱动程序实例的 testng 套件中 运行 多次测试 类?

How to run multiple test classes in testng suite with only one web driver instance?

我正在尝试使用@beforeSuite 和@AfterSuite 运行 我在一个浏览器实例中的测试。然而它 运行 第一个第一个测试 class 但在第二个 class.

时失败并出现空指针异常

下面是我的代码:

LaunchBrowser.java class

public class LaunchBrower {

    protected WebDriver driver;

    public WebDriver getDriver() {
        return driver;
    }

    @Parameters({ "browserType", "appURL" })
    @BeforeSuite
    public void setUp(@Optional String browsertype, @Optional String appURL) {
        System.out.println("Launching google chrome with new profile..");
        driver = getBrowserType(browsertype);
        driver.manage().timeouts().implicitlyWait(60, TimeUnit.SECONDS);
        driver.manage().window().maximize();
        driver.navigate().to(appURL);
    }

    private WebDriver getBrowserType(String browserType) {
        if (driver == null ) {
            if (browserType.equalsIgnoreCase("chrome")) {
                return  new ChromeDriver();
            }
            else if (browserType.equalsIgnoreCase("InternetExplorer")) {
                return new InternetExplorerDriver();
            }
        }
        return driver;
    }


    @AfterSuite
    public void tearDown() {
        if (driver != null)
            driver.quit();
    }   

}

LoginPageTest class

public class LoginPageTest extends LaunchBrower {

    protected WebDriver driver;
    private LoginPage loginpage;
    private MyProfile profilepage;

    Logger log = Logger.getLogger("Results:");

    @BeforeClass(alwaysRun = true)
    public void setUp() {       
        loginpage = PageFactory.initElements(getDriver(), LoginPage.class);
        //loginpage =  new LoginPage(driver);
        //driver=getDriver();
    }

    @Test(groups = "LoginPage")
    public void verifyLogin() throws InterruptedException {
        //LoginPage login =  new LoginPage(driver);
        System.out.println("Sign In functionality details...");
        //System.out.println("Sign In functionality details seee...");
        Thread.sleep(10000);
        //login.enterUserName("11111111");
        Assert.assertEquals("11111111",loginpage.enterUserName("11111111"));
        log.debug("Correct Username Pass");
        //System.out.println("Correct username...");
        Assert.assertEquals("fe9245db",loginpage.enterPassword("fe9245db"));
        log.debug("Correct Password Pass");
        loginpage.clickOnLogin();
        log.debug("Login Pass");
    }
}

MyProfileTestjavaclass

public class MyProfileTest extends LaunchBrower {

    protected WebDriver driver;
    private MyProfile profilepage;

    @BeforeClass(alwaysRun = true)
    public void setUp() {
        profilepage = PageFactory.initElements(getDriver(), MyProfile.class);
        //driver=getDriver();
    }

    @Test(groups = "Myprofile")
    public void verifyMyprofile() throws InterruptedException {
        System.out.println("My profile test...");
        //MyProfile profile = new MyProfile(driver);
        profilepage.ClickToggleLink();
        profilepage.ClickMyProfile();
    }
}

问题出在你的测试代码上。 @BeforeSuite 在 TestNG 中设计为每个 <suite> 只调用一次。您将该逻辑与继承结合起来,然后依靠 @BeforeSuite 方法来初始化您的 WebDriver。所以它将为你的 <suite> 中的第一个 class 从第二个 class 开始执行 TestNG 不会调用 @BeforeSuite 因此你的第二个 class 之后你开始看到 NullPointerException。 您应该考虑依赖 ISuiteListener 实现作为侦听器,然后将其连接到您的测试执行中。

您的测试现在将开始依赖以这种方式创建的网络驱动程序,然后使用它。

请确保您使用的是 TestNG 6.12 或更高版本(此时不存在)。

这是一个完整的例子,展示了所有这些的实际效果。

我的测试 class 的基础 class 如下所示

package com.rationaleemotions.Whosebug.qn46323434;

import org.openqa.selenium.remote.RemoteWebDriver;
import org.testng.annotations.Listeners;

@Listeners(BrowserSpawner.class)
public class MyBaseClass {
    protected void launchPage(String url) {
        RemoteWebDriver driver = BrowserSpawner.getDriver();
        driver.get(url);
        System.err.println("Page Title :" + driver.getTitle());
    }
}

我在此示例中使用的测试 classes 如下所示

package com.rationaleemotions.Whosebug.qn46323434;

import org.testng.annotations.Test;

public class MyFirstTestCase extends MyBaseClass {

    @Test
    public void testGooglePage() {
        launchPage("http://www.google.com");
    }

    @Test
    public void testFaceBookPage() {
        launchPage("http://www.facebook.com");
    }
}

package com.rationaleemotions.Whosebug.qn46323434;

import org.testng.annotations.Test;

public class MySecondTestCase extends MyBaseClass {

    @Test
    public void testHerokkuPage() {
        launchPage("https://the-internet.herokuapp.com/");
    }

    @Test
    public void testWhosebugPage() {
        launchPage("http://whosebug.com/");
    }
}

ISuiteListener 实现如下所示

package com.rationaleemotions.Whosebug.qn46323434;

import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.testng.ISuite;
import org.testng.ISuiteListener;
import org.testng.ITestResult;
import org.testng.Reporter;

public class BrowserSpawner implements ISuiteListener {
    private static final String DRIVER = "driver";

    @Override
    public void onStart(ISuite suite) {
        RemoteWebDriver driver;
        String browserType = suite.getParameter("browserType");
        switch (browserType) {
            case "chrome":
                driver = new ChromeDriver();
                break;
            default:
                driver = new FirefoxDriver();
        }
        suite.setAttribute(DRIVER, driver);
    }

    @Override
    public void onFinish(ISuite suite) {
        Object driver = suite.getAttribute(DRIVER);
        if (driver == null) {
            return;
        }
        if (!(driver instanceof RemoteWebDriver)) {
            throw new IllegalStateException("Corrupted WebDriver.");
        }
        ((RemoteWebDriver) driver).quit();
        suite.setAttribute(DRIVER, null);
    }

    public static RemoteWebDriver getDriver() {
        ITestResult result = Reporter.getCurrentTestResult();
        if (result == null) {
            throw new UnsupportedOperationException("Please invoke only from within an @Test method");
        }
        Object driver = result.getTestContext().getSuite().getAttribute(DRIVER);
        if (driver == null) {
            throw new IllegalStateException("Unable to find a valid webdriver instance");
        }
        if (! (driver instanceof RemoteWebDriver)) {
            throw new IllegalStateException("Corrupted WebDriver.");
        }
        return (RemoteWebDriver) driver;
    }
}

我正在使用的套件 xml 文件如下所示

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="46323434_Suite" parallel="false" verbose="2">
    <parameter name="browserType" value="chrome"/>
    <test name="46323434_test1">
        <classes>
            <class name="com.rationaleemotions.Whosebug.qn46323434.MyFirstTestCase"/>
        </classes>
    </test>
    <test name="46323434_test2">
        <classes>
            <class name="com.rationaleemotions.Whosebug.qn46323434.MySecondTestCase"/>
        </classes>
    </test>
</suite>

当你运行这个输出看起来像下面

...
... TestNG 6.12 by Cédric Beust (cedric@beust.com)
...
Starting ChromeDriver 2.32.498537 (cb2f855cbc7b82e20387eaf9a43f6b99b6105061) on port 45973
Only local connections are allowed.
log4j:WARN No appenders could be found for logger (org.apache.http.client.protocol.RequestAddCookies).
log4j:WARN Please initialize the log4j system properly.
Sep 20, 2017 10:36:41 PM org.openqa.selenium.remote.ProtocolHandshake createSession
INFO: Detected dialect: OSS
Page Title :Facebook – log in or sign up
Page Title :Google
PASSED: testFaceBookPage
PASSED: testGooglePage

===============================================
    46323434_test1
    Tests run: 2, Failures: 0, Skips: 0
===============================================
Page Title :The Internet
Page Title :Stack Overflow - Where Developers Learn, Share, & Build Careers
PASSED: testHerokkuPage
PASSED: testWhosebugPage

===============================================
    46323434_test2
    Tests run: 2, Failures: 0, Skips: 0
===============================================

===============================================
46323434_Suite
Total tests run: 4, Failures: 0, Skips: 0
===============================================

这里要记住的最重要的事情是:

  • 您的测试将不再启动和清理浏览器。这现在将由您的 ISuiteListener 实施来完成。
  • 由于正在使用的模型(即每个 <suite> 依赖一个浏览器实例),您将永远无法 运行 并行测试。您的所有测试都应严格按顺序 运行。
  • 您需要确保 none 的 @Test 方法要么使浏览器崩溃(要么)导致浏览器实例被清理(这适用于处理网格设置时其中你可能最终让浏览器闲置一段时间导致节点从服务器端清理浏览器)。如果您造成其中任何一个,那么您的测试将失败并产生意想不到的结果。