前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Junit5框架详解

Junit5框架详解

作者头像
TestOps
发布2022-04-08 08:18:12
1.1K0
发布2022-04-08 08:18:12
举报
文章被收录于专栏:TestOps云层TestOps云层

1、Junit5初识

1.1、what is junit5

JUnit5 = JUnit Platform + JUnit Jupiter + JUnit Vintage

**JUnit Platform:**是在JVM上启动测试框架的基础。它还定义了用于开发平台上运行的测试框架的测试引擎(TestEngine)API。此外,该平台还提供了一个控制台启动器,可以从命令行启动平台,并为Gradle 和 Maven 构建插件,以及一个基于JUnit 4的运行器(JUnit 4 based Runner),用于在平台上运行任何 TestEngine 。

**JUnit Jupiter:**是在JUnit 5中编写测试和扩展的新编程模型和扩展模型的组合。另外,Jupiter子项目还提供了一个TestEngine,用于在平台上运行基于Jupiter的测试。

**JUnit Vintage:**提供了一个在平台上运行JUnit 3和JUnit 4的 TestEngine 。

1.2、why Junit5

众所周知Java的测试框架比较有名是TestNG、Junit,今本次架构师课程我给大家讲解Junit5框架的基本使用和改造,为什么会现在Junit5也是有很多原因的,对于这2款测试框架我在实践过程中我都用过,至于选择Junit5:

  • 相比Junit4、TestNG功能更强大
  • 完全兼容Spring、SpringBoot,这一点很重要
  • 标准化、可扩展性强

1.3、生命周期

**一个标准的测试用例**

代码语言:javascript
复制
import static org.junit.jupiter.api.Assertions.fail;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;

class StandardTests {

    @BeforeAll
    static void beforeAll() {
    }

    @BeforeEach
    void beforeEach() {
    }

    @AfterEach
    void afterEach() {
    }

    @AfterAll
    static void afterAll() {
    }
    
    @Test
    void demoTest1() {
    }
    
    @Test
    void demoTest2() {
    }
}

1.4、常用注解

  • @Disabled
    • 可以通过 @Disabled 注释、是否测试执行
    • @Disabled可以标记在测试类、测试方法上
代码语言:javascript
复制
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;

@Disabled
class DisabledTestsDemo {

    @Disabled
    @Test
    void testWillBeSkipped() {
    }

    @Test
    void testWillBeExecuted() {
    }
}
  • @Tag
    • JUnit5@Tag可用于从测试计划中过滤测试用例
    • 它可以帮助针对不同的环境,不同的用例或任何特定要求创建多个不同的测试计划
    • 通过仅在测试计划中包括@Tag标记的测试或通过从测试计划中排除其他测试来执行测试集
代码语言:javascript
复制
## 可以在测试类或测试方法或两者上应用@Tag注释
@Tag("development")
public class ClassATest
{
    @Test
    @Tag("userManagement")
    void testCaseA(TestInfo testInfo) {
    }
}
代码语言:javascript
复制
## 在单个测试用例上应用多个标签,以便您可以将其包含在多个测试计划中
public class ClassATest
{
    @Test
    @Tag("development")
    @Tag("v_486")
    void testCaseA() {
    }
}
代码语言:javascript
复制
//@IncludeTags example
@RunWith(JUnitPlatform.class)
@SelectPackages("com.testcases.testops.order")
@IncludeTags("v_486")
public class MultipleTagsExample 
{
}
 
//@ExcludeTags example
@RunWith(JUnitPlatform.class)
@SelectPackages("com.testcases.testops.order")
@ExcludeTags("v_486")
public class MultipleTagsExample 
{
}
代码语言:javascript
复制
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.junit.jupiter.api.Tag;

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Tag("v_486")
public @interface Fast {
}


## 使用定义的元注解Tag
public class ClassATest
{
    @Test
    @Tag("development")
    @Fast
    void testCaseA() {
    }
}

1.5、断言

  • junit常用断言
代码语言:javascript
复制
assertEquals(判断两个对象或两个原始类型是否相等)
assertNotEquals(判断两个对象或两个原始类型是否不相等)
assertSame(判断两个对象引用是否指向同一个对象)
assertNotSame(判断两个对象引用是否指向不同的对象)
assertTrue(判断给定的布尔值是否为 true)
assertFalse(判断给定的布尔值是否为 false)
assertNull (判断给定的对象引用是否为 null)
assertNotNull(判断给定的对象引用是否不为 null)
  • assertAll
代码语言:javascript
复制
@Test
void groupedAssertions() {
    assertAll("assertAll",
            () -> assertEquals("Jane", 23),
            () -> assertEquals("Doe", 23)
    );
}

@Test
void dependentAssertions() {
    assertAll("assertAll",
            () -> {
                String firstName = null;
                assertNotNull(firstName);
                assertAll("assert1",
                        () -> assertTrue(firstName.startsWith("J")),
                        () -> assertTrue(firstName.endsWith("e"))
                );
            },
            () -> {
                String lastName = "De";
                assertNotNull(lastName);
                assertAll("assertAll2",
                        () -> assertTrue(lastName.startsWith("D")),
                        () -> assertTrue(lastName.endsWith("e"))
                );
            }
    );
}
  • Junit5第三方断言
代码语言:javascript
复制
// AssertJ、Hamcrest、Truth
// https://assertj.github.io/doc/

/**字符串 断言**/
String url = "http://www.baidu.com";
//测试变量是否包含指定字符
assertThat(url, containsString("baidu"));
//测试变量是否已指定字符串开头
assertThat(url, startsWith("http://"));
//测试变量是否以指定字符串结尾
assertThat(url, endsWith(".com"));
//测试变量是否等于指定字符串
assertThat(url, equalTo("http://www.baidu.com"));
//测试变量再忽略大小写的情况下是否等于指定字符串
assertThat(url, equalToIgnoringCase("http://www.baidu.com"));
//测试变量再忽略头尾任意空格的情况下是否等于指定字符串
assertThat(url, equalToIgnoringWhiteSpace("http://www.baidu.com"));

/**List 断言**/
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
//测试list中是否还有指值
assertThat(list, hasItem("b"));

/**Map  断言**/
Map<String,String> map = new HashMap<>();
map.put("a", "test1");
map.put("b", "test2");

//测试map中是否还有指定键值对
assertThat(map, hasEntry("a", "test1"));
//测试map中是否还有指定键
assertThat(map, hasKey("b"));
//测试map中是否还有指定值
assertThat(map, hasValue("test2"));

2、Java注解和反射

上面我们介绍了Junit5的基本使用后,下面我们来看看注解和反射,为什么我们要学习注解和反射,这块在设计测试框架以及多框架功能扩展的时候我们就会用到注解和反射,那下面我们来看下注解和反射:

2.1、注解

注解的基本概念

注解(Annotation)提供了一种关联信息以及元数据的途径和方法。是一个接口,程序可以通过反射来获取指定程序元素中的 Annotation 对象,然后通过解析 Annotation 对象获取注解中的元数据。可以应用于包、类型、构造方法、方法、成员变量、参数、局部变量等等的声明中。在注解中以"name = value"的形式存储。

Annotation 不能影响程序代码的执行,尽管一些注解通过反射技术可以在运行时被访问,但是java的语言解释器在工作时是忽略他们的。

2.2、注解定义

  • @Target

含义: 指定注解修饰的对象的范围,通俗的讲就是注解使用的时候要放在哪里(方法上,类上等等)取值(ElementType):

1、CONSTRUCTOR: 描述构造器

2、FIELD: 描述域属性

3、LOCAL_VARIABLE: 描述局部变量

4、METHOD: 描述方法

5、PACKAGE: 描述包

6、PARAMETER: 描述参数

7、TYPE: 描述类、接口或注解、枚举类型enum

  • @Retention

含义: 定义注解保留的时长,限制注解的生命周期。取值(RetentionPolicy)

SOURCE: 源文件保留,被编译器所丢弃

CLASS: 在字节码文件(*.class)中保留,被JVM所丢弃

RUNTIME: 在运行时保留

  • @Inherited

含义: 被标注的类型是被继承的,使用 @Inherited 修饰的类型作用于一个 class 上时,那么注解也将应用在该 class 的子类。

  • @Documented

含义: 可以使用javadoc工具进行文档化操作

代码语言:javascript
复制
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @Auther: Evan.hu
 * @Description: 定义注解
 * @date: 2022/3/6 10:30
 */
@Target({ ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
    String value() default "";
}
代码语言:javascript
复制
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @Auther: Evan.hu
 * @Description: 定义注解
 * @date: 2022/3/6 10:30
 */
@Target({ ElementType.TYPE, ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {

    String column() default "";
    String type() default "";
    int length() default 0;
}

2.3、使用注解

代码语言:javascript
复制
import com.platform.annotation.Column;
import com.platform.annotation.Table;

@Table(value = "mysql")
public class TableTest {
    @Column(column = "名字", type = "java.lang.String", length = 12)
    private String name;

    @Column(column = "年龄", type = "int", length = 2)
    private int age;

    @Column(column = "地址", type = "String")
    private String address;

    @Table(value = "mysql")
    public void test1(){

    }
}

2.4、通过反射解析注解

代码语言:javascript
复制
import com.platform.annotation.Column;
import com.platform.annotation.Table;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class AnnotationUtil {
    public static void parse(Class<?> target){
        Table table = target.getAnnotation(Table.class);
        System.out.println("数据库表名: " + table.value());

        Field[] fields = target.getDeclaredFields();

        for (Field field : fields){
            Column column = field.getAnnotation(Column.class);
            System.out.println(column.column());
            System.out.println(column.type());
            System.out.println(column.length());
        }
    }

    public static void parse(String packagse,String method) throws ClassNotFoundException {
        Class clazz = Class.forName(packagse);

        // 获取类上面所有注解
        Annotation[] annotations = clazz.getAnnotations();
        for (Annotation a : annotations){
            if (a instanceof Table){
                Table table = (Table) clazz.getAnnotation(Table.class);
                System.out.println("数据库表名: " + table.value());
            }
        }

        // 获取类下面所有方法
        Method[] methods = clazz.getDeclaredMethods();
        for (Method m : methods){
            if (m.getName().equals(method)){
                System.out.println("方法里面注解解析");
                Table t = m.getAnnotation(Table.class);
                System.out.println(t.value());
            }else {
                System.out.println("不需要处理的注解方法");
            }
        }

        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields){
            Annotation[] annotations1 = field.getAnnotations();
            for (Annotation a1 : annotations1){
                if (a1 instanceof Column){
                    Column column = field.getAnnotation(Column.class);
                    System.out.println(column.column());
                    System.out.println(column.type());
                    System.out.println(column.length());
                }
            }
        }
    }

    public static void main(String[] args) throws ClassNotFoundException {
        String packages = "com.testcases.junit5.TableTest";
//        AnnotationUtil.parse(TableTest.class);
        AnnotationUtil.parse(packages,"test1");
    }
}

4、junit5高级特性

4.1、@TestMethodOrder

**junit5指定@Test的执行顺序,Order值越小越优先执行**

**根据此注解可以自定义场景测试用例**

代码语言:javascript
复制

@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class demo1Test {
    
    @Test
    @Order(0)
    @DisplayName("用户登录")
    public void test1() {
    }

    @Test
    @Order(2)
    @DisplayName("搜索下单商品")
    public void test2(){
    }

    @Test
    @Order(3)
    @DisplayName("下单支付")
    public void test3() {
    }
}

4.2、@EnabledIf

EnabledIf自定义执行条件函数:com.testcases.condition.Condition#isHasTestDate通过isHasTestDate方法的返回boolean来判断改测试用例是否执行

代码语言:javascript
复制
package com.testcases.condition;

public class Condition {
    public static boolean customCondition(){
        return true;
    }

    public static boolean isHasTestDate(){
        return true;
    }
}


@Test
@Order(2)
@EnabledIf("com.testcases.condition.Condition#isHasTestDate")
@DisplayName("搜索下单商品2")
public void test2(){
    System.out.println("商品搜索");
}

4.3、测试计划执行

  • junit5套件执行
代码语言:javascript
复制
@RunWith(JUnitPlatform.class)
@SelectClasses({ExportTest.class, Export123Test.class})
public class ClassSuite {
}
代码语言:javascript
复制
@RunWith(JUnitPlatform.class)
@SelectPackages({"com.testcases.template.demo2"})
public class PackagesSuite {

}
代码语言:javascript
复制
@RunWith(JUnitPlatform.class)
@SelectPackages({"com.testcases.template.demo2"})
@IncludeTags("v_486")
public class PackagesSuite {

}
代码语言:javascript
复制
@RunWith(JUnitPlatform.class)
@SelectPackages({"com.testcases.template.demo2"})
@ExcludeTags("v_486")
public class PackagesSuite {

}
  • maven执行

**${version}表示测试用例中@Tag的值**

代码语言:javascript
复制
<build>
  <plugins>
    <plugin>
      <artifactId>maven-surefire-plugin</artifactId>
      <version>3.0.0-M3</version>
      <configuration>
        <groups>${version}</groups>
        <testFailureIgnore>true</testFailureIgnore>
      </configuration>
      <dependencies>
        <dependency>
          <groupId>org.junit.platform</groupId>
          <artifactId>junit-platform-surefire-provider</artifactId>
          <version>1.3.2</version>
        </dependency>
        <dependency>
          <groupId>org.junit.jupiter</groupId>
          <artifactId>junit-jupiter-engine</artifactId>
          <version>5.6.2</version>
        </dependency>
      </dependencies>
    </plugin>
  </plugins>
    </build>
  • Java代码执行
代码语言:javascript
复制
public class LauncherMain {
    
    public static void clazz(Class<?> clazz){
        try{
            LauncherDiscoveryRequest request = LauncherDiscoveryRequestBuilder.request()
                .selectors(
                selectClass(clazz)
            )
                .build();
            
            log.info("---------start----------");
            Launcher launcher = LauncherFactory.create();
            
            SummaryGeneratingListener listener = new SummaryGeneratingListener();
            launcher.registerTestExecutionListeners(listener);
            launcher.execute(request);
            TestExecutionSummary summary = listener.getSummary();
            log.info("---------end----------");
        }catch (Throwable throwable){
        }
    }
}

public class TestLauncherMain {
    public static void main(String[] args) {
        LauncherMain.clazz(demo1Test.class);
    }
}

5、总结

  • 框架思维不仅仅是设计一个框架
    • 技术架构
    • 产品思维
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-03-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 TestOps 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1、Junit5初识
    • 1.1、what is junit5
      • 1.2、why Junit5
        • 1.3、生命周期
          • 1.4、常用注解
            • 1.5、断言
            • 2、Java注解和反射
              • 2.1、注解
                • 2.2、注解定义
                  • 2.3、使用注解
                    • 2.4、通过反射解析注解
                    • 4、junit5高级特性
                      • 4.1、@TestMethodOrder
                        • 4.2、@EnabledIf
                          • 4.3、测试计划执行
                          • 5、总结
                          领券
                          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档