软件测试 / 测试开发 | TestNG 与 Junit 对比,测试框架如何选择?
获取更多相关知识
本文为霍格沃兹测试学院学院学员课程学习笔记。
TestNG 和 Junit 作为两大流行的测试框架,有哪些区别?各有哪些优缺点?该如何选择呢?这里简要总结下:
Junit 更适合隔离性比较强的单元测试;
TestNG 是比 Junit 涵盖功能更全面的测试框架,具有参数化和分组的特性,可以做数据驱动;
TestNG 被设计应用覆盖所有的测试,单元、功能、端到端、集成测试等;
TestNG 依赖测试时对于依赖方法失败后的用例标记为跳过,而不是像 Junit 标记为失败,减少失败原因排查成本;
TestNG 可以针对失败用例回归测试,增加测试针对性和效率,而 Junit 需要将所有测试用例重新执行;
TestNG 更适合测试工程师需要的大范围的复杂的集成测试;
注:以上对比指的是 TestNG 和 Junit4 版本的对比,最新的 Junit5 框架已经完善了功能特性,也涵盖了 TestNG 所包含的功能,如果选择 Junit 框架,建议直接选用 Junit5,Junit5 还在推广普及中;具体可参考《Junit5 简介、构成、新特性及基本使用-常用注解、套件执行》

---Junit 与 TestNG 的注解区别---
项目测试实操演练
下面通过项目实操讲解二者区别。做项目测试之前,需要先添加 maven 依赖,如下图所示:

1.基本测试
Junit 和 TestNG 对于测试方法的标注都是@Test
,另外在方法执行前后加上@BeforeMethod
、@AfterMethod

测试结果:

由测试结果可看到在每个测试用例执行前都会先执行@BeforeMethod
注解的方法,之后都会执行@AfterMethod
注解的方法
2.@BeforeClass 和 @AfterClass
在测试类运行之前运行@BeforeClass
和@AfterClass

测试结果:

由测试结果可以看出在测试类执行前后会先后执行一次被@BeforeClass
和@AfterClass
注解的方法
3.套件测试
TestNG 的套件管理有点“特别”,它是以一个 xml 文件作为统一配置文件的,一般会命名为testNG.xml
,实际上文件的命名随意,you happy just ok!
执行时通过运行 xml 文件
最基本的套件管理规则:suite
->test
->classes
->class
同一个test
下的测试类看做是一个整体,其中的注解对整个test
整体都是生效的
下面看实操演示,当前有 3 个测试类SuiteTest1
、SuiteTest2
和SuiteTestConfig

在 resource 下创建套件配置文件 testNGSuite.xml

3.1 套件测试之 @BeforeSuite 和 @AfterSuite1)在SuiteTest1
、SuiteTest2
测试类中分别输入测试用例:

2)再在SuiteTestConfig
测试类中输入@BeforeSuite
和@AfterSuite
注解方法和@Test
方法

3)最后在配置文件testNGSuite.xml
中配置套件执行顺序将SuiteTest1
、SuiteTestConfig
"包"成一个test
整体,SuiteTest2
、SuiteTestConfig
"包"成一个test
整体;然后依顺序执行注:suite 和 test Tag 需要给一个 name,否则会报错

测试结果:

从测试结果我们可以看到@BeforeSuite
和@AfterSuite
仅仅在 suite 执行前后分别执行一次
3.2 套件测试之 @BeforeTest+@AfterTest 在测试类SuiteTestConfig
中输入@BeforeTest
+@AfterTest
注解的方法,xml 套件配置不变

测试结果:

由测试结果可以看到,在每个test
执行前后都会先后执行一次由@BeforeTest
、@AfterTest
注解的方法
4.忽略测试 @Test(enable=false)
在测试方法 test1 的注解中设置属性enable=false

测试结果:

由测试结果可以看到 test1 被忽略了,并没有执行
5.分组测试
5.1 方法分组之@Test(groups=“xxx”)
、@BeforeGroups
+@AfterGroups
分别将方法test1
和test2
分为“测试1组
”和“测试2组
”
再在测试1组
执行前执行@BeforeGroups
注解方法,在测试2组
执行后执行@AfterGroups
注解方法

测试结果:

5.2 测试类分组 @Test(groups=“xxx”)
当前有 3 个测试类 ClassGroups1Test
、ClassGroups2Test
、ClassGroups3Test

1)分别将这 3 个测试类进行分组Group1
、Group2
、Group3



2)将这 3 个测试类以 3、2、1 的执行顺序引入 xml 套件配置文件

3)设置场景,利用配置<groups>
-><run>
-><include>/<exclude>
让Group1
和Group3
执行,Group2
不执行(实际上如果<groups>
中直接不写Group2
,它也不会执行)

xml 套件配置文件呈现结果:

测试结果:

由测试结果可以看到,测试类分组在Group3
和Group1
的方法依次执行了,而Group2
分组中的测试类未被执行
注:如果测试用例的逻辑顺序设计的较合理,平常使用分组的频率可能没那么高
6.异常测试
测试时,我们可能期望的结果就是抛出某种异常,比如单元测试时输入非法入参,程序期望抛出异常,而这是期望的正确结果,我们希望用例是测试通过的,这时就需要用到异常测试注解:@Test(exceptedExceptions = XXXException.class)

测试结果:

注:单元测试平常更多的可能由研发人员自己完成,一般功能和接口测试我们测试工程师期待的都是后端对异常处理后返回的一个状态码 code 和 message 信息
7.依赖测试
有时候一个用例的执行要依赖其他用例的执行结果,例如购买商品前需要依赖用户登录成功才可以,这个时候就需要使用@Test(dependsOnMethods = {"funtion name"})
对另一个用例进行依赖

7.1 依赖用例成功
然后我们直接运行 pay 方法,结果如下:

由测试结果我们可以看到虽然我们直接执行了pay
方法,但是由于pay
方法是依赖于login
方法的,所以会先执行login
方法
7.2 依赖用例失败
我们让被依赖的 login 用例执行失败,直接运行pay
方法,观察结果:

测试结果:

由测试结果可以看到几点现象:
被依赖的用例执行失败,后面的用例会直接跳过忽略
测试结果显示为忽略而不是失败,这样当有成百上千条用例因为被依赖的用例失败而执行不通过时,可以只排查被依赖用例失败原因即可;否则如 Junit4 全部标记为失败的话会造成排查问题和回归测试效率的极大浪费
8.参数化测试
有的方法需要传参,好比登录成功时我们需要用户的姓名和 ID 号
参数的传递直接写在代码中不利于维护更改,也不方便不懂代码的测试人员进行参数修改,这个时候就需要参数化测试
8.1 参数化测试 1:@Parameters+<parameter name="xx" value="xxx"/>
这个时候就需要在方法上加上注解@Parameters
,并在 xml 配置文件中利用<parameter name="xx" value="xxx"/>
的方式传参
注:也可用 tag 对指定的方法进行参数传递

xml 里完成传参:

测试结果:

8.2 参数化测试 2:@Test(dataProvider = “name”)+@DataProvider1)利用@Test(dataProvider = "name")
+@DataProvider(name="name")
将多组数据传递到一个方法中依次执行

测试结果:

2)利用@Test(dataProvider = "name")
+@DataProvider(name="name")
指定测试方法,传递指定入参进行测试

分别单独运行方法userInfo1
和userInfo2
得到测试结果:userInfo1:

userInfo2:

9.多线程测试
9.1 多线程测试注解实现 @Test(invocationCount=10,threadPoolSize=4)参数说明:官方给出的解释是如下

简单来说就是:
invocationCount
表示方法要运行几次,threadPoolSize
表示线程池大小,且要配合invocationCount
才起作用。现在将userInfo1
方法用多线程执行 10 次,线程池大小设为 4,打印当前线程 id 以观察验证

测试结果:

从测试结果中可以看到 4 个不同的线程一共将方法userInfo1
执行了 10 次
9.2 多线程测试 xml 实现:parallel(methods|tests|classes)+thread-count 参数解释:官方文档的解释如下:


parallel(methods|tests|classes)
:设置使用多线程,且有methods|tests|classes
三种不同级别选择
methods
: 所有用例都可以在不同的线程下执行,包括依赖的用例tests
: 同一个中的用例运行在同一个线程下,不同中的用例可以运行在不同线程下classes
:同一个中的用例运行在同一个线程下,不同中的用例可以运行在不同线程下
1)创建 3 个方法,打印线程 ID

2)三种不同级别选择
methods-所有用例都可以在不同的线程下执行
设置parallel
为methods
级别,thread-count
为 3,进行测试

测试结果:

2.2)tests-同一个<test>
中的用例运行在同一个线程下,不同<test>
中的用例可以运行在不同线程下再创建测试类ThreadTest
,添加三个方法并打印 thread ID

设置parallel
为tests
级别,thread-count
为 3,进行测试

测试结果:

2.3)classes-同一个<class>
中的用例运行在同一个线程下,不同<class>
中的用例可以运行在不同线程下
设置parallel
为classes
级别,thread-count
为 3,进行测试

测试结果:

注:虽然框架本身说明了是多线程安全的,但是由于我们自身编码可能不能保证严格规范,容易造成多线程不安全,所以建议不要适用多线程测试,而是适用多进程测试
参考链接:
TestNG 官方说明:https://testng.org/doc/index.html
IBM Developer:https://www.ibm.com/developerworks/cn/java/j-cq08296/?mhsrc=ibmsearch_a&mhq=testNG
获取更多相关知识
版权声明: 本文为 InfoQ 作者【测试人】的原创文章。
原文链接:【http://xie.infoq.cn/article/841a812f354f8752e734004c0】。文章转载请联系作者。
评论