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 -->
运行结果如下:
beforeSuite
beforeTest
beforeClass
beforeGroups
beforeMethod
test1 from group1
afterMethod
beforeMethod
test11 from group1
afterMethod
afterGroups
beforeMethod
test2 from group2
afterMethod
afterClass
this is TestNG test case2
afterTest
afterSuite

如上所示,先运行了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 case1
this is TestNG test case2
PASSED: TestNgLearn1
PASSED: 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 case1
FAILED: TestNgLearn1
junit.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:10
this is TestNG test case2

===============================================
Suite
Total 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:20
this is TestNG test case1, and param1 is:100; param2 is:110
this is TestNG test case1, and param1 is:200; param2 is:210
this is TestNG test case2
PASSED: 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>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,287评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,346评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,277评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,132评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,147评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,106评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,019评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,862评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,301评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,521评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,682评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,405评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,996评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,651评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,803评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,674评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,563评论 2 352

推荐阅读更多精彩内容