使用 H2 进行 Spring Boot 测试 - 未找到表“OAUTH_ACCESS_TOKEN”

Posted

技术标签:

【中文标题】使用 H2 进行 Spring Boot 测试 - 未找到表“OAUTH_ACCESS_TOKEN”【英文标题】:Spring Boot testing with H2 - Table "OAUTH_ACCESS_TOKEN" not found 【发布时间】:2020-04-19 11:55:04 【问题描述】:

我想我需要先设置数据库测试环境(例如,创建表、种子用户,以便可以使用凭据颁发令牌),然后才能运行测试,但不确定如何运行。

@RunWith(SpringRunner.class)
@WebAppConfiguration
@SpringBootTest(classes = Application.class)
public class UsersControllerTest 

    // ...

    private MockMvc mockMvc;

    @Before
    public void setup() 
        this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac)
          .addFilter(springSecurityFilterChain).build();
    

    private String obtainAccessToken(String username, String password) throws Exception 

        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
        params.add("client_id", CLIENTID);
        params.add("grant_type", CLIENTPASSWORD);
        params.add("username", username);
        params.add("password", password);

        ResultActions result = mockMvc.perform(post("/oauth/token")
            .params(params)
            .with(httpBasic(CLIENTID, CLIENTPASSWORD))
            .accept("application/json;charset=UTF-8"))
            .andExpect(status().isOk())
            .andExpect(content().contentType("application/json;charset=UTF-8"));

        String resultString = result.andReturn().getResponse().getContentAsString();

        JacksonJsonParser jsonParser = new JacksonJsonParser();
        return jsonParser.parseMap(resultString).get("access_token").toString();
    

    @Test
    public void givenNoToken_whenGetAllUsers_thenUnauthorized() throws Exception 

        mockMvc.perform(
            get("/users")
            ).andExpect(status().isUnauthorized());
    

    @Test
    public void givenToken_whenGetAllUsers_thenOk() throws Exception 
        String accessToken = obtainAccessToken("martyn", "secret");
        mockMvc.perform(
            get("/users")
                .header("Authorization", "Bearer " + accessToken)
            ).andExpect(status().isOk());
    

    // ...

这是此应用的典型实体:

@Entity(name = "users")
public class User implements Serializable 

    /**
     *
     */
    private static final long serialVersionUID = -8507204786382662588L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Column(nullable = false)
    private String firstName;

    @Column(nullable = false)
    private String surname;

    @Column(nullable = false, unique = true)
    private String email;

    @Column(nullable = false, unique = true)
    private String username;

    @Column(nullable = false)
    @JsonIgnore
    private String password;

    @OneToMany
    @JoinColumn(name="user_id") // cascade = CascadeType.ALL, orphanRemoval = true
    private List<Fund> funds;

    public Long getId() 
        return id;
    

    public String getUsername() 
        return username;
    

    public void setUsername(String username) 
        this.username = username;
    

    public String getPassword() 
        return password;
    

    public void setPassword(String password) 
        this.password = password;
    

    // standard getters and setters

    public String getFirstName() 
        return firstName;
    

    public void setFirstName(String firstName) 
        this.firstName = firstName;
    

    public String getSurname() 
        return surname;
    

    public void setSurname(String surname) 
        this.surname = surname;
    

    public String getEmail() 
        return email;
    

    public void setEmail(String email) 
        this.email = email;
    

    public List<Fund> getFunds() 
        return funds;
    

而且,正如错误所示,我还需要生成这些 oauth* 表。

这是我的 src/test/resources/application.properties

spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:db;DB_CLOSE_DELAY=-1
spring.datasource.username=sa
spring.datasource.password=sa

所以我想我想在运行测试之前在 H2 数据库中生成表(实体和 oauth*)并填充单个用户(?)但似乎无法弄清楚这是如何在 Spring Boot 中完成的.还是我不应该完全访问任何数据库并模拟 JDBC?有人可以为我指出如何在这里准备测试环境的正确方向吗?我有点不知所措。

更新

以下是数据源的配置方式:

@Configuration
public class JDBCTokenConfig 

    @Value("$spring.datasource.url")
    private String datasourceUrl;

    @Value("$spring.datasource.username")
    private String dbUsername;

    @Value("$spring.datasource.password")
    private String dbPassword;

    @Bean
    public DataSource dataSource() 
        final DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setUrl(datasourceUrl);
        dataSource.setUsername(dbUsername);
        dataSource.setPassword(dbPassword);
        return dataSource;
    

    @Bean
    public TokenStore tokenStore(DataSource dataSource) 
        return new JdbcTokenStore(dataSource);
    

    // @Bean
    // public TokenStore tokenStore() 
    //     return new InMemoryTokenStore();
    // 

pom.xml

<dependencies>
    ...
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    ...
</dependencies>

【问题讨论】:

看看 flyway (flywaydb.org) 或 Liquibase (liquibase.org)。两者都允许自动迁移,这也将在开发环境中触发。 感谢您的回复。这些表不是我自己的,它们属于 Spring Security 用于管理令牌。所以我不愿意为这些表编写迁移,但我知道你来自哪里。无论如何,我将发布一个略有不同但更具体的修订版。 【参考方案1】:

我认为无需模拟即可访问您的内存数据库是一件好事。老实说,您将需要更多时间来配置而不是创建数据库所需的正确架构。

使用Spring-boot,很容易配置来测试你的应用程序:

    使用spring-boot-starter-jpa 声明
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
    为您的测试添加内存数据库
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>test</scope>
</dependency>
    删除application.properties 中的spring.datasource 属性

感谢@SpringBootApplication,数据源将自动配置为连接到内存数据库中的 H2。

    创建 SQL 模式 默认情况下,spring-boot-starter-jpa 自动配置数据源以执行脚本classpath:/schema.sql,如果您还需要,classpath:/data.sql。 在src/test/resources中创建一个schema.sql,并创建表(复制以下内容,我认为这是你需要的:https://github.com/spring-projects/spring-security-oauth/blob/master/spring-security-oauth2/src/test/resources/schema.sql)(我不确定这个,也许hibernate自己创建你的架构)。 在src/test/resources/schema.sql 中创建您的架构并在src/test/resources/data.sql 中为用户播种

查看 spring 文档以了解如何配置休眠:

https://docs.spring.io/spring-boot/docs/current/reference/html/appendix-application-properties.html#data-properties https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#reference

现在我明白了,每个人都需要根据您的环境进行不同的配置。最好的办法是依靠配置文件。

在您的情况下,您有一个prod 个人资料和一个test 个人资料。

声明配置文件并将您的 spring.datasource 属性保存在您的 src/test/resources/application.properties 中(我认为在您的情况下更容易)

我建议您阅读此Configure specific in memory database for testing purpose in Spring,如果您在配置环境时遇到问题,请告诉我。

有了这个,你需要:

在测试类的顶部添加注释@ActiveProfiles('test') 恢复您之前删除的spring.datasource 属性并将它们放入src/test/resources/application-test.properties

告诉我

【讨论】:

感谢您的回复。我在 H2 的 pom 文件中缺少 &lt;scope&gt;test&lt;/scope&gt;,所以我添加了它。我已经从 src/test/resources/application.properties 文件中删除了所有内容,但是,我现在看到以下错误 - Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'spring.datasource.url' in value "$spring.datasource.url" - 但对于非测试环境,我仍然需要此属性。顺便说一句,我已经更新了我的帖子以显示它的定义位置。 你能提供你的POM吗?当使用spring-boot-starter-data-jpaspring-boot-starter-jdbc 时,您不需要自己配置Datasource。它将被提供,您可以像使用任何其他常规 bean 一样使用这个“提供”数据源

以上是关于使用 H2 进行 Spring Boot 测试 - 未找到表“OAUTH_ACCESS_TOKEN”的主要内容,如果未能解决你的问题,请参考以下文章

使用 h2database 进行 Spring boot 1.4 测试(在每个测试中回滚)

使用 H2 进行 Spring Boot 测试 - 未找到表“OAUTH_ACCESS_TOKEN”

为啥 H2 进行 spring-boot 测试,但它会将数据写入我的本地 mysql 数据库?

如何告诉 Spring Boot 使用另一个数据库进行测试?

如何在spring boot中创建H2+flyway测试数据库?

使用 gradle 时,在 Spring Boot 测试期间无法加载驱动程序类:“org.h2.Driver”