@MockBean 似乎重新运行上下文创建并失败 afterMigrate.sql
@MockBean seems to rerun context creation and fails afterMigrate.sql
我有两个集成测试类。其中一个 类 依赖于与外部服务通信的 bean,所以我需要模拟这个 bean,而 @MockBean
似乎非常适合这个。为了将一些种子注入 DB,我使用 flyway
的 afterMigrate.sql
。所以这里看起来很热:
@RunWith(SpringRunner.class)
@ActiveProfiles("test")
@SpringBootTest
@Transactional
@Rollback
class FooTest {
@Autowired
private MyService myService;
}
@RunWith(SpringRunner.class)
@ActiveProfiles("test")
@SpringBootTest
@Transactional
@Rollback
class BarTest {
@MockBean
private ExternalService;
@Autowired
private MyService myService;
}
和afterMigrate.sql
:
INSERT INTO my_table (id, name) VALUES (1, 'John Doe')
当我将 ExternatService
注释为 @MockBean
时出现问题,现在 afretMigrate.sql
运行s 两次,我收到错误:
java.lang.IllegalStateException: Failed to load ApplicationContext
....
Message : ERROR: duplicate key value violates unique constraint "my_table_pkey"
当我将 @MockBean
更改为 @Autowired
时,错误消失了,上下文创建也没有任何问题。另外,如果我单独 运行 BarTest
测试 运行 没有问题。
正如文档所述,这不是 @MockBean
的预期行为:
Any existing single bean of the same type defined in the context will
be replaced by the mock. If no existing bean is defined a new one will
be added. Dependencies that are known to the application context but
are not beans (such as those registered directly) will not be found
and a mocked bean will be added to the context alongside the existing
dependency.
并没有说将重新创建上下文。
因为当您使用 @MockBean
注释时,将为每个测试加载您的上下文。请参考this github issue。此页面的引文:
The Spring test framework will cache an ApplicationContext whenever possible between test runs. In order to be cached, the context must have an exactly equivalent configuration. Whenever you use @MockBean, you are by definition changing the context configuration.
因此,当您在不同的测试中使用模拟 bean 时 - 每次都会为您的测试重新创建上下文 class。因此,例如,如果您有一些 bean 在创建上下文时将数据加载到数据库 - 例如 flyway 的 bean - 每次重新创建上下文时都会创建它们。
这是我解决这个问题的方法(我认为这是一个问题)。
解决方案一:
我创建了一个 MockConfig
class 应该为整个测试套件创建一个 mock
:
@Configration
public class MockConfig {
@Bean
@Primary
public ExternalService externalService() {
return mock(ExternalService.class);
}
}
在测试中,我只是自动装配外部服务:
@Autowire
private ExternalService externalService;
但是这个解决方案有一个问题,它会创建一个真正的 bean 然后用模拟 bean 覆盖它。如果您的外部服务在创建时连接到外部资源,而您不需要它,那么您将需要另一个解决方案。
方案二:
创建一个包含 @MockBean
的基本摘要 class:
@RunWith(SpringRunner.class)
@ActiveProfiles("test")
@SpringBootTest
@Transactional
@Rollback
public abstract class BaseIntegrationTest {
@MockBean
ExternalService externalService;
}
并从此基础扩展集成测试class:
class FooTest extends BaseIntegrationTest {
@Autowired
private MyService myService;
}
class BarTest extends BaseIntegrationTest {
@Autowired
private MyService myService;
}
现在上下文不会刷新,因为它始终相同,并且不会创建真正的 bean。
我有两个集成测试类。其中一个 类 依赖于与外部服务通信的 bean,所以我需要模拟这个 bean,而 @MockBean
似乎非常适合这个。为了将一些种子注入 DB,我使用 flyway
的 afterMigrate.sql
。所以这里看起来很热:
@RunWith(SpringRunner.class)
@ActiveProfiles("test")
@SpringBootTest
@Transactional
@Rollback
class FooTest {
@Autowired
private MyService myService;
}
@RunWith(SpringRunner.class)
@ActiveProfiles("test")
@SpringBootTest
@Transactional
@Rollback
class BarTest {
@MockBean
private ExternalService;
@Autowired
private MyService myService;
}
和afterMigrate.sql
:
INSERT INTO my_table (id, name) VALUES (1, 'John Doe')
当我将 ExternatService
注释为 @MockBean
时出现问题,现在 afretMigrate.sql
运行s 两次,我收到错误:
java.lang.IllegalStateException: Failed to load ApplicationContext
....
Message : ERROR: duplicate key value violates unique constraint "my_table_pkey"
当我将 @MockBean
更改为 @Autowired
时,错误消失了,上下文创建也没有任何问题。另外,如果我单独 运行 BarTest
测试 运行 没有问题。
正如文档所述,这不是 @MockBean
的预期行为:
Any existing single bean of the same type defined in the context will be replaced by the mock. If no existing bean is defined a new one will be added. Dependencies that are known to the application context but are not beans (such as those registered directly) will not be found and a mocked bean will be added to the context alongside the existing dependency.
并没有说将重新创建上下文。
因为当您使用 @MockBean
注释时,将为每个测试加载您的上下文。请参考this github issue。此页面的引文:
The Spring test framework will cache an ApplicationContext whenever possible between test runs. In order to be cached, the context must have an exactly equivalent configuration. Whenever you use @MockBean, you are by definition changing the context configuration.
因此,当您在不同的测试中使用模拟 bean 时 - 每次都会为您的测试重新创建上下文 class。因此,例如,如果您有一些 bean 在创建上下文时将数据加载到数据库 - 例如 flyway 的 bean - 每次重新创建上下文时都会创建它们。
这是我解决这个问题的方法(我认为这是一个问题)。
解决方案一:
我创建了一个 MockConfig
class 应该为整个测试套件创建一个 mock
:
@Configration
public class MockConfig {
@Bean
@Primary
public ExternalService externalService() {
return mock(ExternalService.class);
}
}
在测试中,我只是自动装配外部服务:
@Autowire
private ExternalService externalService;
但是这个解决方案有一个问题,它会创建一个真正的 bean 然后用模拟 bean 覆盖它。如果您的外部服务在创建时连接到外部资源,而您不需要它,那么您将需要另一个解决方案。
方案二:
创建一个包含 @MockBean
的基本摘要 class:
@RunWith(SpringRunner.class)
@ActiveProfiles("test")
@SpringBootTest
@Transactional
@Rollback
public abstract class BaseIntegrationTest {
@MockBean
ExternalService externalService;
}
并从此基础扩展集成测试class:
class FooTest extends BaseIntegrationTest {
@Autowired
private MyService myService;
}
class BarTest extends BaseIntegrationTest {
@Autowired
private MyService myService;
}
现在上下文不会刷新,因为它始终相同,并且不会创建真正的 bean。