【问题标题】:Using @MockBean in tests forces reloading of Application Context在测试中使用 @MockBean 会强制重新加载应用程序上下文
【发布时间】:2017-08-09 09:49:48
【问题描述】:

我有几个在 Spring Framework 上运行的集成测试,它们扩展名为 BaseITCase 的基类。
像这样:

@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 重新创建上下文,并且在此之后的测试有时会使用错误的 bean(来自使用 @MockBean 为测试准确创建的上下文)。我只是通过检查 bean 具有不同的哈希码才发现这一点。

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

有什么解决方法吗?

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

【问题讨论】:

  • 错误地在您的帖子中发布了另一个问题的答案。对不起!我删了。

标签: java spring mockito spring-test springmockito


【解决方案1】:

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

如您所说,如果您将模拟 bean 移动到父类,则不会重新加载上下文,这是有道理的,因为 bean 配置保持不变。

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

例如,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;
    }
}

希望这会有所帮助。

【讨论】:

    【解决方案2】:

    @MockBean 可能会导致上下文重新加载as explained in the previous answer

    作为替代方案,如果您使用的是 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 bean 中模拟 bean 并避免冗长的上下文重新创建。

    【讨论】:

      猜你喜欢
      • 2012-10-28
      • 2014-09-03
      • 2016-10-31
      • 1970-01-01
      • 1970-01-01
      • 2022-11-19
      • 1970-01-01
      • 2021-08-02
      相关资源
      最近更新 更多