读书笔记 -- Junit 实战(3rd)Ch04 Junit4 向 Junit5 迁移

4.1 Junit4 向 Junit5 跨越

1. 基础包:

Junit Jupiter 的类和注解都定义在新的 org.junit.jupiter 基础包中;

Junit 4 的类和注解都定义在新的 org.junit 基础包中;

 

2. Junit4 向 Junit5 迁移的步骤

  • 替换所需的依赖项;
  • 替换注解,并引入新的注解;
  • 替换测试类和方法;
  • 用Junit 5 扩展模型替换 Junit4 规则和运行器;

 

4.2 所需的依赖项

<!-- Junit4 的依赖项 -->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13.2</version>
    <scope>test</scope>
</dependency>
<!-- Junit5 处理 遗留的Junit4 测试的依赖项 -->
<dependency>
    <groupId>org.junit.vintage</groupId>
    <artifactId>junit-vintage-engine</artifactId>
    <version>5.6.0</version>
    <scope>test</scope>
</dependency>
<!--  Junit 5 的两个基础依赖:api、engine  -->
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-api</artifactId>
    <version>5.10.0</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-engine</artifactId>
    <version>5.10.0</version>
    <scope>test</scope>
</dependency>

4.3 注解、类和方法

注解   断言   假设
Junit4 Junit5   Junit4 Junit5   Junit4 Junit5
@BeforeClass、@AfterClass @BeforeAll、@AfterAll   Assert 类 Assertions 类   Assume 类 Assumptions 类
@Before、@After @BeforeEach、@AfterEach   第一个参数是可选断言消息 最后一个参数是可选断言消息   assumeNotNull 和 AssumeNoException 移除了 assumeNotNull 和 AssumeNoException
@Ignore @Disable   assertThat()

assertThat() 被移除,

对应的新方法是 assertAll 和 AssertThrows

     
@Category @Tag            

1. 注解:

Junit4:
  • 1. @BeforeClass 和 @AfterClass 注解的方法必须是 public static
  • 2. @Before 和 @After 注解的方法必须是 public
  • 3. @Test 注解的方法必须是public 的
JUnit5:
  • @BeforeAll 和 @AfterAll 注解的方法是 static。或者,用 @TestInstance(TestInstance.Lifecycle.PER_CLASS) 标注整个测试类

2. 分类和标记:

 Junit4 的分类:

// Step1:方法上 或类上 标注 @Category
// 方法上标注
@Category(IndividualTests.class)
@Test
public void testCustomer() {}

// 类上标注
@Category({IndividualTests.class, RepositoryTests.class})
public class JUnit4CustomersRepositoryTest {}

// Step2:给每个标注的分类建立一个接口
public interface IndividualTests {}
public interface RepositoryTests {}

// Step3:组建一个 Suite,
@RunWith(Categories.class)
@Categories.IncludeCategory(IndividualTests.class)
@Suite.SuiteClasses({JUnit4CustomerTest.class, JUnit4CustomersRepositoryTest.class})
public class JUnit4IndividualTestsSuite {
}

/**
 * Junit4 最后形成测试组件:
 *  1. @RunWith(Categories.class),告知 JUnit:JUnit4IndividualTestsSuite 需要使用这个特定的运行器运行测试
 *  2. @Categories.IncludeCategory(),注解标注的测试类别
 *  3. @Suite.SuiteClasses(),在类中查找这些被标注的测试
*/

Junit5 的标记:

// Step1: @Tag() 标注在类或方法上
@Tag("individual")
public class JUnit5CustomerTest {}

// Step2: 
> cd C:\Users\xxx\IdeaProjects\trunk\Junit5InAction\ch04   // pom.xml 所在目录
> mvn clean test -Dgroups=individual

或者,pom.xml 添加 configuration 标识
               <configuration>-->
                    <groups>individual</groups>-->
                    <excludedGroups>repository</excludedGroups>-->
                </configuration>-->
,然后
> cd C:\Users\xxx\IdeaProjects\trunk\Junit5InAction\ch04   // pom.xml 所在目录
> mvn clean test    

3. 规则与扩展

// Junit4:需要 @Rule 注解在 ExpectedException,然后在方法判断时,通过该字段保留异常类型和消息
public class JUnit4RuleExceptionTester {

    // @Rule 必须应用于非静态公有字段或非静态公有方法
    // 要向运行的测试添加行为,必须在 TestRule 字段上使用 @Rule 注解
    @Rule
    // ExpectedException.none() 工厂方法简单地创建了一个未配置的 ExpectedException
    public ExpectedException expectedException = ExpectedException.none();

    private Calculator calculator = new Calculator();

    @Test
    public void expectIllegalArgumentException() {
        // ExpectedException 被配置为在调用 sqrt() 抛出异常前,保持异常类型和消息
        // !! 所以,如果将 sqrt() 方法放在 ExpectedException 则会出现异常报错
        expectedException.expect(IllegalArgumentException.class);
        expectedException.expectMessage("Cannot extract the square root of a negative value");
        calculator.sqrt(-1);
    }
}

 

// Juit5: 直接通过 Throwable 获取异常,进行判断
public class JUnit5ExceptionTester {
    private Calculator calculator = new Calculator();

    @Test
    public void expectIllegalArgumentException() {
        Throwable throwable = assertThrows(IllegalArgumentException.class, () -> calculator.sqrt(-1));
        assertEquals("Cannot extract the square root of a negative value", throwable.getMessage());
    }
}

4. 自定义规则

// Junit4:

public class JUnit4CustomRuleTester {
    @Rule
    // 类字段公有
    public CustomRule myRule = new CustomRule();

    /**Output:
     * CustomStatement myCustomRuleTest has started
     * Call of a test method
     * CustomStatement myCustomRuleTest has finished
     *
     * 解析:通过 @Rule 注释 CustomRule,其将加载该 Rule
     * 通过 CustomRule -> CustomStatement#evaluate 可以看出,先有一个 printout,然后 base.evaluate(),再由一个 printout
     */
    @Test
    public void myCustomRuleTest() {
        System.out.println("Call of a test method");
    }
}

public class CustomRule implements TestRule {

    private Statement base;
    private Description description;

    @Override
    public Statement apply(Statement base, Description description) {
        this.base = base;
        this.description = description;
        // 需要将这两个参数传递出去,所以 CustomStatement 需要创建一个完全参数的构造器
        return new CustomStatement(base, description);
    }
}

@AllArgsConstructor
public class CustomStatement extends Statement {
    private Statement base;
    private Description description;

    @Override
    public void evaluate() throws Throwable {
        System.out.println(this.getClass().getSimpleName() + " " + description.getMethodName() + " has started");
        try {
            base.evaluate();
        } finally {
            System.out.println(this.getClass().getSimpleName() + " " + description.getMethodName() + " has finished");
        }
    }
}
// Junit5:
// 自定义扩展,覆盖 BeforeEach 和 AfterEach 方法
public class CustomExtension implements AfterEachCallback, BeforeEachCallback {

    @Override
    public void beforeEach(ExtensionContext extensionContext) throws Exception {
        System.out.println(this.getClass().getSimpleName() + " " + extensionContext.getDisplayName() + " has started");
    }


    @Override
    public void afterEach(ExtensionContext extensionContext) throws Exception {
        System.out.println(this.getClass().getSimpleName() + " " + extensionContext.getDisplayName() + " has finished");
    }
}

@ExtendWith(CustomExtension.class)
// 使用 CustomExtension 类扩展 JUnit5CustomExtensionTester 类
public class JUnit5CustomExtensionTester {

    @Test
    public void myCustomRuleTest() {
        System.out.println("Call of a test method");
    }
}

 

对于已经创建的扩展,Junit4 向 Junit5 的迁移方式是:

  • 要迁移 Mockito 测试,需在测试类中将 @RunWith(MockitoJUnitRunner.class) 替换成 @ExtendWith(MockitoExtension.class);
  • 要迁移 Spring 测试,需在测试类中将 @RunWith(SpringJUnit4Runner.class) 替换成 @ExtendWith(SpringExtension.class);

 

posted on 2023-11-07 21:12  bruce_he  阅读(64)  评论(0编辑  收藏  举报