For @SpringBootTest, why am I told "Configuration error: found multiple declarations of @BootstrapWith" even though I don't have a single one?

For @SpringBootTest, why am I told "Configuration error: found multiple declarations of @BootstrapWith" even though I don't have a single one?

我正在使用 Spring Boot 2.1。我正在尝试设置 Spring 启动集成测试来测试控制器。我有以下

@ContextConfiguration(classes=CardmaniaApplication.class)
@ExtendWith(SpringExtension.class)
@SpringBootTest
@AutoConfigureMockMvc
@DataJpaTest
public class CardControllerIntegrationTest {

    @Autowired
    private MockMvc mockMvc;

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private ICardRepository cardRepository;

    @Autowired
    private IUserRepository userRepository;
    
    @Autowired
    private IOccasionRepository occasionRepository;
    
    @Test
    @WithMockUser(authorities = {"ADMIN"})
    void createCard() throws Exception {
        final Card card = new Card();
        final User author = userRepository.findAll().get(0);
        card.setAuthor(author);
        final Occasion occasion = occasionRepository.findAll().get(0);
        card.setOccasion(occasion);
        byte[] image = new byte[] {1, 1, 1, 1};
        card.setImage(image);
        
        mockMvc.perform(post("/api/cards").contentType("application/json")
                .content(objectMapper.writeValueAsString(card)))
                .andExpect(status().isOk());

        List<Card> cards = cardRepository.findByOccasion(occasion);
        assertThat(cards.size()).isEqualTo(1);
        final Card cardEntity = cards.get(0);
        assertThat(cardEntity.getImage()).isEqualTo(image);
    }
}

我对如何注释 main class 有点困惑,因为当我 运行 测试时,我得到了异常

java.lang.IllegalStateException: Configuration error: found multiple declarations of @BootstrapWith for test class [com.burrobuie.cardmania.controller.CardControllerIntegrationTest]: [@org.springframework.test.context.BootstrapWith(value=org.springframework.boot.test.context.SpringBootTestContextBootstrapper), @org.springframework.test.context.BootstrapWith(value=org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTestContextBootstrapper)]
    at org.springframework.test.context.BootstrapUtils.resolveExplicitTestContextBootstrapper(BootstrapUtils.java:166)
    at org.springframework.test.context.BootstrapUtils.resolveTestContextBootstrapper(BootstrapUtils.java:127)
    at org.springframework.test.context.TestContextManager.<init>(TestContextManager.java:122)
    at org.junit.jupiter.engine.execution.ExtensionValuesStore.lambda$getOrComputeIfAbsent[=11=](ExtensionValuesStore.java:81)
    at org.junit.jupiter.engine.execution.ExtensionValuesStore$MemoizingSupplier.get(ExtensionValuesStore.java:182)
    at org.junit.jupiter.engine.execution.ExtensionValuesStore.getOrComputeIfAbsent(ExtensionValuesStore.java:84)
    at org.junit.jupiter.engine.execution.ExtensionValuesStore.getOrComputeIfAbsent(ExtensionValuesStore.java:88)
    at org.junit.jupiter.engine.execution.NamespaceAwareStore.getOrComputeIfAbsent(NamespaceAwareStore.java:61)
    at org.springframework.test.context.junit.jupiter.SpringExtension.getTestContextManager(SpringExtension.java:213)
    at org.springframework.test.context.junit.jupiter.SpringExtension.beforeAll(SpringExtension.java:77)
    at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeBeforeAllCallbacks(ClassBasedTestDescriptor.java:359)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeBeforeAllCallbacks(ClassBasedTestDescriptor.java:359)
    at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.before(ClassBasedTestDescriptor.java:189)
    at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.before(ClassBasedTestDescriptor.java:78)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively(NodeTestTask.java:132)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively(NodeTestTask.java:125)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively(NodeTestTask.java:123)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1540)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively(NodeTestTask.java:139)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively(NodeTestTask.java:125)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively(NodeTestTask.java:123)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:248)
    at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute(DefaultLauncher.java:211)
    at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:226)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:199)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:141)
    at org.eclipse.jdt.internal.junit5.runner.JUnit5TestReference.run(JUnit5TestReference.java:98)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:41)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:542)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:770)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:464)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:210)
    Suppressed: java.lang.IllegalStateException: Configuration error: found multiple declarations of @BootstrapWith for test class [com.burrobuie.cardmania.controller.CardControllerIntegrationTest]: [@org.springframework.test.context.BootstrapWith(value=org.springframework.boot.test.context.SpringBootTestContextBootstrapper), @org.springframework.test.context.BootstrapWith(value=org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTestContextBootstrapper)]
        at org.springframework.test.context.BootstrapUtils.resolveExplicitTestContextBootstrapper(BootstrapUtils.java:166)
        at org.springframework.test.context.BootstrapUtils.resolveTestContextBootstrapper(BootstrapUtils.java:127)
        at org.springframework.test.context.TestContextManager.<init>(TestContextManager.java:122)
        at org.junit.jupiter.engine.execution.ExtensionValuesStore.lambda$getOrComputeIfAbsent[=11=](ExtensionValuesStore.java:81)
        at org.junit.jupiter.engine.execution.ExtensionValuesStore$MemoizingSupplier.get(ExtensionValuesStore.java:182)
        at org.junit.jupiter.engine.execution.ExtensionValuesStore.remove(ExtensionValuesStore.java:98)
        at org.junit.jupiter.engine.execution.NamespaceAwareStore.remove(NamespaceAwareStore.java:73)
        at org.springframework.test.context.junit.jupiter.SpringExtension.afterAll(SpringExtension.java:89)
        at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeAfterAllCallbacks(ClassBasedTestDescriptor.java:425)
        at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
        at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeAfterAllCallbacks(ClassBasedTestDescriptor.java:425)
        at java.base/java.util.ArrayList.forEach(ArrayList.java:1540)
        at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeAfterAllCallbacks(ClassBasedTestDescriptor.java:425)
        at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.after(ClassBasedTestDescriptor.java:213)
        at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.after(ClassBasedTestDescriptor.java:78)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively(NodeTestTask.java:145)
        at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively(NodeTestTask.java:145)
    

... 29 more

我需要向我的 header 添加或删除什么才能让我的测试达到 运行?

我必须包括以下内容,因为 SO 告诉我我的问题主要是代码。照顾病人很重要,医生会跟进,但这是一个充满痛苦和痛苦的时期。就最小的细节而言,任何人都不应从事任何一种工作,除非他从中得到一些好处。不要在痛斥中生气在快感中痛斥他要从痛中发一毛希望没有滋生。除非他们被情欲蒙蔽了双眼,否则他们不会出来;他们有错误的是疏忽了他们的职责运行t软化了灵魂,即劳作。

您有多个相互冲突的测试注释。对于您的用例:控制器的集成测试,以下设置就足够了:

@SpringBootTest
@AutoConfigureMockMvc
public class CardControllerIntegrationTest {

    @Autowired
    private MockMvc mockMvc;

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private ICardRepository cardRepository;

    @Autowired
    private IUserRepository userRepository;
    
    @Autowired
    private IOccasionRepository occasionRepository;
    
    @Test
    @WithMockUser(authorities = {"ADMIN"})
    void createCard() throws Exception {
        final Card card = new Card();
        final User author = userRepository.findAll().get(0);
        card.setAuthor(author);
        final Occasion occasion = occasionRepository.findAll().get(0);
        card.setOccasion(occasion);
        byte[] image = new byte[] {1, 1, 1, 1};
        card.setImage(image);
        
        mockMvc.perform(post("/api/cards").contentType("application/json")
                .content(objectMapper.writeValueAsString(card)))
                .andExpect(status().isOk());

        List<Card> cards = cardRepository.findByOccasion(occasion);
        assertThat(cards.size()).isEqualTo(1);
        final Card cardEntity = cards.get(0);
        assertThat(cardEntity.getImage()).isEqualTo(image);
    }
}

解释:

  • @DataJpaTest用于测试你的数据库相关切片 孤立的应用程序(例如存储库)
  • @ExtendWith(SpringExtension.class) 已经是多余的了 @SpringBootTest
  • @ContextConfiguration 是多余的,因为 @SpringBootTest 确保 创建你的上下文

通过此设置,您现在可以启动整个 Spring 上下文(没有嵌入式 servlet 容器)并且可以使用 MockMvc 访问您的端点。确保您已为集成测试配置了对数据库的访问权限,例如您可以为此使用 Testcontainers

如果您也想开始 Tomcat 并使用例如TestRestTemplateWebTestClient 要在没有 MockMvc 的情况下访问您的端点,请使用以下内容:@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)

您可以随时查看注释的源代码以了解它们将在后台启用什么(例如在 IDEA 中 CTRL + 左键单击)。 @SpringBootTest,例如,如下所示:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@BootstrapWith(SpringBootTestContextBootstrapper.class)
@ExtendWith(SpringExtension.class)
public @interface SpringBootTest {

}