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

tquggr8v  于 2023-05-27  发布在  Java
关注(0)|答案(3)|浏览(384)

我在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声明移到基本类中,它工作得很好,因为没有创建新的上下文。但是,它使基础课过于沉重。另外,我试图为这个测试创建一个脏上下文,但是下一个测试失败,并显示上下文已经关闭的消息。

k2arahey

k2arahey1#

原因是具有@MockBean的测试的spring配置与其余测试不同,因此spring框架无法缓存先前使用的上下文,需要再次加载它。在这里,您可以找到更详细的解释:https://github.com/spring-projects/spring-boot/issues/10015
正如您所说的,如果您将mockbean移动到父类,则不会重新加载上下文,这是有意义的,因为bean配置保持不变。
一个可能的解决方法是将mockbean定义为一个简单的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;
    }
}

希望这能帮上忙。

frebpwbc

frebpwbc2#

@MockBean可以使上下文重新加载as explained in the previous answer
作为一个替代方案,如果你使用的是 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;

}
  • 免责声明:我创建这个lib就是为了这个目的:在springbeans中模拟beans,避免冗长的上下文重现。*
2w2cym1i

2w2cym1i3#

除了上述解决方案,如果你想注射他们无处不在,你可以
1.在您的测试包中创建一个配置***,并将mock bean定义为@Primary,以便注入它们而不是真实的的bean。

@Configuration
 public class MockClientConfiguration {

   @Bean
   @Primary
   public ApiClient mockApiClient() {
     return mock(ApiClient.class);
   }

1.在您的基本测试类@Autowire中,由于它们是@Primary,因此您将获得模拟。注意,它们是受保护的
public class Structured {

@Autowired
   protected ApiClient mockApiClient;

1.然后在你的基本测试类中,你可以在每次运行之前重置模拟,并设置默认行为:
@BeforeEach public void setup(){ Mockito.reset(mockApiClient); Mockito.when(mockApiClient.something(USER_ID)).thenReturn(true);}
1.从您的测试类访问模拟:

public class MyTest extends BaseIntTest {
   @Test
   public void importantTestCase() {
       Mockito.reset(mockApiClient);
       Mockito.when(mockApiClient.something(USER_ID)).thenReturn(false);

相关问题