将 Selenium 测试连接到 SauceLabs

Connect Selenium test to SauceLabs

我创建了一个通用的网络驱动程序 class,我可以将其用于我的 selenium 测试。每当我创建一个新测试时,我都会从这个 class 中获取 webdriver。

这是我用来创建我的驱动程序的东西

package com.atmn.config;

import java.net.MalformedURLException;
import java.net.URL;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.remote.CapabilityType;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.remote.RemoteWebDriver;

public class Driver {


    private static Log log = LogFactory.getLog(Driver.class);
    public static String USERNAME = "name";
    public static String ACCESS_KEY = "key";

    public static WebDriver driver = createDriver("firefox", "47.0", "Windows 10");

    public static WebDriver createDriver(String browser, String version, String os) {

        try{
            DesiredCapabilities capabilities = new DesiredCapabilities();
            capabilities.setCapability(CapabilityType.BROWSER_NAME, browser);
            capabilities.setCapability(CapabilityType.VERSION, version);
            capabilities.setCapability(CapabilityType.PLATFORM, os);

            // Launch remote browser and set it as the current thread
            return new RemoteWebDriver(
                    new URL("http://"+USERNAME+":"+ACCESS_KEY+"@ondemand.saucelabs.com:80/wd/hub"),
                    capabilities);
        }
        catch(MalformedURLException e)
        {
            e.printStackTrace();
            //log message
            log.info(e.getMessage());
        }
        return null;
    }

这是我的测试,它将 运行 在 SauceLabs 上使用 Windows 10 和 firefox 47。

package com.atmn.tests;

import com.tplus.atmn.tasks.CommonTasks;
import com.tplus.atmn.objects.TOA6;
import java.lang.reflect.Method;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openqa.selenium.By;
import org.testng.Assert;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import com.tplus.atmn.tasks.HttpRequest;


public class Testing extends CommonTasks {

    private static Log log = LogFactory.getLog(TB6_TOA.class);


    @Test (groups = {"Testing"})
    public static void endToEnd() throws Exception {


        //Load URL
        HttpRequest http = new HttpRequest();
        String URL = http.internetWebAddress();
        driver.get(URL);


        //Page 1
            // Item Text
        verifyText(Object.ItemText, "Multiple Choice - Single Answer Radio - Vertical");
        verifyText(Object.Progress_PercentComplete, "0%");
            //URL
        verifyNoRedirect();
            //Go to next page
        driver.findElement(Object.Start).click();
        System.out.println("Next Button was clicked.");

}
}

我不想 运行 在 SauceLabs 的 1 个浏览器上进行测试,我想 运行 在多个 browser/OS 组合上并行进行此测试。我不确定如何更改我的驱动程序 class 以实现此目的。

所以你正在使用 TestNG。该测试框架支持 运行ning in parallel 内置(这是一个可以添加到套件文件以及以其他方式设置的设置)。它还允许您使用 DataProvider 将参数传入测试方法。建立测试框架的方法有很多种,而且它的结构应该考虑很多因素。

首先,您要在 Driver class 中创建静态 WebDriver 实例,这不是您想要的。您需要多个实例,所以您真正想要的是 class 上的工厂方法,它允许您根据需要为每个方法创建 WebDriver。您要考虑的另一个注意事项是 TestNG 不会为每个方法创建 test class 对象的新实例。因此,您不能将任何状态存储为测试 class 对象上的字段,除非它是以避免争用的方式完成的(例如 ThreadLocal 字段)。不幸的是,在 Selenium 测试的情况下,当您完成 WebDriver 以释放 SauceLabs 资源时,您将希望退出它。您通常可以使用 @AfterMethod 配置方法来做到这一点,但您不能保证配置方法将 运行 在与测试方法相同的线程中,因此使用 ThreadLocal 字段不会'不允许访问相同的参数(例如网络驱动程序)。您可以在 class 对象上使用测试方法映射到 Web 驱动程序(TestNG 允许您将测试方法作为参数注入)但在数据提供者的情况下允许多个 OS /浏览器组合也将具有相同的测试方法。最后,根据我的经验,我发现使用 TestNG 和 Selenium 最好(或至少最简单)使用 DataProviders 并将字段存储在测试 class 对象上并将并行性限制在 class 级别。

话虽如此,这里是一个如何使用并行方法进行测试的示例。从一个简单的 pom.xml 依赖部分开始,以显示用于此示例的版本。即使你们的版本有点不同,应该也很相似。

<dependencies>
    <dependency>
        <groupId>org.seleniumhq.selenium</groupId>
        <artifactId>selenium-java</artifactId>
        <version>2.53.1</version>
    </dependency>
    <dependency>
        <groupId>org.testng</groupId>
        <artifactId>testng</artifactId>
        <version>6.9.10</version>
    </dependency>
</dependencies>

更新 Driver class 以确保它每次都干净地 returns 一个新的驱动程序实例。我删除了它的记录器部分只是为了节省一些 space。我也只是让它不可实例化,因为它实际上只是一个静态工厂并且允许它的实例没有意义。此外,您真的可能不应该允许访问名称和密钥之类的内容,因为它们纯粹是实现细节。

public final class Driver {

    private static final String USERNAME = "name";
    private static final String ACCESS_KEY = "key";

    public static WebDriver createDriver(String browser, String version, String os) {
        // Should probably validate the arguments here
        try {
            DesiredCapabilities capabilities = new DesiredCapabilities();
            capabilities.setCapability(CapabilityType.BROWSER_NAME, browser);
            capabilities.setCapability(CapabilityType.VERSION, version);
            capabilities.setCapability(CapabilityType.PLATFORM, os);
            return new RemoteWebDriver(new URL("http://" + USERNAME + ":" + ACCESS_KEY + "@ondemand.saucelabs.com:80/wd/hub"),
                                       capabilities);
        } catch (MalformedURLException e) {
            throw new RuntimeException("Failure forming the URL to create a web driver", e);
        }
    }

    private Driver() {
        throw new AssertionError("This is a static class and shouldn't be instantiated.");
    }
}

最后,在测试 class 本身中,您需要定义实际的测试方法和数据提供者。如果您希望同一数据提供者用于多个测试/测试 classes,那很好。详情参考TestNG文档:

http://testng.org/doc/documentation-main.html#parameters-dataproviders

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

public class FooTest {

    @DataProvider(name = "DriverInfoProvider", parallel = true) // data providers force single threaded by default
    public Object[][] driverInfoProvider() {
        return new Object[][] {
            { "firefox", "47.0", "Windows 10" },
            { "chrome" , "51.0", "Windows 10" },
            // etc, etc
        };
    }

    @Test(dataProvider = "DriverInfoProvider")
    public void testFoo(String browser, String version, String os) {
        WebDriver driver = Driver.createDriver(browser, version, os);
        try {
            // simple output to see the thread for each test method instance
            System.out.println("starting test in thread: " + Thread.currentThread().getName());
            // Putting this in a try catch block because you want to be sure to close the driver to free
            // up the resources even if the test fails
            driver.get("https://www.google.com");
            driver.findElement(By.name("q")).sendKeys("Hello, world");
        } finally {
            driver.quit();
        }
    }
}

请注意,从体系结构的角度来看,这些示例中有很多我不喜欢的地方,但我想让您了解一些需要考虑的问题并提供一个快速运行的示例这类似于你已经拥有的。最后,为了 运行 并行测试 class es,您将创建然后 运行 一个 TestNG 套件。这些通常在 XML 中定义(尽管您也可以使用 YAML)。

<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="MySuiteNameHere" parallel="methods" thread-count="15">

    <test name="Selenium Tests">
        <classes>
            <class name="foo.bar.FooTest"/>
        </classes>
    </test>
</suite>

最终 运行ning 导致加载 google 的两个测试,并与此示例输出一起执行操作:

starting test in thread: PoolService-1
starting test in thread: PoolService-0