在测试中使用@MockBean 会强制重新加载应用程序上下文

Using @MockBean in tests forces reloading of Application Context

我在 Spring 框架上进行了几个集成测试 运行,扩展了名为 BaseITCase.
的基础 class 像这样:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {AppCacheConfiguration.class, TestConfiguration.class}, loader = SpringBootContextLoader.class)
@Transactional
@WebMvcTest
public abstract class BaseITCase{...}
...
public class UserControllerTest extends BaseITCase {...}

问题是其中一个测试有几个声明: @MockBean 在它里面并且在这个测试执行的那一刻,Spring 重新创建上下文,并且这个测试之后的测试有时会使用错误的 beans(来自为 @MockBean 测试创建的上下文)。我只是通过检查 bean 是否具有不同的哈希码才发现了这一点。

当我使用@EventListener 时,它变得非常重要。因为调用了错误上下文的侦听器(已经完成执行的测试 class 的上下文)并且我在那里有错误的 bean。

有什么解决方法吗?

我尝试将所有@MockBean 声明移动到基本 class 并且它工作正常,因为没有创建新的上下文。但是,它使基本 class 太重了。 另外,我试图为这个测试创建一个脏上下文,但是下一个测试失败并显示上下文已经关闭的消息。

原因是带有@MockBean的测试的spring配置与其余测试不同,所以spring框架无法缓存以前使用过的上下文,需要加载它再次。在这里你可以找到更详细的解释:https://github.com/spring-projects/spring-boot/issues/10015

如您所说,如果将模拟 bean 移动到父级 class,上下文不会重新加载,这很有意义,因为 bean 配置保持不变。

一种可能的解决方法是将您的 mock bean 定义为一个简单的 mock,然后在需要的地方手动注入它。

例如,UserController 依赖于 Foo

public class UserControllerTest extends BaseITCase {

    private Foo foo = Mockito.mock(Foo.class);

    @Autowired
    private UserController userController;

    @Before
    public void setUp() {
        super.setup();

        this.userController.setFoo(foo);
    }
}

@Component
public class UserController {

    private Foo foo;

    @Autowired
    public void setFoo(final Foo foo) {
        this.foo = foo;
    }
}

希望这对您有所帮助。

@MockBean 可能会导致上下文重新加载 .

作为替代方案,如果您使用 spring boot 2.2+,则可以使用 @MockInBean 而不是 @MockBean。它使您的上下文 干净 并且不需要重新加载您的上下文。

@SpringBootTest
public class UserControllerTest extends BaseITCase {

    @MockInBean(UserController.class)
    private Foo foo;

    @Autowired
    private UserController userController;

    @Test
    public void test() {
        userController.doSomething();
        Mockito.verify(foo).hasDoneSomething();
    }
}

@Component
public class UserController {

    @Autowired
    private Foo foo;

}

免责声明:我创建这个库正是为了这个目的:在 spring beans 中模拟 beans 并避免冗长的上下文重新创建。