【问题标题】:Use in-memory db to write tests for Rest controllers使用内存数据库为 Rest 控制器编写测试
【发布时间】:2018-01-02 16:22:03
【问题描述】:

我正在为 Spring Boot Rest 控制器编写测试。这个休息控制器将一些值写入数据库。

我想使用 Spring 为此测试提供的内存数据库。根据this doc我必须用@DataJpaTest注释测试类,这会导致这个错误:

java.lang.IllegalStateException: Failed to load ApplicationContext

在错误堆栈跟踪的更下方,我看到引发了以下异常:

Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration': Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSource': Invocation of init method failed; nested exception is java.lang.IllegalStateException: Failed to replace DataSource with an embedded database for tests. If you want an embedded database please put a supported one on the classpath or tune the replace attribute of @AutoconfigureTestDatabase.

这是我正在研究的测试类:

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
@DataJpaTest
public class AuthenticationControllerFTest {

    @Autowired 
    private MockMvc mockMvc;

    @MockBean
    private AuthenticationManager authenticationManager;

    @Autowired
    private WebApplicationContext context;

    @Autowired
    private Filter springSecurityFilterChain;

    @Before
    public void setup() {
        mockMvc = MockMvcBuilders.webAppContextSetup(context)
                .addFilters(springSecurityFilterChain).build();
    }

    @Test
    public void testCreate() throws Exception {

        String exampleUserInfo = "{\"name\":\"Salam12333\",\"username\":\"test@test1.com\",\"password\":\"Salam12345\"}";
        RequestBuilder requestBuilder = MockMvcRequestBuilders
                .post("/signup")
                .accept(MediaType.APPLICATION_JSON).content(exampleUserInfo)
                .contentType(MediaType.APPLICATION_JSON);

        MvcResult result = mockMvc.perform(requestBuilder).andReturn();

        MockHttpServletResponse response = result.getResponse();
        int status = response.getStatus();
        Assert.assertEquals("http response status is wrong", 200, status);
    }
}

是什么导致了这个错误?

编辑 1 这是我application.properties的内容:

spring.datasource.username = hello
spring.datasource.password = hello
spring.datasource.driver-class-name= com.mysql.jdbc.Driver
spring.datasource.url = jdbc:mysql://localhost:3306/myproject?useSSL=false

spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update
logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type=TRACE
logging.level.org.springframework.web=DEBUG

server.port = 8443
server.ssl.key-store = classpath:tomcat.keystore
server.ssl.key-store-password = hello
server.ssl.key-password = hello
server.ssl.enabled = true
server.ssl.key-alias=myproject

编辑 2

我在pom.xml 中添加了以下内容:

<dependency>
    <groupId>org.hsqldb</groupId>
    <artifactId>hsqldb</artifactId>
    <scope>test</scope>
</dependency>

我创建了application-test.properties,内容如下:

spring.datasource.username= root
spring.datasource.password= password
spring.datasource.driver-class-name= org.h2.Driver
spring.datasource.url= jdbc:h2:mem:db;DB_CLOSE_DELAY=-1
  1. 用户名和密码是什么?我应该在哪里设置它们?
  2. 我还在测试类中添加了@ActiveProfiles("test"),当我运行测试时,我收到一个错误,其中包括这一行:

Caused by: org.hibernate.HibernateException: Access to DialectResolutionInfo cannot be null when 'hibernate.dialect' not set

【问题讨论】:

  • "如果你想要一个嵌入式数据库,请将支持的数据库放在类路径中或调整 @AutoconfigureTestDatabase 的替换属性"。那么,您是否在类路径中放置了嵌入式数据库?
  • 我将我的application.properties 文件内容添加到原始帖子中。
  • application.properties 不影响类路径。您需要将 h2 或 hsqldb 添加到您的 pom.xml。
  • 我加了,请看编辑2。
  • spring.datasource.url= jdbc:h2:mem:db;DB_CLOSE_DELAY=-1 -- h2 不是 hsqldb。请参阅下面的答案..慢慢来,小心。

标签: java spring testing spring-boot


【解决方案1】:

假设您使用@SpringBootApplication 注释类,这将启用自动配置,并且您对类路径有H2 依赖(见下文)Spring Boot 将看到H2 内存数据库依赖,它将创建javax.sql.DataSource执行。默认连接 URL 为jdbc:h2:mem:testdb,默认用户名和密码为:用户名:sa,密码:空。

application.properties 文件

spring.datasource.url=jdbc:h2:mem:tesdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
    spring.datasource.driverClassName=org.h2.Driver
    spring.datasource.username=sa
    spring.datasource.password=

    spring.datasource.testWhileIdle = true
    spring.datasource.validationQuery = SELECT 1

    spring.jpa.show-sql = true
    spring.h2.console.enabled=true // if you need console

H2 依赖

    <dependency>
      <groupId>com.h2database</groupId>
       <artifactId>h2</artifactId>
      <scope>runtime</scope>
   </dependency>

   <dependency> // If you need h2 web console 
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
   </dependency>

您可以访问h2控制台进行管理http://localhost:8080/h2-console

【讨论】:

  • usernamepassword 的值是默认值吗?如果没有,我应该在哪里设置它们?为什么范围是runtime 而不是test
  • @ArianHosseinzadeh 我已经更新了我的答案,凭据是默认的,您可以在 h2 控制台中更改它们,在我的答案中添加了描述。不确定范围 - 运行时对我来说很好。您可以更改以进行测试。
【解决方案2】:

要使用内存数据库测试 REST 服务,您需要执行以下操作:
1.在pom.xml中添加h2依赖

<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>runtime</scope>
    <optional>true</optional>
</dependency>

2。在 application.properties 或 application.yaml 中定义 h2 配置

spring.jpa.database = h2
spring.datasource.url=jdbc:hsqldb:mem:testdb
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.database-platform=org.hibernate.dialect.HSQLDialect
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=create

3。注释你的测试类

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)

完整的代码如下:

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class AuthenticationControllerFTest {

    @Autowired 
    private MockMvc mockMvc;

    @MockBean
    private AuthenticationManager authenticationManager;

    @Autowired
    private WebApplicationContext context;

    @Autowired
    private Filter springSecurityFilterChain;

    @Before
    public void setup() {
        mockMvc = MockMvcBuilders.webAppContextSetup(context)
                .addFilters(springSecurityFilterChain).build();
    }

    @Test
    public void testCreate() throws Exception {

        String exampleUserInfo = "{\"name\":\"Salam12333\",\"username\":\"test@test1.com\",\"password\":\"Salam12345\"}";
        RequestBuilder requestBuilder = MockMvcRequestBuilders
                .post("/signup")
                .accept(MediaType.APPLICATION_JSON).content(exampleUserInfo)
                .contentType(MediaType.APPLICATION_JSON);

        MvcResult result = mockMvc.perform(requestBuilder).andReturn();

        MockHttpServletResponse response = result.getResponse();
        int status = response.getStatus();
        Assert.assertEquals("http response status is wrong", 200, status);
    }
}

【讨论】:

    【解决方案3】:

    删除@AutoConfigureMockMvc 和@DataJpaTest 这两个注解。您正在尝试测试完整的应用程序,因此需要 @SpringBootTest 注释。仅当您只想测试数据应用切片时才需要 @DataJpaTest。看看这个:https://spring.io/blog/2016/04/15/testing-improvements-in-spring-boot-1-4

    【讨论】:

    • 但我不想使用实际的数据库。我想使用内存数据库。
    • 当我删除两个注释时,我得到这个错误:Unsatisfied dependency expressed through field 'mockMvc'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.test.web.servlet.MockMvc' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
    【解决方案4】:

    在spring boot中,我们不需要为内存数据库配置添加任何额外的东西,除了类路径上的jar文件和类路径上的应用程序属性文件(application.properties)(src/test/resources如果使用maven)rest的事情将由弹簧靴(靴子之美)照顾。

    另一种选择是在类路径src/amin/resources 上提供配置文件特定的属性文件(例如application-test.properties

    这两个文件都对测试配置有效

    属性文件的示例配置如下(考虑类路径上的 HSQL DB jar):

    spring.jpa.hibernate.ddl-auto = create-drop
    spring.jpa.database = HSQL
    spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.HSQLDialect
    spring.datasource.driverClassName = org.hsqldb.jdbcDriver
    spring.datasource.url: jdbc:hsqldb:mem:scratchdb
    spring.datasource.username = sa
    spring.datasource.password = pass
    

    【讨论】:

      【解决方案5】:

      也许这会有所帮助。

      spring.datasource.url=jdbc:hsqldb:mem:testdb
      spring.datasource.username=sa
      spring.datasource.password=
      
      spring.jpa.database-platform=org.hibernate.dialect.HSQLDialect
      spring.jpa.show-sql=true
      spring.jpa.hibernate.ddl-auto=create
      

      另见Configure specific in memory database for testing purpose in Spring

      【讨论】:

      • 而且我假设我必须先配置hsqldb,对吧?
      • 有没有办法在Java中设置usernamepassword和创建testdb
      • 数据库在内存中...创建testdb是什么意思? Hibernate 将在启动时从您的实体创建表。如果要使用值预加载它,则必须使用 SQL 脚本。如果您还有其他问题,请搜索 SO 或提出新问题。
      • 我的问题是关于你的回答。 usernamepassword 的值从何而来?据我了解,它尝试使用为属性usernamepassword 提供的值连接到testdb
      • 它们是为 hsqldb 硬编码的。我认为任何数据库名称都可以,请参阅Chapter 1. Running and Using HyperSQL
      【解决方案6】:

      我相信,您可以将以下内存数据库与集成测试一起使用 -

      如果您使用 json[b](尽管它在 DB2 中,但我们的代码不支持插入/更新等操作)数据类型或 DB2 中不存在的任何其他字段(兼容性问题),这也会有所帮助。

      然后参考这个TestContainer - Stackoverflow answer

      Pom.xml

          <dependency>
              <groupId>org.testcontainers</groupId>
              <artifactId>postgresql</artifactId>
              <version>1.15.1</version>
              <scope>test</scope>
          </dependency>
          <dependency>
              <groupId>org.testcontainers</groupId>
              <artifactId>junit-jupiter</artifactId>
              <version>1.15.1</version>
              <scope>test</scope>
          </dependency>
      

      XyzIT.java

      @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
      @ActiveProfiles("test")
      @Testcontainers
      
      mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
      @Test
      void test(){
              var mvcResult = mockMvc.perform(MockMvcRequestBuilders.post("/opportunities/process")
                      .header("emailId", "ravi.parekh@xyz.com")
                      .header("Authorization", "authorization")
                      .header("Content-Type", "application/json").content(objectMapper.writeValueAsString(opportunity))).andReturn();
      
      }
      

      Application-test.yml

        datasource:
          initialization-mode: always
          schema: classpath*:schema-h2.sql  #initial sql script to createDB
          url: jdbc:tc:postgresql:11.9:///
        jpa:
          hibernate.ddl-auto: none
          properties:
            hibernate:
              dialect: org.hibernate.dialect.PostgreSQLDialect
              format_sql: true
              default_schema: public
          show-sql: true
      

      【讨论】:

        猜你喜欢
        • 2018-08-26
        • 1970-01-01
        • 2020-10-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-10
        相关资源
        最近更新 更多