当前位置:网站首页>如何利用 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/]
边栏推荐
猜你喜欢

法国A+ 法国VOC标签最高环保级别

Full recharge, im+rtc+x full communication service "feedback season" starts

German agbb VOC hazardous substances test

DTD modeling

Swin-Transformer(2021-08)

Cobbler轻松上手

Dlib library for face key point detection (openCV Implementation)

Video content production and consumption innovation

Kalman filter -- Derivation from Gaussian fusion

Redis入门到精通01
随机推荐
NBI visual platform quick start tutorial (V) introduction to editor functions and operations
Small program container technology to promote the operation efficiency of the park
新版EasyGBS如何配置WebRTC视频流格式播放?
Year after year, why is breaking the data island still the primary task of enterprise development
Word -- a solution for word to encounter errors when trying to open a file
华兴证券:混合云原生架构下的 Kitex 实践
PO模式简介「建议收藏」
Cloud Native Landing Practice Using rainbond for extension dimension information
全栈代码测试覆盖率及用例发现系统的建设和实践
Sqlserver SQL Server Management Studio and transact SQL create accounts and create read-only users to access the specified database
传统微服务框架如何无缝过渡到服务网格 ASM
slice
Swin-transformer --relative positional Bias
slice
拓维信息使用 Rainbond 的云原生落地实践
Where do the guests come from
torch stack() meshgrid()
基于UDP协议设计的大文件传输软件
Go Redis连接池
Rust 如何实现依赖注入?