17、Selenium 测试框架:第三方服务与示例代码解析

Selenium 测试框架:第三方服务与示例代码解析

1. TestObject 真实设备云

2017 年推出的 TestObject 真实设备云,是一个包含 Android 和 iOS 物理移动设备的资源池。用户可以购买许可证来访问这些设备,用于手动测试移动应用程序或运行自动化测试。与在模拟器和仿真器平台上运行不同,用户可以在他们所需测试的真实设备及其平台和版本上运行测试。

若要针对真实设备进行测试,需设置额外的驱动类功能以指向不同的远程中心,示例代码如下:

// section in CreateDriver.java class for TestObject features
...
boolean realDevice = true;
if ( realDevice == true ) {
    caps.setCapability("testobject_device",
                       "iPhone 6");
    caps.setCapability("testobject_cache_device",
                        false);
    caps.setCapability("testobject_session_creation_timeout",
                        "900000");
    caps.setCapability("testobject_appium_version",
                        "1.7.1");
    caps.setCapability("testobject_suite_name",
                        "mySuiteName");
    caps.setCapability("testobject_app_id",
                        1);
    caps.setCapability("testobject_test_name",
                        "myTestName");
    // private pool caps
    caps.setCapability("phoneOnly",
                       "iphone.phoneOnly.rdc");
    caps.setCapability("tabletOnly",
                       "iphone.tabletOnly.rdc");
    caps.setCapability("privateDevicesOnly",
                       "iphone.privateDevicesOnly.rdc");
    if ( browser.contains("iphone") || browser.contains("ipad") ) {
        caps.setCapability("testobject_api_key",
                           "iOSAppKey");
    }
    else {
        caps.setCapability("testobject_api_key",
                           "androidAppKey");
    }
    remoteHubURL = "https://us1.appium.testobject.com/wd/hub";
}

2. Jenkins 插件

还有一个适用于 Jenkins 的 Sauce Labs 插件。该插件允许用户在 Jenkins 项目中选择所需的平台、浏览器、移动设备及其版本。用户还可以设置 SauceConnect 隧道参数,以及其他所需的命令行选项,如日志文件、日志文件路径、代理服务器、端口等。这些选择会被设置为系统环境变量,在运行时可被驱动程序调用。

3. 自建网格与第三方网格的优缺点对比

对比项 自建网格 第三方网格(如 Sauce Labs)
平台数量 难以构建包含 900 种浏览器/操作系统组合或数百个移动 iOS 和 Android 设备的网格 几乎拥有无限的浏览器和移动平台资源池,可测试不同的浏览器版本、操作系统版本、移动设备版本等
维护 需要持续安装操作系统补丁、安全补丁、浏览器升级、Selenium 升级、驱动程序升级等。在移动方面,还需不断升级 Xcode 或 Android SDK 以及模拟器/仿真器,以及 Appium、NPM、Node.js 等 无需维护,只需承担财务成本
性能 速度比 Sauce Labs 测试云快 25 - 30% 测试运行时会引入延迟,但如果将测试设计得小而模块化,并并行运行,可利用多个虚拟机同时运行,从而加快测试速度
高可用性 由于定期向网格上的虚拟机推送软件更新,节点可能会重启,导致节点可用性不稳定 服务大部分时间可用,至少 99% 的时间处于运行状态
升级 用户需要在版本发布之间安排停机时间来关闭中心和节点,升级频率较低 持续提供新平台、浏览器和移动设备的升级,支持最新的 Selenium、Appium 和浏览器驱动程序修订版
增强功能 支持基于 Selenium 的插件,如 BrowserMob Proxy,并提供最新技术的测试和使用,包括新推出的支持物理设备移动测试的 TestObject 真实设备云服务

4. 第三方插件集成

在 Selenium 框架中,由于使用 Java 和 TestNG 技术,与之相关的各种插件和 API 易于集成:
- IntelliJ 插件 :有适用于 IntelliJ 的 Selenium 插件,以及内置的 TestNG 插件,可在控制台和报告格式中提供测试结果。
- Jenkins 插件 :Jenkins 有 TestNG 插件提供结果和历史数据,还有 HTML 发布器插件,允许用户包含框架自动生成的 HTML 报告。
- ExtentReports API :可利用 DataProvider 数据和 TestNG 结果集成到框架中。

5. 工作示例框架

示例框架包含驱动类、所需的实用类、浏览器页面对象类、浏览器测试类和 JSON 数据文件,可在 IntelliJ 或 Eclipse IDE 中运行,具体组件如下:
- Selenium 驱动和 DataProvider 类
- Selenium 实用类
- ExtentReports 类
- 浏览器页面对象基类和子类
- 浏览器测试类和数据文件
- 浏览器套件 XML 和 Maven POM XML 文件

5.1 运行示例测试的准备工作

  • 下载文件 :下载最新的 Selenium 3.x JAR 文件、TestNG JAR 文件以及所需的浏览器驱动程序版本。具体包括:
    • Java 1.8 SDK 和 JRE
    • IntelliJ IDEA 2017.3
    • Selenium 3.7.1 WebDriver JARs
    • TestNG 6.11 JARs
    • ExtentReports 3.1.0 JARs
    • ChromeDriver.exe 2.33(Windows 32 位;目前无 64 位驱动)
    • Firefox GeckoDriver.exe 0.19.1(Windows 64 位)
    • IEDriverServer.exe 3.7.1(Windows 32 位;比 64 位驱动运行速度快)
    • Chrome 浏览器 62.0
    • Firefox 浏览器 57.0
    • Internet Explorer 浏览器 11.0
  • 文件放置与路径修改 :将文件放置在 IDE 的项目文件夹中,并修改 selenium.properties 和 Global_VARS.java 文件中的路径,以指向正确的包和驱动程序位置。
  • 包结构创建 :在 IntelliJ 中创建名为 SeleniumFrameworkDesign 的项目,并创建以下包结构:src/main/java/com/framework/ux/utils/chapter10。同时,创建以下文件夹用于存放驱动程序和测试输出:SeleniumFrameworkDesign/drivers 和 SeleniumFrameworkDesign/test - output。

5.2 示例代码

5.2.1 CreateDriver.java
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.firefox.FirefoxOptions;
import org.openqa.selenium.firefox.FirefoxProfile;
import org.openqa.selenium.ie.InternetExplorerDriver;
import org.openqa.selenium.ie.InternetExplorerOptions;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.remote.RemoteWebDriver;
import java.io.FileInputStream;
import java.util.*;
import java.util.concurrent.TimeUnit;
/**
 * @author Carl Cocchiaro
 *
 * Selenium Driver Class
 *
 */
public class CreateDriver {
    // local variables
    private static CreateDriver instance = null;
    private static final int IMPLICIT_TIMEOUT = 0;
    private ThreadLocal<WebDriver> webDriver =
        new ThreadLocal<WebDriver>();
    private ThreadLocal<String> sessionId =
        new ThreadLocal<String>();
    private ThreadLocal<String> sessionBrowser =
        new ThreadLocal<String>();
    private ThreadLocal<String> sessionPlatform =
        new ThreadLocal<String>();
    private ThreadLocal<String> sessionVersion =
        new ThreadLocal<String>();
    private String getEnv = null;
    private Properties props = new Properties();
    // constructor
    private CreateDriver() {
    }
    /**
     * getInstance method to retrieve active driver instance
     *
     * @return CreateDriver
     */
    public static CreateDriver getInstance() {
        if ( instance == null ) {
            instance = new CreateDriver();
        }

        return instance;
    }
    /**
     * setDriver method to create driver instance
     *
     * @param browser
     * @param environment
     * @param platform
     * @param optPreferences
     * @throws Exception
     */
    @SafeVarargs
    public final void setDriver(String browser,
                                String platform,
                                String environment,
                                Map<String, Object>... optPreferences)
                                throws Exception {
        DesiredCapabilities caps = null;
        String getPlatform = null;
        props.load(new FileInputStream(Global_VARS.SE_PROPS));
        switch (browser) {
            case "firefox":
                caps = DesiredCapabilities.firefox();
                FirefoxOptions ffOpts = new FirefoxOptions();
                FirefoxProfile ffProfile = new FirefoxProfile();
                ffProfile.setPreference("browser.autofocus",
                                         true);
                ffProfile.setPreference("browser.tabs.remote.autostart.2", false);
                caps.setCapability(FirefoxDriver.PROFILE,
                                   ffProfile);
                caps.setCapability("marionette",
                                    true);
                // then pass them to the local WebDriver
                if ( environment.equalsIgnoreCase("local") ) {
                    System.setProperty("webdriver.gecko.driver",
                    props.getProperty("gecko.driver.windows.path"));
                    webDriver.set(new 
                    FirefoxDriver(ffOpts.merge(caps)));
                }
                break;
            case "chrome":
                caps = DesiredCapabilities.chrome();
                ChromeOptions chOptions = new ChromeOptions();
                Map<String, Object> chromePrefs =
                    new HashMap<String, Object>();
                chromePrefs.put("credentials_enable_service",
                                 false);
                chOptions.setExperimentalOption("prefs",
                                                 chromePrefs);

                chOptions.addArguments("--disable-plugins",
                                       "--disable-extensions",
                                       "--disable-popup-blocking");
                caps.setCapability(ChromeOptions.CAPABILITY,
                                   chOptions);
                caps.setCapability("applicationCacheEnabled",
                                    false);
                if ( environment.equalsIgnoreCase("local") ) {
                    System.setProperty("webdriver.chrome.driver",
                    props.getProperty("chrome.driver.windows.path"));
                    webDriver.set(new 
                    ChromeDriver(chOptions.merge(caps)));
                }
                break;
            case "internet explorer":
                caps = DesiredCapabilities.internetExplorer();
                InternetExplorerOptions ieOpts =
                    new InternetExplorerOptions();
                ieOpts.requireWindowFocus();
                ieOpts.merge(caps);
                caps.setCapability("requireWindowFocus",
                                    true);
                if ( environment.equalsIgnoreCase("local") ) {
                    System.setProperty("webdriver.ie.driver",
                    props.getProperty("ie.driver.windows.path"));
                    webDriver.set(new InternetExplorerDriver(
                                  ieOpts.merge(caps)));
                }
                break;
        }
        getEnv = environment;
        getPlatform = platform;
        sessionId.set(((RemoteWebDriver) webDriver.get())
        .getSessionId().toString());
        sessionBrowser.set(caps.getBrowserName());
        sessionVersion.set(caps.getVersion());
        sessionPlatform.set(getPlatform);
        System.out.println("\n*** TEST ENVIRONMENT = "
                + getSessionBrowser().toUpperCase()
                + "/" + getSessionPlatform().toUpperCase()
                + "/" + getEnv.toUpperCase()
                + "/Selenium Version="
                + props.getProperty("selenium.revision")
                + "/Session ID="
                + getSessionId()
                + "\n");
        getDriver().manage().timeouts().implicitlyWait(
            IMPLICIT_TIMEOUT, TimeUnit.SECONDS);
        getDriver().manage().window().maximize();

    }
    /**
     * getDriver method to retrieve active driver
     *
     * @return WebDriver
     */
    public WebDriver getDriver() {
        return webDriver.get();
    }
    /**
     * closeDriver method to close active driver
     *
     */
    public void closeDriver() {
        try {
            getDriver().quit();
        }
        catch ( Exception e ) {
            // do something
        }
    }
    /**
     * getSessionId method to retrieve active id
     *
     * @return String
     * @throws Exception
     */
    public String getSessionId() throws Exception {
        return sessionId.get();
    }
    /**
     * getSessionBrowser method to retrieve active browser
     * @return String
     * @throws Exception
     */
    public String getSessionBrowser() throws Exception{
        return sessionBrowser.get();
    }
    /**
     * getSessionVersion method to retrieve active version
     *
     * @return String
     * @throws Exception
     */
    public String getSessionVersion() throws Exception {
        return sessionVersion.get();
    }
    /**
     * getSessionPlatform method to retrieve active platform
     * @return String
     * @throws Exception
     */
    public String getSessionPlatform() throws Exception {
        return sessionPlatform.get();
    }
}
5.2.2 JSONDataProvider.java
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.testng.annotations.DataProvider;
import java.io.FileReader;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
 * @author Carl Cocchiaro
 *
 * TestNG JSON DataProvider Utility Class
 *
 */
public class JSONDataProvider {
    public static String dataFile = "";
    public static String testCaseName = "NA";
    public JSONDataProvider() throws Exception {
    }
    /**
     * fetchData method to retrieve test data for specified method
     *
     * @param method
     * @return Object[][]
     * @throws Exception
     */
    @DataProvider(name = "fetchData_JSON")
    public static Object[][] fetchData(Method method) throws Exception 
    {
        Object rowID, description;
        Object[][] result;
        testCaseName = method.getName();
        List<JSONObject> testDataList = new ArrayList<JSONObject>();
        JSONArray testData =
            (JSONArray) 
            extractData_JSON(dataFile).get(method.getName());
        for ( int i = 0; i < testData.size(); i++ ) {
            testDataList.add((JSONObject) testData.get(i));
        }
        // include Filter
        if ( System.getProperty("includePattern") != null ) {
            String include = System.getProperty("includePattern");
            List<JSONObject> newList = new ArrayList<JSONObject>();
            List<String> tests = Arrays.asList(include.split(",", -1));
            for ( String getTest : tests ) {

                for ( int i = 0; i < testDataList.size(); i++ ) {
                    if ( 
                       testDataList.get(i).toString().
                       contains(getTest) ) {
                        newList.add(testDataList.get(i));
                    }
                }
            }
            // reassign testRows after filtering tests
            testDataList = newList;
        }
        // exclude Filter
        if ( System.getProperty("excludePattern") != null ) {
            String exclude =System.getProperty("excludePattern");
            List<String> tests = Arrays.asList(exclude.split(",", -1));
            for ( String getTest : tests ) {
                for ( int i = testDataList.size() - 1 ; i >= 0; i-- ) {
                    if ( testDataList.get(i).toString().
                    contains(getTest) ) {
                        testDataList.remove(testDataList.get(i));
                    }
                }
            }
        }
        // create object for dataprovider to return
        try {
            result =
            new Object[testDataList.size()]
            [testDataList.get(0).size()];
            for ( int i = 0; i < testDataList.size(); i++ ) {
                rowID = testDataList.get(i).get("rowID");
                description = testDataList.get(i).get("description");
                result[i] =
                new Object[] { rowID, description, testDataList.get(i) 
               };
            }
        }
        catch(IndexOutOfBoundsException ie) {
            result = new Object[0][0];
        }
        return result;
    }
    /**
     * extractData_JSON method to get JSON data from file
     *
     * @param file
     * @return JSONObject
     * @throws Exception
     */
    public static JSONObject extractData_JSON(String file) throws 
    Exception {
        FileReader reader = new FileReader(file);
        JSONParser jsonParser = new JSONParser();
        return (JSONObject) jsonParser.parse(reader);
    }
}
5.2.3 工作流 mermaid 图
graph LR
    A[开始] --> B[选择测试平台]
    B --> C{是否使用第三方服务}
    C -- 是 --> D[使用 Sauce Labs 等服务]
    C -- 否 --> E[自建 Selenium 网格]
    D --> F[配置 TestObject 真实设备云或其他服务]
    E --> G[维护网格环境]
    F --> H[运行测试]
    G --> H
    H --> I[生成测试报告]
    I --> J[结束]

通过以上内容,我们对 Selenium 框架中第三方插件的使用、自建网格与第三方网格的优缺点有了清晰的了解,并掌握了示例框架的运行方法和相关代码。

6. Selenium 实用类代码解析

6.1 BrowserUtils.java

import org.openqa.selenium.*;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
/**
 * @author Carl Cocchiaro
 *
 * Browser Utility Class
 *
 */
public class BrowserUtils {
    /**
     * waitFor method to poll page title
     *
     * @param title
     * @param timer
     * @throws Exception
     */
    public static void waitFor(String title,
                               int timer)
                               throws Exception {
        WebDriver driver = CreateDriver.getInstance().getDriver();
        WebDriverWait exists = new WebDriverWait(driver, timer);
        exists.until(ExpectedConditions.refreshed(
                     ExpectedConditions.titleContains(title)));
    }
    /**
     * waitForURL method to poll page URL
     *
     * @param url
     * @param timer
     * @throws Exception
     */
    public static void waitForURL(String url,
                                  int timer)
                                  throws Exception {
        WebDriver driver = CreateDriver.getInstance().getDriver();
        WebDriverWait exists = new WebDriverWait(driver, timer);
        exists.until(ExpectedConditions.refreshed(
                     ExpectedConditions.urlContains(url)));
    }
    /**
     * waitForClickable method to poll for clickable
     *
     * @param by
     * @param timer
     * @throws Exception
     */
    public static void waitForClickable(By by,
                                        int timer)
                                        throws Exception {
        WebDriver driver = CreateDriver.getInstance().getDriver();
        WebDriverWait exists = new WebDriverWait(driver, timer);
        exists.until(ExpectedConditions.refreshed(
                     ExpectedConditions.elementToBeClickable(by)));
    }
    /**
     * click method using JavaScript API click
     *
     * @param by
     * @throws Exception
     */
    public static void click(By by) throws Exception {
        WebDriver driver = CreateDriver.getInstance().getDriver();
        WebElement element = driver.findElement(by);
        JavascriptExecutor js = (JavascriptExecutor)driver;
        js.executeScript("arguments[0].click();", element);
    }
}

BrowserUtils 类提供了一些实用的方法,用于处理浏览器操作:
- waitFor 方法:用于轮询页面标题,直到标题包含指定的字符串。
- waitForURL 方法:用于轮询页面 URL,直到 URL 包含指定的字符串。
- waitForClickable 方法:用于轮询元素是否可点击。
- click 方法:使用 JavaScript API 点击元素。

6.2 Global_VARS.java

import java.io.File;
/**
 * @author Carl Cocchiaro
 *
 * Global Variable Utility Class
 *
 */
public class Global_VARS {
    // browser defaults
    public static final String BROWSER = "chrome";
    public static final String PLATFORM = "Windows 7";
    public static final String ENVIRONMENT = "local";
    public static String DEF_BROWSER = null;
    public static String DEF_PLATFORM = null;
    public static String DEF_ENVIRONMENT = null;
    // suite folder defaults
    public static String SUITE_NAME = null;
    public static final String TARGET_URL =
    "http://www.practiceselenium.com/";
    public static String propFile =
    "src/main/java/com/framework/ux/utils/chapter10/selenium.properties";
    public static final String SE_PROPS =
    new File(propFile).getAbsolutePath();
    public static final String TEST_OUTPUT_PATH = "test-output/";
    public static final String LOGFILE_PATH = TEST_OUTPUT_PATH + 
    "Logs/";
    public static final String REPORT_PATH = TEST_OUTPUT_PATH + 
    "Reports/";
    public static final String REPORT_CONFIG_FILE =
    "src/main/java/com/framework/ux/utils/chapter10/extent-config.xml";
    // suite timeout defaults
    public static final int TIMEOUT_MINUTE = 60;
    public static final int TIMEOUT_ELEMENT = 10;
}

Global_VARS 类定义了一些全局变量,包括默认的浏览器、平台、环境,以及测试输出路径、日志文件路径、报告路径等。

6.3 TestNG_ConsoleRunner.java

import org.testng.ITestContext;
import org.testng.ITestResult;
import org.testng.TestListenerAdapter;
import java.io.*;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
 * @author Carl Cocchiaro
 *
 * TestNG Listener Utility Class
 *
 */
public class TestNG_ConsoleRunner extends TestListenerAdapter {
    private static String logFile = null;
    /**
     * onStart method
     *
     * @param testContext
     */
    @Override
    public void onStart(ITestContext testContext) {
        super.onStart(testContext);
    }
    /**
     * onFinish method
     *
     * @param testContext
     */
    @Override
    public void onFinish(ITestContext testContext) {
        log("\nTotal Passed = "
            + getPassedTests().size()
            + ", Total Failed = "
            + getFailedTests().size()
            + ", Total Skipped = "
            + getSkippedTests().size()
            + "\n");
        super.onFinish(testContext);
    }
    /**
     * onTestStart method
     *
     * @param tr
     */
    @Override
    public void onTestStart(ITestResult tr) {
        if ( logFile == null ) {
            logFile = Global_VARS.LOGFILE_PATH
                      + Global_VARS.SUITE_NAME
                      + "-"
                      + new SimpleDateFormat("MM.dd.yy.HH.mm.ss")
                      .format(new Date())
                      + ".log";
        }
        log("\n---------------------------------- Test '"
            + tr.getName()
            + getTestDescription(tr)
            + "' ----------------------------------\n");
        log(tr.getStartMillis(),
            "START-> "
            + tr.getName() + "\n");
        log("    ***Test Parameters = "
            + getTestParams(tr)
            + "\n");
        super.onTestStart(tr);
    }
    /**
     * onTestSuccess method
     *
     * @param tr
     */
    @Override
    public void onTestSuccess(ITestResult tr) {
        log("    ***Result = PASSED\n");
        log(tr.getEndMillis(),
            "END  -> "
            + tr.getName());
        log("\n---\n");
        super.onTestSuccess(tr);
    }
    /**
     * onTestFailure method
     *
     * @param tr
     */
    @Override
    public void onTestFailure(ITestResult tr) {
        if ( !getTestMessage(tr).equals("") ) {
            log(getTestMessage(tr) + "\n");
        }
        log("    ***Result = FAILED\n");
        log(tr.getEndMillis(),
            "END  -> "
            + tr.getInstanceName()
            + "." + tr.getName());
        log("\n---\n");

        super.onTestFailure(tr);
    }
    /**
     * onTestSkipped method
     *
     * @param tr
     */
    @Override
    public void onTestSkipped(ITestResult tr) {
        if ( !getTestMessage(tr).equals("") ) {
            log(getTestMessage(tr)
                + "\n");
        }
        log("    ***Result = SKIPPED\n");
        log(tr.getEndMillis(),
            "END  -> "
            + tr.getInstanceName()
            + "."
            + tr.getName());
        log("\n---\n");
        super.onTestSkipped(tr);
    }
    /**
     * onConfigurationSuccess method
     *
     * @param itr
     */
    @Override
    public void onConfigurationSuccess(ITestResult itr) {
        super.onConfigurationSuccess(itr);
    }
    /**
     * onConfigurationFailure method
     *
     * @param tr
     */
    @Override
    public void onConfigurationFailure(ITestResult tr) {
        if ( !getTestMessage(tr).equals("") ) {
            log(getTestMessage(tr)
                + "\n");
        }
        log("    ***Result = CONFIGURATION FAILED\n");
        log(tr.getEndMillis(),
            "END CONFIG -> "
            + tr.getInstanceName()
            + "."
            + tr.getName());
        log("\n---\n");
        super.onConfigurationFailure(tr);
    }
    /**
     * onConfigurationSkip method
     *
     * @param tr
     */
    @Override
    public void onConfigurationSkip(ITestResult tr) {
        log(getTestMessage(tr));
        log("    ***Result = CONFIGURATION SKIPPED\n");
        log(tr.getEndMillis(),
            "END CONFIG -> "
            + tr.getInstanceName()
            + "."
            + tr.getName());
        log("\n---\n");
        super.onConfigurationSkip(tr);
    }
    /**
     * log method
     *
     * @param dateMillis
     * @param line
     */
    public void log(long dateMillis,String line) {
        System.out.format("%s: %s%n",
                          String.valueOf(new Date(dateMillis)),line);
        if ( logFile != null ) {
            writeTestngLog(logFile,
                           line);
        }
    }
    /**
     * log method
     *
     * @param line
     */
    public void log(String line) {
        System.out.format("%s%n", line);
        if ( logFile != null ) {
            writeTestngLog(logFile, line);
        }
    }
    /**
     * getTestMessage method
     *
     * @param tr
     * @return String
     */
    public String getTestMessage(ITestResult tr) {
        Boolean found = false;
        if ( tr != null && tr.getThrowable() != null ) {
            found = true;
        }
        if ( found == true ) {
            return tr.getThrowable().getMessage() ==
                null ? "" : tr.getThrowable().getMessage();
        }
        else {
            return "";
        }
    }
    /**
     * getTestParams method
     *
     * @param tr
     * @return String
     */
    public String getTestParams(ITestResult tr) {
        int iLength = tr.getParameters().length;
        String message = "";
        try {
            if ( tr.getParameters().length > 0 ) {
                message = tr.getParameters()[0].toString();
                for ( int iCount = 0; iCount < iLength; iCount++ ) {
                    if ( iCount == 0 ) {
                        message = tr.getParameters()[0].toString();
                    }
                    else {
                        message = message
                                  + ", "
                                  + tr.getParameters()
                                  [iCount].toString();
                    }
                }
            }
        }
        catch(Exception e) {
            // do nothing...
        }
        return message;
    }
    /**
     * getTestDescription method
     *
     * @param tr
     * @return String
     */
    public String getTestDescription(ITestResult tr) {
        String message = "";
        try {
            if ( tr.getParameters().length > 0 ) {
                message = ": "
                          + tr.getParameters()[1].toString();
            }
        }
        catch(Exception e) {
            // do nothing...
        }
        return message;
    }
    /**
     * writeTestngLog method
     *
     * @param logFile
     * @param line
     */
    public void writeTestngLog(String logFile,String line) {
        DateFormat dateFormat =
            new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");
        Date date = new Date();
        File directory = new File(Global_VARS.LOGFILE_PATH);
        File file = new File(logFile);
        try {
            if ( !directory.exists() ) {
                directory.mkdirs();
            }
            else if ( !file.exists() ) {
                file.createNewFile();
            }
            BufferedWriter writer =
                new BufferedWriter(new FileWriter(logFile, true));
            if ( line.contains("START") || line.contains("END") ) {
                writer.append("["
                              + dateFormat.format(date)
                              + "] "
                              + line);
            }
            else {
                writer.append(line);
            }
            writer.newLine();
            writer.close();
        }
        catch(IOException e) {
            // do nothing...
        }
    }
}

TestNG_ConsoleRunner 类是一个 TestNG 监听器,用于记录测试执行的日志。它会在测试开始、成功、失败、跳过以及配置成功、失败、跳过时记录相应的信息,并将日志写入文件。

7. 总结与应用建议

7.1 总结

  • 第三方插件在 Selenium 框架中具有重要作用,如 Sauce Labs 的 TestObject 真实设备云、Jenkins 插件等,它们能方便地集成到框架中,提高测试效率。
  • 自建 Selenium 网格和使用第三方网格各有优缺点,需要根据项目的具体需求进行选择。
  • 示例框架提供了完整的代码结构,包括驱动类、实用类、页面对象类和测试类等,为实际项目开发提供了参考。

7.2 应用建议

  • 选择合适的测试方式 :如果项目需要测试多种平台和设备,且对维护成本敏感,可选择第三方网格服务;如果对性能要求较高,且有足够的维护资源,可考虑自建网格。
  • 优化测试代码 :编写小而模块化的测试用例,并并行运行,以提高测试速度。
  • 利用插件和 API :充分利用 IntelliJ、Jenkins 等工具的插件,以及 ExtentReports API 等,提升测试结果的可视化和分析能力。

7.3 测试流程总结 mermaid 图

graph LR
    A[环境准备] --> B[选择测试平台]
    B --> C[配置测试环境]
    C --> D[编写测试用例]
    D --> E[运行测试]
    E --> F[生成测试报告]
    F --> G{测试是否通过}
    G -- 是 --> H[结束测试]
    G -- 否 --> I[分析失败原因]
    I --> J[修复问题]
    J --> E

通过以上内容,我们全面了解了 Selenium 框架中第三方服务的使用、自建与第三方网格的对比,以及示例框架的代码实现和应用建议。希望这些信息能帮助你更好地进行 Web 和移动应用的测试工作。

Java是一种具备卓越性能广泛平台适应性的高级程序设计语言,最初由Sun Microsystems(现属Oracle公司)的James Gosling及其团队于1995年正式发布。该语言在设计上追求简洁性、稳定性、可移植性以及并发处理能力,同时具备动态执行特性。其核心特征显著优点可归纳如下: **平台无关性**:遵循“一次编写,随处运行”的理念,Java编写的程序能够在多种操作系统硬件环境中执行,无需针对不同平台进行修改。这一特性主要依赖于Java虚拟机(JVM)的实现,JVM作为程序底层系统之间的中间层,负责解释并执行编译后的字节码。 **面向对象范式**:Java全面贯彻面向对象的设计原则,提供对封装、继承、多态等机制的完整支持。这种设计方式有助于构建结构清晰、模块独立的代码,提升软件的可维护性扩展性。 **并发编程支持**:语言层面集成了多线程处理能力,允许开发者构建能够同时执行多项任务的应用程序。这一特性尤其适用于需要高并发处理的场景,例如服务器端软件、网络服务及大规模分布式系统。 **自动内存管理**:通过内置的垃圾回收机制,Java运行时环境能够自动识别并释放不再使用的对象所占用的内存空间。这不仅降低了开发者在内存管理方面的工作负担,也有效减少了因手动管理内存可能引发的内存泄漏问题。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值