将 bean 注入到 WebMvc 测试的上下文中:@AutoConfigureMockMvc 不能与 @Component 注解 @Configuration 结合使用
Injecting a bean into the context of a WebMvc test: @AutoConfigureMockMvc cannot be used in combination with the @Component annotation @Configuration
对于我的 Java Spring 测试,我决定伪造系统时钟以使测试更容易
我有一个继承公共基础的测试 bean class
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
@AutoConfigureMockMvc(print = MockMvcPrint.LOG_DEBUG)
@TestExecutionListeners(mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS,
value = {FlywayTestExecutionListener.class}
)
@FlywayTest
@Getter(AccessLevel.PROTECTED)
public abstract class AbstractMockMvcTest {}
@Configuration
@Sql(scripts = "classpath:..", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(scripts = "classpath:..", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
@WithMockUser(username = "junit", authorities = {}, roles = {})
class RolesControllerTest extends AbstractMockMvcTest {
private final Instant fixedInstant = Instant.from(OffsetDateTime.of(2021, 6, 2, 15, 54, 0, 0, ZoneOffset.ofHours(2)));
@Bean
Clock fakeClock() {
return Clock.fixed(fixedInstant, ZoneId.ofOffset("UTC", ZoneOffset.ofHours(2)));
}
}
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@ConditionalOnMissingBean(Clock.class)
@Bean
public Clock systemClock() {
return Clock.systemUTC();
}
}
我的想法是我的应用程序声明了一个主时钟,每个需要访问时间的 bean 都使用它(例如 getClock().instant()
或 Instant.now(getClock())
)。通过 mocking 时钟,我使测试更容易。
但我发现使用 Spring beans 比 Mockito 更自然。 Java Clock
有正确模拟时钟的方法
Best practice for applications is to pass a Clock into any method that requires the current instant. A dependency injection framework is one way to achieve this:
public class MyBean {
private Clock clock; // dependency inject
...
public void process(LocalDate eventDate) {
if (eventDate.isBefore(LocalDate.now(clock)) {
...
}
}
}
This approach allows an alternate clock, such as fixed or offset to be used during testing.
但是当我尝试 运行 测试时,我偶然发现了这个
@AutoConfigureMockMvc cannot be used in combination with the @Component annotation @Configuration
在 Web MVC 测试中声明额外的主要 bean 有什么好主意吗?
设置一个静态内部@TestConfiguration
class,其中定义了bean:
// Remove the @Configuration annotation here
@Sql(scripts = "classpath:..", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(scripts = "classpath:..", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
@WithMockUser(username = "junit", authorities = {}, roles = {})
@ContextConfiguration(classes = RolesControllerTest.TestConfiguration.class) // Add a ContextConfiguration if the test configuration isn't automatically used.
class RolesControllerTest extends AbstractMockMvcTest {
private final Instant fixedInstant = Instant.from(OffsetDateTime.of(2021, 6, 2, 15, 54, 0, 0, ZoneOffset.ofHours(2)));
@TestConfiguration
public static class TestConfig {
@Bean
Clock fakeClock() {
return Clock.fixed(fixedInstant, ZoneId.ofOffset("UTC", ZoneOffset.ofHours(2)));
}
}
}
另一种方法是声明一个 @MockBean private Clock clock
并使用 mockito return 固定时刻。
我发现以下工作。这是@Michiel's 的一个稍微替代的解决方案。最初(在发布答案之前)我尝试了他们的内部配置方法 class,但我偶然发现了另一个问题:内部配置 class,@Configuration
或 [=12] =],删除了整个上下文,并且没有其他 bean 可见。包括无法自动装配的 PlatformTransactionManager
以下对我有用
@Configuration
@Sql(scripts = "classpath:..", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(scripts = "classpath:..", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
@WithMockUser(username = "junit", authorities = {}, roles = {})
class RolesControllerTest extends AbstractMockMvcTest {
private final Instant fixedInstant = Instant.from(OffsetDateTime.of(2021, 6, 2, 15, 54, 0, 0, ZoneOffset.ofHours(2)));
@TestConfiguration
@Import(Application.class)
static class ClockConfigurer {
@Bean
@Primary
Clock fakeClock() {
return Clock.fixed(fixedInstant, ZoneId.ofOffset("UTC", ZoneOffset.ofHours(2)));
}
}
}
在我的例子中,我必须添加 @Import
注释到测试配置 class,指向主配置。
我最初在测试class中使用了@Import
,注释掉了,但每次上下文都出错了。我还必须将时钟标记为 @Primary
以便使用按类型自动装配的分辨率
这很有帮助。还是感谢@Michiel
对于我的 Java Spring 测试,我决定伪造系统时钟以使测试更容易
我有一个继承公共基础的测试 bean class
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
@AutoConfigureMockMvc(print = MockMvcPrint.LOG_DEBUG)
@TestExecutionListeners(mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS,
value = {FlywayTestExecutionListener.class}
)
@FlywayTest
@Getter(AccessLevel.PROTECTED)
public abstract class AbstractMockMvcTest {}
@Configuration
@Sql(scripts = "classpath:..", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(scripts = "classpath:..", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
@WithMockUser(username = "junit", authorities = {}, roles = {})
class RolesControllerTest extends AbstractMockMvcTest {
private final Instant fixedInstant = Instant.from(OffsetDateTime.of(2021, 6, 2, 15, 54, 0, 0, ZoneOffset.ofHours(2)));
@Bean
Clock fakeClock() {
return Clock.fixed(fixedInstant, ZoneId.ofOffset("UTC", ZoneOffset.ofHours(2)));
}
}
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@ConditionalOnMissingBean(Clock.class)
@Bean
public Clock systemClock() {
return Clock.systemUTC();
}
}
我的想法是我的应用程序声明了一个主时钟,每个需要访问时间的 bean 都使用它(例如 getClock().instant()
或 Instant.now(getClock())
)。通过 mocking 时钟,我使测试更容易。
但我发现使用 Spring beans 比 Mockito 更自然。 Java Clock
有正确模拟时钟的方法
Best practice for applications is to pass a Clock into any method that requires the current instant. A dependency injection framework is one way to achieve this:
public class MyBean {
private Clock clock; // dependency inject
...
public void process(LocalDate eventDate) {
if (eventDate.isBefore(LocalDate.now(clock)) {
...
}
}
}
This approach allows an alternate clock, such as fixed or offset to be used during testing.
但是当我尝试 运行 测试时,我偶然发现了这个
@AutoConfigureMockMvc cannot be used in combination with the @Component annotation @Configuration
在 Web MVC 测试中声明额外的主要 bean 有什么好主意吗?
设置一个静态内部@TestConfiguration
class,其中定义了bean:
// Remove the @Configuration annotation here
@Sql(scripts = "classpath:..", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(scripts = "classpath:..", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
@WithMockUser(username = "junit", authorities = {}, roles = {})
@ContextConfiguration(classes = RolesControllerTest.TestConfiguration.class) // Add a ContextConfiguration if the test configuration isn't automatically used.
class RolesControllerTest extends AbstractMockMvcTest {
private final Instant fixedInstant = Instant.from(OffsetDateTime.of(2021, 6, 2, 15, 54, 0, 0, ZoneOffset.ofHours(2)));
@TestConfiguration
public static class TestConfig {
@Bean
Clock fakeClock() {
return Clock.fixed(fixedInstant, ZoneId.ofOffset("UTC", ZoneOffset.ofHours(2)));
}
}
}
另一种方法是声明一个 @MockBean private Clock clock
并使用 mockito return 固定时刻。
我发现以下工作。这是@Michiel's 的一个稍微替代的解决方案。最初(在发布答案之前)我尝试了他们的内部配置方法 class,但我偶然发现了另一个问题:内部配置 class,@Configuration
或 [=12] =],删除了整个上下文,并且没有其他 bean 可见。包括无法自动装配的 PlatformTransactionManager
以下对我有用
@Configuration
@Sql(scripts = "classpath:..", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(scripts = "classpath:..", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
@WithMockUser(username = "junit", authorities = {}, roles = {})
class RolesControllerTest extends AbstractMockMvcTest {
private final Instant fixedInstant = Instant.from(OffsetDateTime.of(2021, 6, 2, 15, 54, 0, 0, ZoneOffset.ofHours(2)));
@TestConfiguration
@Import(Application.class)
static class ClockConfigurer {
@Bean
@Primary
Clock fakeClock() {
return Clock.fixed(fixedInstant, ZoneId.ofOffset("UTC", ZoneOffset.ofHours(2)));
}
}
}
在我的例子中,我必须添加 @Import
注释到测试配置 class,指向主配置。
我最初在测试class中使用了@Import
,注释掉了,但每次上下文都出错了。我还必须将时钟标记为 @Primary
以便使用按类型自动装配的分辨率
这很有帮助。还是感谢@Michiel