【问题标题】:Injecting a bean into the context of a WebMvc test: @AutoConfigureMockMvc cannot be used in combination with the @Component annotation @Configuration将 bean 注入到 WebMvc 测试的上下文中:@AutoConfigureMockMvc 不能与 @Component 注解 @Configuration 结合使用
【发布时间】:2021-07-30 15:33:22
【问题描述】:

对于我的 Java Spring 测试,我决定伪造系统时钟以使测试更容易

我有一个继承公共基类的测试 bean

@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()))。通过模拟时钟,我让测试变得更容易。

但我发现使用 Spring bean 比使用 Mockito 更自然。 Java Clock 具有正确模拟时钟的方法

应用程序的最佳实践是将时钟传递给任何需要当前时刻的方法。依赖注入框架是实现此目的的一种方法:

public class MyBean {
    private Clock clock;  // dependency inject
    ...
    public void process(LocalDate eventDate) {
      if (eventDate.isBefore(LocalDate.now(clock)) {
        ...
      }
    }
  }
 

这种方法允许在测试期间使用备用时钟,例如固定时钟或偏移时钟。

但是当我尝试运行测试时,我偶然发现了这个

@AutoConfigureMockMvc cannot be used in combination with the @Component annotation @Configuration

在 Web MVC 测试中声明额外的主 bean 有什么聪明的想法吗?

【问题讨论】:

    标签: java spring-boot spring-test mockmvc junit-jupiter


    【解决方案1】:

    设置一个静态内部 @TestConfiguration 类,在其中定义 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 返回固定瞬间。

    【讨论】:

    • 不确定它是否是一个完整且有效的解决方案。我的意思是,在我的情况下,@TestConfiguration 类无法清除整个 Spring 上下文,从而有效地删除了我需要的 PlatformTransactionManager。我会发布另一个答案,但我可能会接受这个
    【解决方案2】:

    我发现以下工作。这是@Michiel 的稍微替代的解决方案。最初(在发布答案之前)我尝试了他们的内部配置类方法,但我偶然发现了另一个问题:内部配置类,@Configuration@TestConfiguration,删除了整个上下文,没有其他 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 注释到测试配置类,指向主配置。

    我最初在测试类中使用@Import,将其注释掉,但每次上下文都是错误的。我还必须将时钟标记为@Primary,以便使用自动装配的按类型分辨率

    这有帮助。仍然感谢@Michiel

    【讨论】:

      猜你喜欢
      • 2016-12-12
      • 1970-01-01
      • 2016-10-31
      • 1970-01-01
      • 2011-05-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多