Spring Boot 2.1.1:java.lang.IllegalStateException:运行单元测试时无法检索@EnableAutoConfiguration 基本包错误

Posted

技术标签:

【中文标题】Spring Boot 2.1.1:java.lang.IllegalStateException:运行单元测试时无法检索@EnableAutoConfiguration 基本包错误【英文标题】:Spring Boot 2.1.1 : java.lang.IllegalStateException: Unable to retrieve @EnableAutoConfiguration base packages error when running unit test 【发布时间】:2019-06-02 21:44:47 【问题描述】:

执行单元测试时会抛出以下错误。请告知我是否错过了什么。我正在使用 Spring Boot 2.1.1.RELEASE。谢谢!

java.lang.IllegalStateException:无法检索 @EnableAutoConfiguration 基础包

application-test.yml

spring:
  profiles: test
  datasource:
    driver-class-name: org.h2.Driver
    url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1
    username : xxx
    password : xxx
  jpa:
    hibernate:
      ddl-auto: update
  cache:
    type: simple

AppRepository.java

@Repository
public interface AppRepository extends CrudRepository<App, Integer> 

    App findFirstByAppId(String appId);

   

AppRepositoryTest.java

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = AppRepository.class)
@EnableConfigurationProperties
@DataJpaTest
@ActiveProfiles("test")
public class AppRepositoryTest 

    @Autowired
    AppRepository appRepository;

    @Before
    public void setUp() throws Exception 
        App app = new App();
        app.setAppId("testId");
        appRepository.save(app);
    

    @Test
    public void testFindFirstByAppId() 
        assertNotNull(appRepository.findFirstByAppId("testId"));        
    

包结构

└───src
    ├───main
    │   ├───java
    │   │   └───com
    │   │       └───abc
    │   │           └───app
    │   │               ├───config
    │   │               ├───data
    │   │               │   ├───model
    │   │               │   └───repository
    │   │               ├───exception
    │   │               ├───service
    │   │               └───serviceImpl
    │   └───resources
    │       ├───META-INF
    │       └───static
    │           ├───css
    │           ├───images
    │           └───js
    └───test
        └───java
            └───com
                └───abc
                    └───app
                        ├───data
                        │   └───repository
                        ├───service
                        └───serviceImpl

【问题讨论】:

你能展示你的包结构吗?为什么要设置 ContextConfiguration 和 EnableConfigurationProperties? 已编辑显示包的问题。谢谢。在我的测试中,如果我删除 EnableConfigurationProperties,则无法读取 test.yml 属性文件。如果我删除 ContextConfiguration,我将收到“无法找到 SpringBootConfiguration”错误。 test.yml的名字应该是application-test.xml 抱歉,已经是 application-test.yml。应该完整拼写。 【参考方案1】:

我尝试了 Zaccus 的解决方案,但这对我不起作用。我正在使用 Spring Boot 2.3.2.RELEASE 和 JUnit 5。对于我的情况,我需要将我的模型和存储库移动到一个单独的库中,因为它需要由我的 webapp 和一个工具共享。

下面是我要工作的内容:

没有 main 或 SpringApplication 的 Spring Boot JPA 测试

package com.example.repository;

import com.example.model.Place;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.ContextConfiguration;

import javax.persistence.EntityManager;
import javax.sql.DataSource;
import java.util.Optional;

import static org.assertj.core.api.Assertions.assertThat;

@DataJpaTest
@ContextConfiguration(classes=PlaceRepositoryTest.class)
@EnableJpaRepositories(basePackages = "com.example.*")
@EntityScan("com.example.model")
public class PlaceRepositoryTest 
    @Autowired private DataSource dataSource;
    @Autowired private JdbcTemplate jdbcTemplate;
    @Autowired private EntityManager entityManager;
    @Autowired private PlaceRepository repo;

    @Test
    void testInjectedComponentsAreNotNull()
        assertThat(dataSource).isNotNull();
        assertThat(jdbcTemplate).isNotNull();
        assertThat(entityManager).isNotNull();
        assertThat(repo).isNotNull();
    

    @Test
    public void testInsert() throws Exception 
        String placeName = "San Francisco";
        Place p = new Place(null, placeName);

        repo.save(p);
        Optional<Place> op = repo.findByName(placeName);

        assertThat(op.isPresent()).isTrue();
    

从 Spring Boot 2.1 开始,使用@DataJpaTest 时,不再需要指定

@ExtendWith(SpringExtension.class)
@EnableJpaRepositories(basePackages = "com.example.*")

对于我的情况,basePackages = "com.example.*" 不是必需的,因为 PlaceRepository 和 PlaceRepositoryTest 在同一个包中。我只是在这里添加它,以防有人进行了包含在不同包中找到的存储库的测试。如果没有“basePackages”,@EnableJpaRepositories 将默认扫描注解的配置类的包以查找 Spring Data 存储库。

最初,我只有以下注释:

@DataJpaTest
@ContextConfiguration(classes=PlaceRepositoryTest.class)
@EnableJpaRepositories(basePackages = "com.example.*")

我发现的网站说我只需要@DataJpaTest 和@EnableJpaRepositories,但是,只有上面的,我得到了以下错误:

java.lang.IllegalStateException: Failed to load ApplicationContext
:
:
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'placeRepository' defined in com.example.repository.PlaceRepository defined in @EnableJpaRepositories declared on PlaceRepositoryTest: Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Not a managed type: class com.example.model.Place

我花了一段时间才弄明白。对于“非托管类型”,我认为我的班级地点有问题:

package com.example.model;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@NoArgsConstructor
@AllArgsConstructor
@Data
@Entity
public class Place 
    @Id
    @GeneratedValue(strategy= GenerationType.AUTO)
    private Long id;
    private String name;

根本原因是地方未被作为实体扫描。为了解决这个问题,我需要添加

@EntityScan("com.example.model")

我在 *** 上的另一个解决方案中找到了“@EntityScan”:Spring boot - Not an managed type

以下是我的设置:

src
 + main
    + java
       + com.example
          + model
             + Place
          + repository
             + PlaceRepository
 + test
    + java
       + com.example
          + repository
             + PlaceRepository
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>jpa</artifactId>
    <version>1.0-SNAPSHOT</version>

    <packaging>jar</packaging>

    <properties>
        <spring.boot.starter.version>2.3.2.RELEASE</spring.boot.starter.version>
        <h2.version>1.4.200</h2.version>
        <lombok.version>1.18.12</lombok.version>
        <maven.compiler.target>1.8</maven.compiler.target>
        <maven.compiler.source>1.8</maven.compiler.source>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <version>$spring.boot.starter.version</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
            <version>$spring.boot.starter.version</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>$lombok.version</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <version>$spring.boot.starter.version</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>$h2.version</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
package com.example.repository;

import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import com.example.model.Place;

import java.util.Optional;

@Repository
public interface PlaceRepository extends CrudRepository<Place, Long> 
    Optional<Place> findByName(String name);

【讨论】:

【参考方案2】:

当我删除“ActiveProfiles”和“EnableConfigurationProperties”并最终在 ContextConfiguration 注释中指定 Main 类时,我设法让它工作:

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = AppMain.class)
@DataJpaTest
public class AppRepositoryTest 

    @Autowired
    AppRepository appRepository;

    @Before
    public void setUp() throws Exception 
        App app = new App();
        app.setAppId("testId");
        appRepository.save(app);
    

    @Test
    public void testFindFirstByAppId() 
        assertNotNull(appRepository.findFirstByAppId("testId"));        
    

【讨论】:

【参考方案3】:

根据 45.3 Testing Spring Boot Applications 文档,启用 Spring Boot 功能(如 @EnableAutoConfiguration)的推荐方法是使用 @SpringBootTest 而不是旧的 @ContextConfiguration

Spring Boot 提供了一个 @SpringBootTest 注解,当你需要 Spring Boot 特性时,它可以作为标准 spring-test @ContextConfiguration 注解的替代品。注释通过 SpringApplication 创建测试中使用的 ApplicationContext 来工作。除了@SpringBootTest 之外,还提供了许多其他注释来测试应用程序的更具体的切片。

您可以尝试使用 @ContextConfiguration 编写测试,这是部分 Spring Boot 设置,但您会遇到类似的问题。 Spring Boot 很大程度上基于约定,例如组件扫描从包含@SpringBootApplication 注释类的包开始。不建议违反这些约定。

【讨论】:

感谢您的回复。我确实尝试过 SpringBootTest,但是当我将它与 DataJpaTest 一起使用时,我会收到错误“java.lang.IllegalStateException: Configuration error: found multiple declarations of BootstrapWith”使用 SpringBoot 2.x,我没有收到此错误。跨度> 这并不完全正确,因为您可以使用测试切片技术 (baeldung.com/spring-tests#5-using-test-slices)。 SpringBootTest 将加载整个应用程序以运行您的测试。这就是上面@Zaccus 通知的错误发生的原因。在测试中定义 ContextConfiguration 的其他原因是使用自定义加载器,即 MockitoLoader (github.com/desiderati/commons/blob/master/common-test/src/main/…)。

以上是关于Spring Boot 2.1.1:java.lang.IllegalStateException:运行单元测试时无法检索@EnableAutoConfiguration 基本包错误的主要内容,如果未能解决你的问题,请参考以下文章

Spring 5.x Spring Boot 2.x Spring Cloud 与常用技术栈整合

Spring Boot内嵌Tomcat原理

Spring Boot学习篇

Spring Boot Kafka

Spring Boot Actuator [监控与管理]

Spring boot 2.1.1 到 2.1.2:创建名称为“payloadRootAnnotationMethodEndpointMapping”的 bean 时出错