JAVASpring

Spring 测试类 @DirtiesContext

背景


从昨天到今天,耗了整整一天的Build Faild最终被注解@DirtiesContext解决了。

相同的测试代码,在本地运行良好,但在Jenkins里不同的环境中分别编译失败,而且每次失败的错误还不一样。

终极原因是运行测试的application context的缓存中有脏数据。

@DirtiesContext注解用在测试类上,待整个测试类的所有测试执行结束后,该测试的application context会被关闭,同时缓存会清除。@DirtiesContext分为method-levelclass-level

ChatGpt的解释

@DirtiesContext


@DirtiesContext是一个Spring测试注释。它指示关联的测试或类修改了ApplicationContext。它告诉测试框架关闭并重新创建上下文以用于以后的测试。

我们可以注释测试方法或整个类。通过设置MethodMode或ClassMode,我们可以控制Spring何时标记要关闭的上下文。

如果我们将@DirtiesContext放在类上,则注释将应用于具有给定ClassMode的类中的每个方法。

在不清除Spring上下文的情况下进行测试


假设我们有一个用户:

public class User {
    String firstName;
    String lastName;
}

我们还有一个非常简单的UserCache:

@Component
public class UserCache {

    @Getter
    private Set<String> userList = new HashSet<>();

    public boolean addUser(String user) {
        return userList.add(user);
    }

    public void printUserList(String message) {
        System.out.println(message +":" + userList);
    }

}

我们创建一个集成测试来加载和测试整个应用程序:

@TestMethodOrder(OrderAnnotation.class)
@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = SpringDataRestApplication.class)
class DirtiesContextIntegrationTest {

    @Autowired
    protected UserCache userCache;
   
    ...
}

第一种方法addJaneDoeAndPrintCache将一个条目添加到缓存中:

@Test
@Order(1)
void addJaneDoeAndPrintCache() {
    userCache.addUser("Jane Doe");
    userCache.printUserList("addJaneDoeAndPrintCache");
}

将用户添加到缓存后,它将打印缓存的内容:

addJaneDoeAndPrintCache: [Jane Doe]

接下来,printCache再次打印用户缓存:

@Test
@Order(2)
void printCache() {
    userCache.printUserList("printCache");
}

它包含在先前测试中添加的名称:

printCache: [Jane Doe]

假设以后的测试依赖于空缓存进行某些声明。以前插入的名称可能会导致不良行为。

使用@DirtiesContext


现在,我们将使用默认方法模式AFTER_METHOD显示@DirtiesContext。这意味着Spring将在相应的测试方法完成后将上下文标记为关闭。

为了隔离测试的更改,我们添加了@DirtiesContext。让我们看看它是如何工作的。

addJohnDoeAndPrintCache测试方法将用户添加到缓存中。我们还添加了@DirtiesContext批注,该批注表示上下文应在测试方法结束时关闭:

@DirtiesContext(methodMode = MethodMode.AFTER_METHOD)
@Test
@Order(3)
void addJohnDoeAndPrintCache() {
    userCache.addUser("John Doe");
    userCache.printUserList("addJohnDoeAndPrintCache");
}

现在的输出是:

addJohnDoeAndPrintCache: [John Doe, Jane Doe]

最后,printCacheA再次打印缓存:

@Test
@Order(4)
void printCacheAgain() {
    userCache.printUserList("printCacheAgain");
}

运行完整的测试类,我们看到在addJohnDoeAndPrintCache和printCacheAgain之间重新加载了Spring上下文。因此,缓存将重新初始化,并且输出为空:

printCacheAgain: []

总结


Class级别:测试类的ClassMode选项定义了何时重置上下文

  • BEFORE_CLASS:在当前测试课程之前
  • BEFORE_EACH_TEST_METHOD:在当前测试类中的每个测试方法之前
  • AFTER_EACH_TEST_METHOD:当前测试类中的每个测试方法之后
  • AFTER_CLASS:在当前测试课程之后

方法级别:单个方法的MethodMode选项定义何时重置上下文

  • BEFORE_METHOD:当前测试方法之前
  • AFTER_METHOD:当前测试方法之后