写点什么

☕【Java 技术指南】「TestNG 专题」单元测试框架之 TestNG 使用教程指南(下)

发布于: 2 小时前
☕【Java技术指南】「TestNG专题」单元测试框架之TestNG使用教程指南(下)

承接上文

☕【Java技术指南】「TestNG专题」单元测试框架之TestNG使用教程指南(上),继续开展我们的下篇的 TestNG 的技术特性。

分组测试

分组测试即为使用 group,如果你使用 xml 的话就是里边的<groups>标签,如果是直接在 class 中,是通过 @Test(groups="group2")这种方式来分组,如第四节的注解说明中的那个例子,分了两个 group,而且 @BeforeGroup 是需要添加 group 名称才可以正确挂载到该 group 下的。


这个 group 说明可以是在单个的测试方法上,也可以在 class 上,只要具有同样的 group 名称都会在同一个 group 中,同时 group 名称可以有多个,类似 @Test(groups = {"mysql","database"})这种,范例如下:


一个测试文件 NewTest.class:


public class NewTest {  @Test(groups="group1")  public void test1() {    System.out.println("test1 from group1");    Assert.assertTrue(true);  }
@Test(groups="group1") public void test11() { System.out.println("test11 from group1"); Assert.assertTrue(true); } @Test(groups="group2") public void test2() { System.out.println("test2 from group2"); Assert.assertTrue(true); } @BeforeTest public void beforeTest() { System.out.println("beforeTest"); } @AfterTest public void afterTest() { System.out.println("afterTest"); } @BeforeClass public void beforeClass() { System.out.println("beforeClass"); } @AfterClass public void afterClass() { System.out.println("afterClass"); } @BeforeSuite public void beforeSuite() { System.out.println("beforeSuite"); } @AfterSuite public void afterSuite() { System.out.println("afterSuite"); } @BeforeGroups(groups="group1") public void beforeGroups() { System.out.println("beforeGroups"); } @AfterGroups(groups="group1") public void afterGroups() { System.out.println("afterGroups"); } @BeforeMethod public void beforeMethod() { System.out.println("beforeMethod"); } @AfterMethod public void afterMethod() { System.out.println("afterMethod"); }}
复制代码
另一个 TestCase1.class:
@Test(groups= "group2")public class TestCase1 {
@Test(enabled=false) public void TestNgLearn1() { System.out.println("this is TestNG test case1"); } @Test public void TestNgLearn2() { System.out.println("this is TestNG test case2"); }}
复制代码
xml 如下:
<?xml version="1.0" encoding="UTF-8"?><suite name="Suite" parallel="false">  <test name="Test">    <groups>      <include name="group1"></incloud>      <include name="group2"></incloud>    </groups>    <classes>      <class name="com.demo.test.testng.NewTest"/>      <class name="com.demo.test.testng.TestCase1"/>    </classes>  </test> <!-- Test --></suite> <!-- Suite -->
复制代码
运行结果如下:
beforeSuitebeforeTestbeforeClassbeforeGroupsbeforeMethodtest1 from group1afterMethodbeforeMethodtest11 from group1afterMethodafterGroupsbeforeMethodtest2 from group2afterMethodafterClassthis is TestNG test case2afterTestafterSuite
复制代码


如上所示,先运行了 group1 的两个用例,再运行 group2 的两条用例;注意在 xml 标识 group,需要将要运行的 group 加进来,同时还要将被标识这些 group 的 class 也加进来,不被加进去的不会运行;

分 suite 测试

  • 测试套件是用于测试软件程序的行为或一组行为的测试用例的集合。 在 TestNG 中,我们无法在测试源代码中定义一个套件,但它可以由一个 XML 文件表示,因为套件是执行的功能。 它还允许灵活配置要运行的测试。

  • 套件可以包含一个或多个测试,并由<suite>标记定义。<suite>是 testng.xml 的根标记。 它描述了一个测试套件,它又由几个<test>部分组成。


下表列出了<suite>接受的所有定义的合法属性。

依赖测试

可能需要以特定顺序调用测试用例中的方法,或者可能希望在方法之间共享一些数据和状态。 TestNG 支持这种依赖关系,因为它支持在测试方法之间显式依赖的声明。


TestNG 允许指定依赖关系:


  • 在 @Test 注释中使用属性 dependsOnMethods

  • 在 @Test 注释中使用属性 dependsOnGroups


除此之外依赖还分为 hard 依赖和 soft 依赖:


  • hard 依赖:默认为此依赖方式,即其所有依赖的 methods 或者 groups 必须全部 pass,否则被标识依赖的类或者方法将会被略过,在报告中标识为 skip,如后面的范例所示,此为默认的依赖方式;

  • soft 依赖:此方式下,其依赖的方法或者组有不是全部 pass 也不会影响被标识依赖的类或者方法的运行,注意如果使用此方式,则依赖者和被依赖者之间必须不存在成功失败的因果关系,否则会导致用例失败。

  • 此方法在注解中需要加入 alwaysRun=true 即可,如 @Test(dependsOnMethods= {"TestNgLearn1"}, alwaysRun=true);


在 TestNG 中,我们使用 dependOnMethods 和 dependsOnGroups 来实现依赖测试。 且这两个都支持正则表达式,如范例三所示,如下为几个使用范例


范例一,被依赖方法 pass:


public class TestCase1 {    @Test(enabled=true)    public void TestNgLearn1() {        System.out.println("this is TestNG test case1");    }    @Test(dependsOnMethods= {"TestNgLearn1"})    public void TestNgLearn2() {        System.out.println("this is TestNG test case2");    }}
复制代码


运行结果:


this is TestNG test case1this is TestNG test case2PASSED: TestNgLearn1PASSED: TestNgLearn2
复制代码


范例二,被依赖方法 fail:


public class TestCase1 {    @Test(enabled=true)    public void TestNgLearn1() {        System.out.println("this is TestNG test case1");        Assert.assertFalse(true);    }    @Test(dependsOnMethods= {"TestNgLearn1"})    public void TestNgLearn2() {        System.out.println("this is TestNG test case2");    }}
复制代码


结果:


this is TestNG test case1FAILED: TestNgLearn1junit.framework.AssertionFailedError  at junit.framework.Assert.fail(Assert.java:47)  at junit.framework.Assert.assertTrue(Assert.java:20)  at junit.framework.Assert.assertFalse(Assert.java:34)  at junit.framework.Assert.assertFalse(Assert.java:41)  at com.demo.test.testng.TestCase1.TestNgLearn1(TestCase1.java:26)  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)  at java.lang.reflect.Method.invoke(Method.java:498)  at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:104)  at org.testng.internal.Invoker.invokeMethod(Invoker.java:645)  at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:851)  at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:1177)  at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:129)  at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:112)  at org.testng.TestRunner.privateRun(TestRunner.java:756)  at org.testng.TestRunner.run(TestRunner.java:610)  at org.testng.SuiteRunner.runTest(SuiteRunner.java:387)  at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:382)  at org.testng.SuiteRunner.privateRun(SuiteRunner.java:340)  at org.testng.SuiteRunner.run(SuiteRunner.java:289)  at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52)  at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:86)  at org.testng.TestNG.runSuitesSequentially(TestNG.java:1293)  at org.testng.TestNG.runSuitesLocally(TestNG.java:1218)  at org.testng.TestNG.runSuites(TestNG.java:1133)  at org.testng.TestNG.run(TestNG.java:1104)  at org.testng.remote.AbstractRemoteTestNG.run(AbstractRemoteTestNG.java:114)  at org.testng.remote.RemoteTestNG.initAndRun(RemoteTestNG.java:251)  at org.testng.remote.RemoteTestNG.main(RemoteTestNG.java:77)
SKIPPED: TestNgLearn2
复制代码


范例三、group 依赖:


如下所示,method1 依赖 group 名称为 init 的所有方法:


@Test(groups = { "init" })public void serverStartedOk() {} @Test(groups = { "init" })public void initEnvironment() {} @Test(dependsOnGroups = { "init.*" })public void method1() {}
复制代码


这里 init 这个 group 中的两个方法的执行顺序如果没有在 xml 中指明则每次运行的顺序不能保证

参数化测试

TestNG 中的另一个有趣的功能是参数化测试。 在大多数情况下,您会遇到业务逻辑需要大量测试的场景。 参数化测试允许开发人员使用不同的值一次又一次地运行相同的测试。


TestNG 可以通过两种不同的方式将参数直接传递给测试方法:

使用 testng.xml

使用数据提供者


下面分别介绍两种传参方式:


  1. 使用 textng.xml 传送参数


范例代码如下:


public class TestCase1 {    @Test(enabled=true)    @Parameters({"param1", "param2"})    public void TestNgLearn1(String param1, int param2) {        System.out.println("this is TestNG test case1, and param1 is:"+param1+"; param2 is:"+param2);        Assert.assertFalse(false);    }    @Test(dependsOnMethods= {"TestNgLearn1"})    public void TestNgLearn2() {        System.out.println("this is TestNG test case2");    }}
复制代码


xml 配置:


<?xml version="1.0" encoding="UTF-8"?><suite name="Suite" parallel="false">  <test name="Test">    <parameter name="param1" value="1011111" />    <parameter name="param2" value="10" />    <classes>      <class name="com.demo.test.testng.TestCase1"/>    </classes>  </test> <!-- Test --></suite> <!-- Suite -->
复制代码


运行 xml,结果如下:


this is TestNG test case1, and param1 is:1011111; param2 is:10this is TestNG test case2
===============================================SuiteTotal tests run: 2, Failures: 0, Skips: 0===============================================
复制代码
使用 @DataProvider 传递参数

此处需要注意,传参的类型必须要一致,且带有 @DataProvider 注解的函数返回的必然是 Object[][],此处需要注意。


代码如下:


public class TestCase1 {    @DataProvider(name = "provideNumbers")    public Object[][] provideData() {        return new Object[][] { { 10, 20 }, { 100, 110 }, { 200, 210 } };    }    @Test(dataProvider = "provideNumbers")    public void TestNgLearn1(int param1, int param2) {        System.out.println("this is TestNG test case1, and param1 is:"+param1+"; param2 is:"+param2);        Assert.assertFalse(false);    }    @Test(dependsOnMethods= {"TestNgLearn1"})    public void TestNgLearn2() {        System.out.println("this is TestNG test case2");    }}
复制代码


运行此 class,结果为:


this is TestNG test case1, and param1 is:10; param2 is:20this is TestNG test case1, and param1 is:100; param2 is:110this is TestNG test case1, and param1 is:200; param2 is:210this is TestNG test case2PASSED: TestNgLearn1(10, 20)PASSED: TestNgLearn1(100, 110)PASSED: TestNgLearn1(200, 210)PASSED: TestNgLearn2
复制代码

XML 配置文件说明

前面讲的大多都是以测试脚本为基础来运行的,少部分是以 xml 运行,这里以 xml 来讲解下:


<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" ><suite name="SuiteName" verbose="1" > 
复制代码
如下分别讲解各个标签:

suite 标签


testNG.xml 文件的最外层标签即 suite,即测试套件,其下可以有多个<test>和<groups>,其有几个可以添加的属性在第十节的分 suite 测试中有做说明,这里做下详细说明


  1. name 属性


此属性属于必须要有的,值可以自行设定,此名字会在 testNG 的报告中看到;


  1. verbose 属性


此属性为指定 testNG 报告的详细程度,从 0 开始到 10,其中 10 为最详细,默认生成的 xml 此属性值为 1;


  1. parallel 属性


  • 此属性是指代运行方式,默认为 none,即串行运行方式;并行执行方法包括如下几种,下面做分别说明:

  • methods:方法层级,若为此值,则该 suite 下所有的测试方法都将进行多线程,即测试用例级别的多线程。如果用例之间有依赖,则执行顺序会按照设定的依赖来运行;


  <suite name="My suite" parallel="methods" thread-count="5">
复制代码


- tests:TestNG将在同一线程中运行相同的<Test>标签中的所有方法,每个<test>标签都将处于一个单独的线程中,这允许您将不是线程安全的所有类分组在同一个<test>中,并保证它们都将在同一个线程中运行,同时利用TestNG使用尽可能多的线程运行测试。
复制代码


  <suite name="My suite" parallel="tests" thread-count="5">
复制代码


- classes:类级别并发,即TestNG会将该suite下每个class都将在单独的线程中运行,同一个class下的所有用例都将在同一个线程中运行;
复制代码


  <suite name="My suite" parallel="classes" thread-count="5">
复制代码


- instances:实例级别,即TestNG将在同一线程中运行同一实例中的所有方法,两个不同实例上的两个方法将在不同的线程中运行。
复制代码


  <suite name="My suite" parallel="instances" thread-count="5">
复制代码


  1. thread-count 属性


此属性用于指定线程数,按照需要输入,需要 parallel 参数非 none 时才可以添加;


  1. annotations 属性


此项为注解的级别,为 methods 级别和 class 级别,一般不用设置;


  1. time-out 属性


此属性用于指定超时时间,该 suite 下所有的用例的超时时间;


  1. group-by-instances 属性


此项用于那些有依赖的方法,且被依赖的对象有多个重载对象,因为如果是依赖方法,且该方法有多个重载方法,则默认是会将所有重载方法都跑完再运行被依赖方法,但有时候我们不想这样,则将此项设置为 true 即可;


  1. preserve-order 属性


值可输入 true 或者 false,如果为 true,则用例执行会按照在 xml 中的顺序执行,否则会乱序执行,不添加此属性的话默认是按顺序执行的;


2、test 标签


此标签无特别意义,其下可以包括多个标签,如 groups、classes 等,如下介绍下几种书写方式:


选择一个包中的全部测试脚本(包含子包)


<test name = "allTestsInAPackage" >   <packages>      <package name = "whole.path.to.package.* />   </packages></test>
复制代码


选择一个类中的全部测试脚本


<test name = "allTestsInAClass" >   <classes>  <class name="whole.path.to.package.className />   </classes></test>
复制代码


选择一个类中的部分测试脚本


<test name = "aFewTestsFromAClass" >   <classes>  <class name="whole.path.to.package.className >      <methods>         <include name = "firstMethod" />         <include name = "secondMethod" />         <include name = "thirdMethod" />      </methods>  </class>   </classes></test>
复制代码


选择一个包中的某些组


<test name = "includedGroupsInAPackage" >   <groups>      <run>         <include name = "includedGroup" />      </run>   </groups>   <packages>      <package name = "whole.path.to.package.* />   </packages></test>
复制代码


排除一个包中的某些组


<test name = "excludedGroupsInAPackage" >   <groups>      <run>         <exclude name = "excludedGroup" />      </run>   </groups>   <packages>      <package name = "whole.path.to.package.* />   </packages></test>
复制代码
group 标签

此标签必然是在<test>标签下的,用于标识那些组会被用于测试或者被排除在测试之外,其同级必然要包含一个<classes>标签或者<pakages>标签,用于指定 groups 来自于哪些包或者类;如下即为包含一个 group,排除一个 group 的例子:


<groups>  <run>     <include name = "includedGroupName" />     <exclude name = "excludedGroupName" />  </run></groups>
复制代码


高级应用:


<test name="Regression1">  <groups>    <define name="functest">      <include name="windows"/>      <include name="linux"/>    </define>      <define name="all">      <include name="functest"/>      <include name="checkintest"/>    </define>      <run>      <include name="all"/>    </run>  </groups>    <classes>    <class name="test.sample.Test1"/>  </classes></test>
复制代码

其他

其他的话就是测试脚本的选择了,有三种方式:


选择一个包


<packages>    <package name = "packageName" /></packages>
复制代码


选择一个类


<classes>    <class name = "className" /></classes>
复制代码


选择一个方法


<classes>    <class name = "className" />       <methods>          <include name = "methodName" />       </methods>    </class></classes>
复制代码


这里也支持正则表达式,例如:


<test name="Test1">  <classes>    <class name="example1.Test1">      <methods>        <include name=".*enabledTestMethod.*"/>        <exclude name=".*brokenTestMethod.*"/>      </methods>     </class>  </classes></test>
复制代码


发布于: 2 小时前阅读数: 2
用户头像

🏆2021年InfoQ写作平台-签约作者 🏆 2020.03.25 加入

👑【酷爱计算机技术、醉心开发编程、喜爱健身运动、热衷悬疑推理的”极客狂人“】 🏅 【Java技术领域,MySQL技术领域,APM全链路追踪技术及微服务、分布式方向的技术体系等】 我们始于迷惘,终于更高水平的迷惘

评论

发布
暂无评论
☕【Java技术指南】「TestNG专题」单元测试框架之TestNG使用教程指南(下)