关于多线程 DataProvider 传递能力的问题

Question Regarding Multithreading DataProvider Passing Capabilities

我正在尝试在我正在编写的新线程安全测试框架中实现并行性。我有一个 DataProviderManager.java class,其中包含我的静态 DataProvider

@DataProvider(parallel = true, name = Config.StaticProps.DEFAULT_WEB_DATA_PROVIDER)
public static Object[][] defaultWebDataProvider() {
    return new Object[][] {
            new Object[]{"chrome", "69", "MAC 10.12"},
            new Object[]{"safari", "11.0", "Mac 10.13"},
            new Object[]{"safari", "11.0", "Mac 10.13"}
            new Object[]{"chrome", "70", "Mac 10.13"}
            new Object[]{"firefox", "63", "MAC 10.12"},
            new Object[]{"firefox", "62", "MAC 10.12"}
    };
}

我有一个测试 class 将 3 个参数从我的 DataProvider 传递到 ThreadLocal getter 和 setter 以设置每个线程的 Selenium 功能,从而实例化具有这些功能的驱动程序实例。测试本身只是根据一些 运行 时间参数打开一个特定的 url。

@Factory(dataProvider = StaticProps.DEFAULT_WEB_DATA_PROVIDER, dataProviderClass = DataProviderManager.class)
//@Factory(dataProvider = StaticProps.DEFAULT_WEB_DATA_PROVIDER)
public WebExampleTest(String browser, String browserVersion, String platform) {
    super.setRunParams(browser, browserVersion, platform);
}

@BeforeMethod(alwaysRun = true)
public void setupTest() {
    home = new Home();
}

@TestCaseId("")
@Features({GroupProps.WEB})
@Test(groups = {GroupProps.DEBUG}, dataProvider = StaticProps.DEFAULT_WEB_DATA_PROVIDER, dataProviderClass = DataProviderManager.class)
@Parameters({ ParamProps.WEB, ParamProps.MOBILE_WEB })
public void openSiteTest(String browser, String browserVersion, String platform) {
    Logger.logMessage("Current Thread ID: " +  Thread.currentThread().getId());
    new WebInteract(null, null).pause(1000).openUrl(URLBuilder.buildUrl());
}

这是我的 testing.xml 文件

<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="Test Engineering Core Suite"
       parallel="true"
       data-provider-thread-count="6"
       thread-count="6"
       verbose="2">

    <!-- LISTENERS -->
    <listeners>
        <listener class-name="com.msg.test.core.listeners.TestListeners" />
        <listener class-name="com.msg.test.core.listeners.SuiteListeners" />
        <listener class-name="com.msg.test.core.listeners.TestAnnotationTransformer" />
    </listeners>

    <!-- TEST EXECUTION QUEUE -->
    <test verbose="1" name="All Tests" annotations="JDK">

        <!-- TEST GROUPS
            "Full" - all tests in the test suite.
            "Broken" - those tests that are broken and safe to ignore until they can be resolved.
        -->

        <!--<groups>-->
            <!--<run>-->
                <!--<include name="Debug" />-->
                <!--<exclude name="Broken" />-->
            <!--</run>-->
        <!--</groups>-->

    </test>    
</suite>

这是输出的一部分,向您展示当前发生的事情

2018-11-18 12:49:37.633 ========NEW TEST SESSION========
2018-11-18 12:49:37.633 Desktop OS: Mac 10.13
2018-11-18 12:49:37.633 Browser: chrome
2018-11-18 12:49:37.633 Browser Version: 70
2018-11-18 12:49:37.634 Current Thread ID: 66
2018-11-18 12:49:37.635 Pause for '1000' milliseconds.
Nov 18, 2018 12:49:37 AM org.openqa.selenium.remote.ProtocolHandshake createSession
INFO: Detected dialect: OSS
2018-11-18 12:49:37.846 Session ID: 622fda273344469ab98aef0d06f90315
2018-11-18 12:49:37.847 ========NEW TEST SESSION========
2018-11-18 12:49:37.847 Desktop OS: Mac 10.13
2018-11-18 12:49:37.847 Browser: chrome
2018-11-18 12:49:37.848 Browser Version: 70
2018-11-18 12:49:37.849 Current Thread ID: 68
2018-11-18 12:49:37.849 Pause for '1000' milliseconds.
2018-11-18 12:49:37.950 Open url 'https://www.example.com'.
2018-11-18 12:49:38.571 Open url 'https://www.example.com'.
2018-11-18 12:49:38.571 Open url 'https://www.example.com'.
2018-11-18 12:49:38.576 ======SUCCESS======
2018-11-18 12:49:38.577 Desktop OS: Mac 10.13
2018-11-18 12:49:38.577 Browser: chrome
2018-11-18 12:49:38.577 Browser Version: 70
2018-11-18 12:49:38.640 Open url 'https://www.example.com'.
2018-11-18 12:49:38.854 Open url 'https://www.example.com'.
2018-11-18 12:49:39.059 ======SUCCESS======
2018-11-18 12:49:39.060 Desktop OS: Mac 10.13
2018-11-18 12:49:39.060 Browser: chrome
2018-11-18 12:49:39.060 Browser Version: 70
2018-11-18 12:49:39.083 ======SUCCESS======
2018-11-18 12:49:39.084 Desktop OS: Mac 10.13
2018-11-18 12:49:39.084 Browser: chrome
2018-11-18 12:49:39.084 Browser Version: 70
2018-11-18 12:49:39.126 ======SUCCESS======
2018-11-18 12:49:39.126 Browser: chrome
2018-11-18 12:49:39.126 Browser Version: 70
2018-11-18 12:49:39.393 ======SUCCESS======

如您所见,我能够 运行 在不同线程上并行执行相同的测试,但每个线程都 运行 在来自我的 DataProvider 的相同浏览器、版本和平台上运行。

我的问题是 运行 多个线程同时从 DataProvider 执行相同的数据(浏览器、版本平台 - chrome、70、Mac 10.13) ,如何 运行 我 运行 每个(浏览器,版本,平台)

  new Object[]{"chrome", "69", "MAC 10.12"}, **This should be thread 1**
            new Object[]{"safari", "11.0", "Mac 10.13"}, **This should be thread 2**
            new Object[]{"safari", "11.0", "Mac 10.13"} **This should be thread 3**
            new Object[]{"chrome", "70", "Mac 10.13"} **This should be thread 4**
            new Object[]{"firefox", "63", "MAC 10.12"}, **This should be thread 5**
            new Object[]{"firefox", "62", "MAC 10.12"} **This should be thread 6**

同时在不同线程上从 DataProvider 获取?

这是 TestNG 的工作原理

常规 @Test 方法与数据提供程序相结合

当一个普通的 @Test 注释测试方法需要 运行 多次但使用不同的数据集时,您可以将其与数据提供程序结合使用。要并行 运行 它,基本上可以在 @DataProvider 注释上启用并行属性,并在 <suite> 标记级别指定属性 parallel=methods

下面的示例显示了所有这些操作。它使用 org.testng.IHookable 接口,因此基本上可以提取通过数据提供程序发送到 @Test 方法的参数,并使用它们进行每次迭代所需的任何设置。

您也可以使用 @BeforeMethod 注释,但是不可能从一个@BeforeMethod.

import org.testng.IHookCallBack;
import org.testng.IHookable;
import org.testng.ITestResult;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

public class TestClassWithDataDrivenTest implements IHookable {

  @Override
  public void run(IHookCallBack callBack, ITestResult result) {
    beforeMethod(callBack.getParameters());
    callBack.runTestMethod(result);
  }

  public void beforeMethod(Object[] parameters) {
    String browser = parameters[0].toString();
    String browserVersion = parameters[1].toString();
    String platform = parameters[2].toString();
    String msg =
        String.format(
            "[beforeMethod()-Thread Id : %d] [%s] flavor version [%s] on platform [%s]",
            Thread.currentThread().getId(), browser, browserVersion, platform);
    System.err.println(msg);
  }

  @Test(dataProvider = "dp")
  public void testMethod(String browser, String browserVersion, String platform) {
    String msg =
        String.format(
            "[testMethod()-Thread Id : %d] [%s] flavor version [%s] on platform [%s]",
            Thread.currentThread().getId(), browser, browserVersion, platform);
    System.err.println(msg);
  }

  @DataProvider(parallel = true, name = "dp")
  public static Object[][] defaultWebDataProvider() {
    return new Object[][] {
      new Object[] {"chrome", "69", "MAC 10.12"},
      new Object[] {"safari", "11.0", "Mac 10.13"},
      new Object[] {"safari", "11.0", "Mac 10.13"},
      new Object[] {"chrome", "70", "Mac 10.13"},
      new Object[] {"firefox", "63", "MAC 10.12"},
      new Object[] {"firefox", "62", "MAC 10.12"}
    };
  }
}

当你运行这个测试时对应的输出如下

[beforeMethod()-Thread Id : 11] [chrome] flavor version [69] on platform [MAC 10.12]
[beforeMethod()-Thread Id : 12] [safari] flavor version [11.0] on platform [Mac 10.13]
[beforeMethod()-Thread Id : 14] [chrome] flavor version [70] on platform [Mac 10.13]
[beforeMethod()-Thread Id : 13] [safari] flavor version [11.0] on platform [Mac 10.13]
[beforeMethod()-Thread Id : 15] [firefox] flavor version [63] on platform [MAC 10.12]
[testMethod()-Thread Id : 12] [safari] flavor version [11.0] on platform [Mac 10.13]
[beforeMethod()-Thread Id : 16] [firefox] flavor version [62] on platform [MAC 10.12]
[testMethod()-Thread Id : 13] [safari] flavor version [11.0] on platform [Mac 10.13]
[testMethod()-Thread Id : 14] [chrome] flavor version [70] on platform [Mac 10.13]
[testMethod()-Thread Id : 11] [chrome] flavor version [69] on platform [MAC 10.12]
[testMethod()-Thread Id : 16] [firefox] flavor version [62] on platform [MAC 10.12]
[testMethod()-Thread Id : 15] [firefox] flavor version [63] on platform [MAC 10.12]

===============================================
Default Suite
Total tests run: 6, Passes: 6, Failures: 0, Skips: 0
===============================================

一个@Factory加上一个数据提供者

一个人使用 @Factory 方法(这个注解可以添加到构造函数的顶部或 returns 一个 Object[] 的静态方法上)当一个人想要生成测试时class 个实例。

测试 class 实例被定义为一个常规 class,其中包含一个或多个 @Test 方法。工厂与数据提供者结合使用数据提供者提供的数据来实例化每个测试 class.

如果有人希望 TestNG 运行 @Test 方法在所有实例中一次性并行(TestNG 仍然 运行 所有 @Test每个测试 class 中的方法按顺序),那么您可以通过 <suite> 标签上的属性 parallel=instances 指定它。

请注意,您不能 运行 每个测试 class 实例中的所有 @Test 方法并行。 TestNG AFAIK 没有在该级别启用并行性的选项。

下面是一个演示此操作的示例

import org.testng.annotations.BeforeMethod;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Factory;
import org.testng.annotations.Test;

public class TestClassWithFactory {

  private String browser, browserVersion, platform;

  @Factory(dataProvider = "dp")
  public TestClassWithFactory(String browser, String browserVersion, String platform) {
    this.browser = browser;
    this.browserVersion = browserVersion;
    this.platform = platform;
  }

  @BeforeMethod
  public void beforeMethod() {
    String msg =
        String.format(
            "[Instance id %d][beforeMethod()-Thread Id : %d] [%s] flavor version [%s] on platform [%s]",
            this.hashCode(), Thread.currentThread().getId(), browser, browserVersion, platform);
    System.err.println(msg);
  }

  @Test
  public void testMethod() {
    String msg =
        String.format(
            "[Instance id %d][testMethod()-Thread Id : %d] [%s] flavor version [%s] on platform [%s]",
            this.hashCode(), Thread.currentThread().getId(), browser, browserVersion, platform);
    System.err.println(msg);
  }

  @DataProvider(parallel = true, name = "dp")
  public static Object[][] defaultWebDataProvider() {
    return new Object[][] {
      new Object[] {"chrome", "69", "MAC 10.12"},
      new Object[] {"safari", "11.0", "Mac 10.13"},
      new Object[] {"safari", "11.0", "Mac 10.13"},
      new Object[] {"chrome", "70", "Mac 10.13"},
      new Object[] {"firefox", "63", "MAC 10.12"},
      new Object[] {"firefox", "62", "MAC 10.12"}
    };
  }
}

testng套件xml文件如下

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="53345934_suite" parallel="instances" verbose="2">
  <test name="53345934_test">
    <classes>
      <class name="com.rationaleemotions.Whosebug.qn53345934.TestClassWithFactory"/>
    </classes>
  </test>
</suite>

执行输出如下:

...
... TestNG 7.0.0-beta1 by Cédric Beust (cedric@beust.com)
...
[Instance id 1627821297][beforeMethod()-Thread Id : 15] [safari] flavor version [11.0] on platform [Mac 10.13]
[Instance id 1549409129][beforeMethod()-Thread Id : 14] [chrome] flavor version [70] on platform [Mac 10.13]
[Instance id 922872566][beforeMethod()-Thread Id : 11] [firefox] flavor version [63] on platform [MAC 10.12]
[Instance id 1132547352][beforeMethod()-Thread Id : 12] [safari] flavor version [11.0] on platform [Mac 10.13]
[Instance id 1525037790][beforeMethod()-Thread Id : 13] [chrome] flavor version [69] on platform [MAC 10.12]
[Instance id 1132547352][testMethod()-Thread Id : 12] [safari] flavor version [11.0] on platform [Mac 10.13]
[Instance id 1549409129][testMethod()-Thread Id : 14] [chrome] flavor version [70] on platform [Mac 10.13]
[Instance id 1525037790][testMethod()-Thread Id : 13] [chrome] flavor version [69] on platform [MAC 10.12]
[Instance id 922872566][testMethod()-Thread Id : 11] [firefox] flavor version [63] on platform [MAC 10.12]
[Instance id 1627821297][testMethod()-Thread Id : 15] [safari] flavor version [11.0] on platform [Mac 10.13]
[Instance id 1651855867][beforeMethod()-Thread Id : 12] [firefox] flavor version [62] on platform [MAC 10.12]
[Instance id 1651855867][testMethod()-Thread Id : 12] [firefox] flavor version [62] on platform [MAC 10.12]
PASSED: testMethod
PASSED: testMethod
PASSED: testMethod
PASSED: testMethod
PASSED: testMethod
PASSED: testMethod

===============================================
    53345934_test
    Tests run: 6, Failures: 0, Skips: 0
===============================================

===============================================
53345934_suite
Total tests run: 6, Passes: 6, Failures: 0, Skips: 0
===============================================