当前位置:网站首页>如何利用 xUnit 框架对测试用例进行维护?
如何利用 xUnit 框架对测试用例进行维护?
2022-06-30 18:00:00 【华为云】
本文为霍格沃兹测试学院优秀学员 Junit 学习笔记。测试开发技能进阶,文末加群。
1、xUnit 是什么
先看 Wikipedia 上的解释:

xUnit 是一系列测试框架的统称,最开始来源于一个叫做 Smalltalk 的 SUnit 框架,现在各种面向对象的语言,如 Java、Python 的鼻祖就是 Smalltalk,后来这些语言都借助了 Sunit 框架的理念,有很多通用的规范和特征,也就统称为 xUnit。
1.1 xUnit 框架体系
Java : JUnit、TestNG
Python : UnitTest、PyTest
1.2 xUnit 的共同特征
Test Runner :测试的运行器
Test Case :测试用例
Test Fixtures : 测试夹具 / 治具,用来管理测试用例的执行
Test Suites :测试套件,用来编排测试用例
Test Execution:测试执行,以何种顺序执行
Test Result Formatter:测试结果,具备相同的格式,可被整合
Assertions:断言
2、从 Junit4 开启 xUnit 框架之旅
2.1 为何从 Junit4 开始
Junit4 仍然是 99% 的研发工程师的首选框架,方便测试工程师与研发工程师交流(拉关系~~);
TestNG 的使用多用于测试工程师;
Junit5 还未大规模普及(最推荐的框架,成熟、好用、研发测试通用);
很多框架基于 Junit4 定制;
2.2 测试用例的核心元素
测试用例的名字:特性方法名
测试用例描述与标签:注解
测试用例的容器:类或者套件
测试过程
单元测试
Web 自动化测试 Selenium
App 自动化测试 Appium
接口自动化测试 RestAssured
测试断言
2.3 基本 demo 运行
1)创建 maven 工程 XUnit,pom.xml 中添加 Junit 依赖;
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope></dependency>2)src/test/java 下创建测试类 Junit4DemoTest
注意
测试类要以 Test 开头或者结尾
maven auto-import
src/main/java 存放应用实现代码
src/test/java 存放单元测试
单元测试的原则之一:用例可以独立运行
基本测试 demo 运行:

运行结果:

2.4 用例间的执行顺序
Junit4:
Default 取决于反射方法获得的列表,顺序固定(不保险)
@FixMethodOrder(MethodSorters.JVM) 顺序可能变化
@FixMethodOrder(MethodSorters.NAME_ASCENDING) 按照名字 ASCII 顺序(稳定常用,建议使用)
TestNG、Junit5:
可以通过注解设置顺序 Order
顺序演示

运行结果:

2.5 测试套件的执行顺序支持

Junit4:
@BeforeClass、@AfterClass
@Before、@After
TestNG:
@BeforeClass
@BeforeMethod
BeforeGroup、@BeforeSuite
Junit5:
@BeforeClass
@BeforeEach
实操演示 1
在用例执行前后增加 @Before 和 @After:

运行结果:

实操演示 2
再增加 @BeforeClass 和 @AfterClass

运行结果:

2.5 用例管理的实际应用举例——App 自动化测试用例管理
基类的 @BeforeClass:
配置读取、配置 Capability、初始化 driver、安装 App,PageObject 初始化
集成的子类执行流程
@Before:启动并进入特定界面
@Test:测试用例执行
@After:回退到入口
@BeforeClass:进图特定的 tab 子功能页面
@AfterClass:关闭 app
基类的 @AfterClass
driver.quit
2.6 继承关系下的测试流程
流程顺序:
父类 @BeforeClass
子类 @BeforeClass
父类 @Before
子类 @Before
子类 @Test
父类 @Test
子类 @After
父类 @After
子类 @AfterClass
父类 @AfterClass
实操演示 1
现在创建一个子类 Junit4DemoChildrenTest,继承 Junit4DemoTest,然后实现和父类一样的方法并运行子类:

运行结果:

从运行结果中我们可以看到,子类会将与父类中一样的方法进行覆盖,只执行子类中的方法
实操演示 2
现在将子类中的方法名进行修改,使其与父类方法名不同,再运行子类:

运行结果:
我是 @BeforeClass,我是第一步我是 [email protected],我是第一步我是 @Before,用例执行前先到我这我是 [email protected],用例执行前先到我这Children testDemoB我是 [email protected], 用例执行后到我这我是 @After, 用例执行后到我这我是 @Before,用例执行前先到我这我是 [email protected],用例执行前先到我这Children testDemoA我是 [email protected], 用例执行后到我这我是 @After, 用例执行后到我这java.lang.AssertionError at org.junit.Assert.fail(Assert.java:86) at org.junit.Assert.assertTrue(Assert.java:41) at org.junit.Assert.assertTrue(Assert.java:52) ... at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47) at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242) at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)我是 @Before,用例执行前先到我这我是 [email protected],用例执行前先到我这Children testDemoC我是 [email protected], 用例执行后到我这我是 @After, 用例执行后到我这我是 @Before,用例执行前先到我这我是 [email protected],用例执行前先到我这testDemoA我是 [email protected], 用例执行后到我这我是 @After, 用例执行后到我这java.lang.AssertionError at org.junit.Assert.fail(Assert.java:86) at org.junit.Assert.assertTrue(Assert.java:41) at org.junit.Assert.assertTrue(Assert.java:52) ... at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242) at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)我是 @Before,用例执行前先到我这我是 [email protected],用例执行前先到我这testDemoB我是 [email protected], 用例执行后到我这我是 @After, 用例执行后到我这我是 @Before,用例执行前先到我这我是 [email protected],用例执行前先到我这testDemoC我是 [email protected], 用例执行后到我这我是 @After, 用例执行后到我这我是 [email protected],我是最后一步我是 @AfterClass,我是最后一步2.7 测试套件
RunWith
SuiteClasses
class
实操演示
新建一个子类 Junit4DemoChildren2Test,继承 Junit4DemoTest

再建一个测试类 SuitesTest, 写上注解 @RunWith(Suite.class), 表明这是一个测试套件,是多个测试类的一个集合,一个容器;
然后利用注解 @Suite.SuiteClasses 来设置测试类集合,设置测试类执行的顺序

运行结果:

我是 @Before,用例执行前先到我这我是 [email protected],用例执行前先到我这Children2 testDemoC我是 [email protected], 用例执行后到我这我是 @After, 用例执行后到我这我是 @Before,用例执行前先到我这我是 [email protected],用例执行前先到我这Children2 testDemoB我是 [email protected], 用例执行后到我这我是 @After, 用例执行后到我这我是 @Before,用例执行前先到我这我是 [email protected],用例执行前先到我这Children2 testDemoA我是 [email protected], 用例执行后到我这我是 @After, 用例执行后到我这java.lang.AssertionError at org.junit.Assert.fail(Assert.java:86) at org.junit.Assert.assertTrue(Assert.java:41) ... at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242) at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)我是 @Before,用例执行前先到我这我是 [email protected],用例执行前先到我这testDemoA我是 [email protected], 用例执行后到我这我是 @After, 用例执行后到我这java.lang.AssertionError at org.junit.Assert.fail(Assert.java:86) at org.junit.Assert.assertTrue(Assert.java:41) ... at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242) at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)我是 @Before,用例执行前先到我这我是 [email protected],用例执行前先到我这testDemoB我是 [email protected], 用例执行后到我这我是 @After, 用例执行后到我这我是 @Before,用例执行前先到我这我是 [email protected],用例执行前先到我这testDemoC我是 [email protected], 用例执行后到我这我是 @After, 用例执行后到我这我是 [email protected],我是最后一步我是 @AfterClass,我是最后一步我是 @BeforeClass,我是第一步我是 @Before,用例执行前先到我这testDemoA我是 @After, 用例执行后到我这java.lang.AssertionError at org.junit.Assert.fail(Assert.java:86) at org.junit.Assert.assertTrue(Assert.java:41) ... at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)我是 @Before,用例执行前先到我这testDemoB我是 @After, 用例执行后到我这我是 @Before,用例执行前先到我这testDemoC我是 @After, 用例执行后到我这我是 @AfterClass,我是最后一步我是 @BeforeClass,我是第一步我是 [email protected],我是第一步我是 @Before,用例执行前先到我这我是 [email protected],用例执行前先到我这Children testDemoB我是 [email protected], 用例执行后到我这我是 @After, 用例执行后到我这我是 @Before,用例执行前先到我这我是 [email protected],用例执行前先到我这Children testDemoA我是 [email protected], 用例执行后到我这我是 @After, 用例执行后到我这java.lang.AssertionError at org.junit.Assert.fail(Assert.java:86) at org.junit.Assert.assertTrue(Assert.java:41) at org.junit.Assert.assertTrue(Assert.java:52) ... at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)我是 @Before,用例执行前先到我这我是 [email protected],用例执行前先到我这Children testDemoC我是 [email protected], 用例执行后到我这我是 @After, 用例执行后到我这我是 @Before,用例执行前先到我这我是 [email protected],用例执行前先到我这testDemoA我是 [email protected], 用例执行后到我这我是 @After, 用例执行后到我这java.lang.AssertionError at org.junit.Assert.fail(Assert.java:86) at org.junit.Assert.assertTrue(Assert.java:41) ... at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)我是 @Before,用例执行前先到我这我是 [email protected],用例执行前先到我这testDemoB我是 [email protected], 用例执行后到我这我是 @After, 用例执行后到我这我是 @Before,用例执行前先到我这我是 [email protected],用例执行前先到我这testDemoC我是 [email protected], 用例执行后到我这我是 @After, 用例执行后到我这我是 [email protected],我是最后一步我是 @AfterClass,我是最后一步我是 @BeforeClass,我是第一步我是 [email protected],我是第一步由测试结果可以看到使用套件后,测试过程为 Junit4DemoChildren2Test、Junit4DemoTest、Junit4DemoChildrenTest 的顺序执行
2.8 分组测试[email protected]
有时候我们需要对一些特定的用例进行分组测试,这个时候就可以用@Category来实现
另外在套件执行类上运用注解:
@RunWith(Categories.class) : 固定写法,指明以 Category 方式分组@Categories.IncludeCategory(SlowGroup.class) : 指明要执行的测试分组包含哪些@Categories.ExcludeCategory(FastGroup.class) : 指明要执行的测试分组不包含哪些@Suite.SuiteClasses({ : 指明要执行的测试类 TestDemo.class})@Category分组需要给定一个标签,以类或者接口都可以,这里创建连个接口SlowGroup和FastGrouppublic interface FastGroup {}public interface SlowGroup {}在用例上分别分组为
SlowGroup、FastGroup和SlowGroup+FastGroup

指明
SlowGroup组测试执行,FastGroup组的测试不执行:

测试结果:

仅指明
SlowGroup组测试执行

测试结果:

仅指明不执行的组为
FastGroup

测试结果:

2.9 参数化 @Paramterized
有时候我们需要传入测试数据,且数据可能是多组,这个时候就需要使用参数化来传入多组数据进行测试
Junit4 的参数化稍微有点麻烦:
1)先在类名上加入注解@RunWith(Parameterized.class)表明要以参数化运行

2)用注解@Parameterized.Parameters来设定数据源

3)最后用注解 @Parameterized.Parameter 来指定数据源数据对应的参数

4)总览

测试结果:

从测试结果可以看到 3 组参数分别传入方法中,方法各执行了一次,完成参数化测试
3、总结-测试用例的顺序
测试用例之间的顺序
test fixtures 的顺序
继承顺序
套件之间的顺序
参考文档链接
JUnit4 单元测试框架 [https://junit.org/junit4/]
JUnit5 单元测试框架 [https://junit.org/junit5/]
边栏推荐
- [Collection - industry solutions] how to build a high-performance data acceleration and data editing platform
- Swin-transformer --relative positional Bias
- PC wechat multi open
- ArcGIS无插件加载(无偏移)天地图
- 10 statistical methods commonly used for "dry goods" data analysis, with key application scenarios attached
- Reading notes of "high EQ means being able to talk"
- Entry node of link in linked list - linked list topic
- 4个技巧告诉你,如何使用SMS促进业务销售?
- Swin-Transformer(2021-08)
- Ansi/ul 94 class 5-V vertical combustion test
猜你喜欢

3.10 haas506 2.0开发教程-example-TFT

嵌入式软件开发新趋势:DevOps

Leader: who can use redis expired monitoring to close orders and get out of here!

Swin-Transformer(2021-08)

Coding officially entered Tencent conference application market!

Entropy - conditional entropy - joint entropy - mutual information - cross entropy

3.10 haas506 2.0 development tutorial example TFT

PC wechat multi open

Cloud Native Landing Practice Using rainbond for extension dimension information

Four tips tell you how to use SMS to promote business sales?
随机推荐
Vs common shortcut key commands
音频 librosa 库 与 torchaudio 库中 的 Mel- spectrogram 进行对比
删除排序链表中的重复元素 II[链表节点统一操作--dummyHead]
Go redis connection pool
Courage to be hated: Adler's philosophy class: the father of self inspiration
3.10 haas506 2.0开发教程-example-TFT
Redis入门到精通01
拓维信息使用 Rainbond 的云原生落地实践
torch stack() meshgrid()
Four tips tell you how to use SMS to promote business sales?
PO模式简介「建议收藏」
年复一年,为什么打破数据孤岛还是企业发展的首要任务
Can go struct in go question bank · 15 be compared?
嵌入式软件开发新趋势:DevOps
NBI visual platform quick start tutorial (V) introduction to editor functions and operations
Practical application of "experience" crawler in work "theory"
Development: how to install offline MySQL in Linux system?
Some interesting modules
Full recharge, im+rtc+x full communication service "feedback season" starts
Pytorch learning (III)