注解 | 说明 |
---|---|
@DisplayName |
自定义显示名称;类和方法上都可使用 |
@DisplayNameGeneration |
定义显示名称规则;类上都使用 |
@Disable |
禁用测试;类和方法上都可使用 |
@Tag |
标记测试;类和方法上都可使用 |
@DisplayName
使用
@DisplayName
注解不但可以放在测试方法上也可以放在测试类的上面。是一个类和方法都可以使用的注解。
eg : @DisplayName("This is a displayname annotation test")
对应括号内是string
类型的字符串就是对应的显示内容,string类型内可以填写包含空格、特殊字符甚至表情符号😊。
eg : @DisplayName("😊计算器测试用例 ╯°□°)╯")
作用
作用就是自定义显示名称,自己想怎么显示就怎么显示。
一般情况下的作用就是在 IDE 和构建工具中的测试报告中自定义显示的类名和方法名,当然可以和allure
报告结合,在allure
报告中同样显示对应方法名和类名
总结
总结为以下几点:
- 带注释的测试类或测试方法声明自定义显示名称
- 通常用于 IDE 和构建工具中的测试报告
- 可包含空格、特殊字符甚至表情符号😊
- 结合Allure报告,可以显示对应方法名和类名
package top.testeru.common;
import top.testeru.util.BaseTest;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* @program: tutorials
* @author: testeru.top
* @description: 常用注解测试:显示名称
* DisplayName:类+方法注解
* @Version 1.0
* @create: 2022/1/17 5:01 下午
*/
@DisplayName("😊Calculator Test╯°□°)╯")
public class CommonAnnotation1Test extends BaseTest {
@DisplayName("加法")
@Test
void addTest1(){
logger.info("Begin Add Test");
//加法运算
int sum = mySUT.sum(5, 8);
logger.info("Operation result:{}",sum);
// expected:期望值, actual:运算的实际值
assertEquals(13,sum);
}
@DisplayName("2数相减")
@Test
void subtractTest1(){
logger.info("Begin Subtract Test");
//减法运算
int subtract = mySUT.subtract(5, 8);
logger.info("Operation result:{}",subtract);
// expected:期望值, actual:运算的实际值
assertEquals(-3,subtract);
}
@DisplayName("字符串拼接🐶")
@Test
void csTest1(){
logger.info("Begin ConcatStr Test");
//字符串拼接
String concatStr = mySUT.concatStr("Hello","Junit5");
logger.info("Operation result:{}",concatStr);
// expected:期望值, actual:运算的实际值
assertEquals("Hello Junit5",concatStr);
}
@DisplayName("100连续减😱")
@Test
void subtractTest2(){
logger.info("Begin Continuous Subtract Test");
//100连续减
int subtract = mySUT.subtract(50,30,10,60);
logger.info("Operation result:{}",subtract);
// expected:期望值, actual:运算的实际值
assertEquals(-50,subtract);
}
@DisplayName("平均数😐")
@Test
void avTest1(){
logger.info("Begin Average Test");
//平均值
double average = mySUT.average(55,44,86,72,64);
logger.info("Operation result:{}",average);
// expected:期望值, actual:运算的实际值
assertEquals(64.2,average);
}
}
@DisplayNameGeneration
使用
类上的注解,不能在方法上使用。
类上声明一次,对应的方法内也是用该类上声明的规则进行对应方法名称的显示。
作用
在类上添加该注解,并在注解内指明对应类,比如:DisplayNameGenerator.ReplaceUnderscores
类,它会默认用空格替换名称中的任何下划线,无论是类名上的还是方法名上下划线都会被替换掉。
其实就是自己在类上声明@DisplayNameGeneration
注解,并在注解内配置 当前类和方法的显示名称生成器规则
总结
- 配置自定义显示名称生成器
DisplayNameGenerator.ReplaceUnderscores
类,它用空格替换名称中的任何下划线
package top.testeru.common;
import org.junit.jupiter.api.DisplayNameGeneration;
import org.junit.jupiter.api.DisplayNameGenerator;
import org.junit.jupiter.api.Test;
import top.testeru.util.BaseTest;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* @program: tutorials
* @author: testeru.top
* @description: 常用注解测试:显示名称
* DisplayNameGeneration
* 空格替换名称中的任何下划线 ReplaceUnderscores
* @Version 1.0
* @create: 2022/1/17 5:01 下午
*/
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
public class Common_Annotation2_Test extends BaseTest {
@Test
void add_test_1(){
logger.info("Begin Add Test");
//加法运算
int sum = mySUT.sum(5, 8);
logger.info("Operation result:{}",sum);
// expected:期望值, actual:运算的实际值
assertEquals(13,sum);
}
@Test
void subtract_test_1(){
logger.info("Begin Subtract Test");
//减法运算
int subtract = mySUT.subtract(5, 8);
logger.info("Operation result:{}",subtract);
// expected:期望值, actual:运算的实际值
assertEquals(-3,subtract);
}
@Test
void cs_test_1(){
logger.info("Begin ConcatStr Test");
//字符串拼接
String concatStr = mySUT.concatStr("Hello","Junit5");
logger.info("Operation result:{}",concatStr);
// expected:期望值, actual:运算的实际值
assertEquals("Hello Junit5",concatStr);
}
@Test
void subtract_test_2(){
logger.info("Begin Continuous Subtract Test");
//100连续减
int subtract = mySUT.subtract(50,30,10,60);
logger.info("Operation result:{}",subtract);
// expected:期望值, actual:运算的实际值
assertEquals(-50,subtract);
}
@Test
void av_test_1(){
logger.info("Begin Average Test");
//平均值
double average = mySUT.average(55,44,86,72,64);
logger.info("Operation result:{}",average);
// expected:期望值, actual:运算的实际值
assertEquals(64.2,average);
}
}
自定义显示名规则
步骤
编写静态内部类
继承Standard
重写方法
generateDisplayNameForClass
:声明对应的类名 显示的规则generateDisplayNameForMethod
: 声明对应的方法名显示的规则
static class ReplaceCamelCase extends DisplayNameGenerator.Standard {
@Override
public String generateDisplayNameForClass(Class<?> testClass) {
return replaceCamelCase(super.generateDisplayNameForClass(testClass))+ "...";
}
@Override
public String generateDisplayNameForNestedClass(Class<?> nestedClass) {
return replaceCamelCase(super.generateDisplayNameForNestedClass(nestedClass));
}
@Override
public String generateDisplayNameForMethod(Class<?> testClass, Method testMethod) {
//+ DisplayNameGenerator.parameterTypesAsString(testMethod) 方法的括号
return this.replaceCamelCase(testClass.getSimpleName()+ ";方法名:" + testMethod.getName()+ ".");
}
String replaceCamelCase(String camelCase) {
StringBuilder result = new StringBuilder();
result.append(camelCase.charAt(0));
for (int i=1; i<camelCase.length(); i++) {
if (Character.isUpperCase(camelCase.charAt(i))) {
result.append(' ');
result.append(Character.toLowerCase(camelCase.charAt(i)));
} else {
result.append(camelCase.charAt(i));
}
}
return result.toString().replace("test","Test");
}
}
注意⚠️
方法/类上有@DisplayName
,并且有自定义的显示规则优先级排序「优先级自上而下」:{1 > 2}
- 方法/类上的
@DisplayName
- 自定义的显示规则
就是如果 方法/类上有
@DisplayName
则直接显示,如果没有则按自定义的显示规则进行显示
完整代码
package com.testeru.base;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.DisplayNameGeneration;
import org.junit.jupiter.api.DisplayNameGenerator;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Method;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* @program: tutorials
* @author: testeru.top
* @description: 自定义显示名称
* @Version 1.0
* @create: 2022/1/17 5:01 下午
*/
@DisplayNameGeneration(CommonAnnotation3Test.ReplaceCamelCase.class)
public class CommonAnnotation3Test extends BaseTest{
@DisplayName("加法")
@Test
void addTest1(){
logger.info("Begin Add Test");
//加法运算
int sum = mySUT.sum(5, 8);
logger.info("Operation result:{}",sum);
// expected:期望值, actual:运算的实际值
assertEquals(13,sum);
}
@Test
void subtractTest1(){
logger.info("Begin Subtract Test");
//减法运算
int subtract = mySUT.subtract(5, 8);
logger.info("Operation result:{}",subtract);
// expected:期望值, actual:运算的实际值
assertEquals(-3,subtract);
}
@Test
void csTest1(){
logger.info("Begin ConcatStr Test");
//字符串拼接
String concatStr = mySUT.concatStr("Hello","Junit5");
logger.info("Operation result:{}",concatStr);
// expected:期望值, actual:运算的实际值
assertEquals("Hello Junit5",concatStr);
}
@Test
void subtractTest2(){
logger.info("Begin Continuous Subtract Test");
//100连续减
int subtract = mySUT.subtract(50,30,10,60);
logger.info("Operation result:{}",subtract);
// expected:期望值, actual:运算的实际值
assertEquals(-50,subtract);
}
@Test
void avTest1(){
logger.info("Begin Average Test");
//平均值
double average = mySUT.average(55,44,86,72,64);
logger.info("Operation result:{}",average);
// expected:期望值, actual:运算的实际值
assertEquals(64.2,average);
}
//标准显示名称生成行为 Standard默认配置
static class ReplaceCamelCase extends DisplayNameGenerator.Standard {
@Override
public String generateDisplayNameForClass(Class<?> testClass) {
return replaceCamelCase(super.generateDisplayNameForClass(testClass))+ "...";
}
@Override
public String generateDisplayNameForNestedClass(Class<?> nestedClass) {
return replaceCamelCase(super.generateDisplayNameForNestedClass(nestedClass));
}
@Override
public String generateDisplayNameForMethod(Class<?> testClass, Method testMethod) {
//+ DisplayNameGenerator.parameterTypesAsString(testMethod) 方法的括号
return this.replaceCamelCase(testClass.getSimpleName()+ ";方法名:" + testMethod.getName()+ ".");
}
String replaceCamelCase(String camelCase) {
StringBuilder result = new StringBuilder();
result.append(camelCase.charAt(0));
for (int i=1; i<camelCase.length(); i++) {
if (Character.isUpperCase(camelCase.charAt(i))) {
result.append(' ');
result.append(Character.toLowerCase(camelCase.charAt(i)));
} else {
result.append(camelCase.charAt(i));
}
}
return result.toString().replace("test","Test");
}
}
}
@Disable
使用
@Disable
注解不但可以放在测试方法上也可以放在测试类的上面。是一个类和方法都可以使用的注解。
eg : @Disabled("平均值有bug")
对应括号内是string
类型的字符串就是对应方法/类被禁用的原因。也可以不写括号及括号内的string,默认就是返回值 包名.类名.方法 is @Disabled
eg :void top.testeru.common.CommonAnnotation4Test.csTest1() is @Disabled
作用
该注解在方法上声明对应的方法则不被执行,但是方法的类还是依然会被实例化。
如果注解在类上声明则该类下所有测试方法都不被执行,对应类也不会实例化。
总结
- 表示带注释的测试类或测试方法当前已禁用,不应执行
- 可以添加()并声明原因
- 在类级别应用时,该类中的所有测试方法会自动禁用
- 在方法级别应用时,@Disable不会阻止测试类被实例化
- 会阻止执行测试方法和方法级别的生命周期回调
- 例如 @BeforeEach 方法、@AfterEach 方法和相应的扩展 API
package top.testeru.common;
import org.junit.jupiter.api.*;
import top.testeru.util.BaseTest;
import java.lang.reflect.Method;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* @program: tutorials
* @author: testeru.top
* @description: 常用注解测试:
* 禁用测试方法:@Disable
* 括号内有描述显示描述信息
* 不带括号,显示默认信息:{返回值 包名.类名.方法名 is @Disabled}
* 显示名称
* 自定义显示名称
* @Version 1.0
* @create: 2022/1/17 5:01 下午
*/
@DisplayNameGeneration(CommonAnnotation4Test.ReplaceCamelCase.class)
public class CommonAnnotation4Test extends BaseTest {
@DisplayName("加法")
@Test
void addTest1(){
logger.info("Begin Add Test");
//加法运算
int sum = mySUT.sum(5, 8);
logger.info("Operation result:{}",sum);
// expected:期望值, actual:运算的实际值
assertEquals(13,sum);
}
@Test
void subtractTest1(){
logger.info("Begin Subtract Test");
//减法运算
int subtract = mySUT.subtract(5, 8);
logger.info("Operation result:{}",subtract);
// expected:期望值, actual:运算的实际值
assertEquals(-3,subtract);
}
@DisplayName("字符串拼接🐶")
@Test
@Disabled
void csTest1(){
logger.info("Begin ConcatStr Test");
//字符串拼接
String concatStr = mySUT.concatStr("Hello","Junit5");
logger.info("Operation result:{}",concatStr);
// expected:期望值, actual:运算的实际值
assertEquals("Hello Junit5",concatStr);
}
@Test
void subtractTest2(){
logger.info("Begin Continuous Subtract Test");
//100连续减
int subtract = mySUT.subtract(50,30,10,60);
logger.info("Operation result:{}",subtract);
// expected:期望值, actual:运算的实际值
assertEquals(-50,subtract);
}
@Test
@Disabled("平均值有bug")
void avTest1(){
logger.info("Begin Average Test");
//平均值
double average = mySUT.average(55,44,86,72,64);
logger.info("Operation result:{}",average);
// expected:期望值, actual:运算的实际值
assertEquals(64.2,average);
}
//标准显示名称生成行为 Standard默认配置
static class ReplaceCamelCase extends DisplayNameGenerator.Standard {
@Override
public String generateDisplayNameForClass(Class<?> testClass) {
return replaceCamelCase(super.generateDisplayNameForClass(testClass))+ "...";
}
@Override
public String generateDisplayNameForNestedClass(Class<?> nestedClass) {
return replaceCamelCase(super.generateDisplayNameForNestedClass(nestedClass));
}
@Override
public String generateDisplayNameForMethod(Class<?> testClass, Method testMethod) {
//+ DisplayNameGenerator.parameterTypesAsString(testMethod) 方法的括号
return this.replaceCamelCase(testClass.getSimpleName()+ ";方法名:" + testMethod.getName()+ ".");
}
String replaceCamelCase(String camelCase) {
StringBuilder result = new StringBuilder();
result.append(camelCase.charAt(0));
for (int i=1; i<camelCase.length(); i++) {
if (Character.isUpperCase(camelCase.charAt(i))) {
result.append(' ');
result.append(Character.toLowerCase(camelCase.charAt(i)));
} else {
result.append(camelCase.charAt(i));
}
}
return result.toString().replace("test","Test");
}
}
}
注意⚠️
- 对应不执行的测试方法的
@BeforeEach
和@AfterEach
都没有执行 - 如果有描述,显示不执行的描述,如果没有描述内容,显示对应方法名不被执行
@Tag
使用
@Tag
注解不但可以放在测试方法上也可以放在测试类的上面。是一个类和方法都可以使用的注解。可以在一个类/方法上有多个,也可以只有一个。
eg :
- 只有一个:
@Tag("decimal")
- 有多个:
@Tags({
@Tag("decimal"),
@Tag("dev")
})
命名规范
对应Tag标签的标识名称的命名规范,首先不能是空的字符串,空字符串对应的没有意义,同时标签名称不能有对应的空格及ISO控制字符。
一般情况下,对应的名称都为英文或者是中文来做区分即可。
总结为以下几点:
- 不为空
- 标签不得包含空格
- 标签不得包含 ISO 控制字符。
- 标签不得包含以下任何保留字符
,
(
)
&
|
!
作用
在运行suite套件的时候可以指定对应tag标签来运行特定的类或者方法,只要是对应类或方法上有该标签就会运行。
也可以在pom文件中进行对应group的配置,这样对应的可以只运行特定的方法和类。
总结以上,@Tag
注解就是为了特定运行某些方法和类。
总结
- 标记测试
- 测试类或测试方法声明
应用
用例分组
- 环境分组: 测试环境、预发布环境
- 阶段分组: 冒烟用例
- 版本分组: V1.1、V1.2
自定义组成的注释命名
- 接口名随便定义,不一定非要Test结尾
自定义方法上元注释
步骤
- 自定义元注解写在测试方法上
- 生成对应接口文件
- 在接口文件上添加对应注解即可
实现效果
@IntTagTest 替换 @Test+@Tag("integer")
- 示例代码
@DisplayName("加法")
// @Test
// @Tag("integer")
@IntTagTest
void addTest1(){
logger.info("Begin Add Test");
//加法运算
int sum = mySUT.sum(5, 8);
logger.info("Operation result:{}",sum);
// expected:期望值, actual:运算的实际值
assertEquals(13,sum);
}
实现代码
package top.testeru.util;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @Package: top.testeru.util
* @author: testeru.top
* @Description:
* @date: 2022年02月09日 3:03 PM
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Tag("integer")
@Test
public @interface IntTagTest {
}
说明
ElementType.METHOD
代表该注解只是在方法上才可以使用RetentionPolicy.RUNTIME
注释将由编译器记录在类文件中,并在运行时由VM
保留,因此可以反射性地读取它们
自定义类上元注释
步骤
- 自定义元注解写在测试类上
- 生成对应接口文件
- 在接口文件上添加对应注解即可
实现效果
@MyClassTag 替换类上的注解 @Tag("mytag")
- 示例代码
@MyClassTag
public class CommonAnnotation6Test extends BaseTest {
...
}
实现代码
package top.testeru.util;
import org.junit.jupiter.api.Tag;
import java.lang.annotation.*;
/**
* @Package: top.testeru.util
* @author: testeru.top
* @Description:
* @date: 2022年02月09日 3:09 PM
*/
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Tag("mytag")
@Documented
public @interface MyClassTag {
}
@RepeatedTest
- 想要重复多次的任何测试方法
- 只能添加在测试方法上,和Test使用方法一致
- 方法不能是私有的或静态的,并且返回值必须是void
- ()内为执行的次数
- 方法上有@RepeatedTest则不需要@Test
- 不然真正执行的次数为RepeatedTest的参数值 + 1
package com.testeru.classes;
import org.junit.jupiter.api.*;
/**
* @program: tutorials
* @author: testeru.top
* @description:
* @Version 1.0
* @create: 2022/1/7 4:46 下午
*/
@DisplayName("junit5 test")
public class FourDemoTest extends BaseDemoTest{
@BeforeAll
static void beforeAll1(){
System.out.println("---进入到首页---");
}
@BeforeEach
void beforeEach1(){
System.out.println("---搜索商品---");
}
@DisplayName("first test")
@Test
void test(){
System.out.println("---点击查看第 1 个商品详情---");
}
@DisplayName("second test")
@Disabled("先不执行")
@Test
void test2(){
System.out.println("---点击查看第 2 个商品详情---");
}
@DisplayName("third test")
@Disabled
@Test
void test3(){
System.out.println("---点击查看第 2 个商品详情---");
}
@DisplayName("five test")
@RepeatedTest(3)
void test5(){
System.out.println("---点击查看第 6 个商品详情---");
}
@AfterEach
void afterEach1(){
System.out.println("---添加购物车---");
}
@AfterAll
static void afterAll1(){
System.out.println("---进入购物车列表,生成订单,进行订单付款---");
}
}
- 相当于重复写了3个相同的方法
- 对应beforeeach和aftereach都会随着重复次数来执行
自定义显示名称
@RepeatedTest(value = 3,name = "Custom name {currentRepetition}/{totalRepetitions}")
- value
- 重复执行多少次
- name
- 对应每次执行的自定义名称
currentRepetition
:当前重复次数totalRepetitions
:重复的总数
- value
package com.testeru.classes;
import org.junit.jupiter.api.*;
/**
* @program: tutorials
* @author: testeru.top
* @description:
* @Version 1.0
* @create: 2022/1/7 4:46 下午
*/
@DisplayName("junit5 test")
public class FourDemoTest extends BaseDemoTest{
@BeforeAll
static void beforeAll1(){
System.out.println("---进入到首页---");
}
@BeforeEach
void beforeEach1(){
System.out.println("---搜索商品---");
}
@DisplayName("first test")
@Test
void test(){
System.out.println("---点击查看第 1 个商品详情---");
}
@DisplayName("second test")
@Disabled("先不执行")
@Test
void test2(){
System.out.println("---点击查看第 2 个商品详情---");
}
@DisplayName("third test")
@Disabled
@Test
void test3(){
System.out.println("---点击查看第 2 个商品详情---");
}
@DisplayName("five test")
@RepeatedTest(value = 3,name = "Custom name {currentRepetition}/{totalRepetitions}")
void test5(){
System.out.println("---点击查看第 6 个商品详情---");
}
@AfterEach
void afterEach1(){
System.out.println("---添加购物车---");
}
@AfterAll
static void afterAll1(){
System.out.println("---进入购物车列表,生成订单,进行订单付款---");
}
}
方法内调用
- 方法内想要知道调用当前是第几次重复和总共重复的次数
@RepeatedTest(value = 3,name = "Custom name {currentRepetition}/{totalRepetitions}")
void test4(RepetitionInfo repetitionInfo) {
int currentRepetition = repetitionInfo.getCurrentRepetition();
System.out.println("第" + currentRepetition +"次搜索 查看第 " + currentRepetition + "个商品详情");
}
- 测试方法不是
@RepeatedTest
,则不能将RepetitionInfo
注入到@BeforeEach
或@AfterEach
方法中
package com.testeru.classes;
import org.junit.jupiter.api.*;
/**
* @program: tutorials
* @author: testeru.top
* @description:
* @Version 1.0
* @create: 2022/1/7 4:46 下午
*/
@DisplayName("junit5 test")
public class FiveDemoTest{
@BeforeEach
void beforeEach1(RepetitionInfo repetitionInfo){
int currentRepetition = repetitionInfo.getCurrentRepetition();
System.out.println("---搜索商品---" + currentRepetition);
}
@AfterEach
void afterEach1(RepetitionInfo repetitionInfo){
int currentRepetition = repetitionInfo.getCurrentRepetition();
System.out.println("---添加购物车---"+ currentRepetition);
}
@DisplayName("four test")
@RepeatedTest(value = 3,name = "重复搜索次数 {currentRepetition}/{totalRepetitions}")
void test4(RepetitionInfo repetitionInfo) {
int currentRepetition = repetitionInfo.getCurrentRepetition();
System.out.println("第" + currentRepetition +"次搜索 查看第 " + currentRepetition + "个商品详情");
}
@DisplayName("third test")
@Test
void test3(){
System.out.println("---点击查看第 2 个商品详情---");
}
@DisplayName("first test")
@RepeatedTest(2)
void test1(){
System.out.println("---点击查看第 1 个商品详情---");
}
}
报错:
org.junit.jupiter.api.extension.ParameterResolutionException: No ParameterResolver registered for parameter [org.junit.jupiter.api.RepetitionInfo arg0] in method [void com.testeru.classes.FiveDemoTest.beforeEach1(org.junit.jupiter.api.RepetitionInfo)].