资讯专栏INFORMATION COLUMN

Spring、Spring Boot和TestNG测试指南 - 测试关系型数据库

Meils / 3006人阅读

摘要:地址提供了对的支持,能够让我们很方便对关系型数据库做集成测试。如果想要在打包的时候跳过集成测试,只需要。例子使用因为使用了来做集成测试,得益于其机制,不需要自己构建和的。

Github地址

Spring Test Framework提供了对JDBC的支持,能够让我们很方便对关系型数据库做集成测试。

同时Spring Boot提供了和Flyway的集成支持,能够方便的管理开发过程中产生的SQL文件,配合Spring已经提供的工具能够更方便地在测试之前初始化数据库以及测试之后清空数据库。

本章节为了方便起见,本章节使用了H2作为测试数据库。

注意:在真实的开发环境中,集成测试用数据库应该和最终的生产数据库保持一致,这是因为不同数据库的对于SQL不是完全相互兼容的,如果不注意这一点,很有可能出现集成测试通过,但是上了生产环境却报错的问题。

因为是集成测试,所以我们使用了maven-failsafe-plugin来跑,它和maven-surefire-plugin的差别在于,maven-failsafe-plugin只会搜索*IT.java来跑测试,而maven-surefire-plugin只会搜索*Test.java来跑测试。

如果想要在maven打包的时候跳过集成测试,只需要mvn clean install -DskipITs

被测试类

先介绍一下被测试的类。

Foo.java:

public class Foo {

  private String name;

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }
}

FooRepositoryImpl.java:

@Repository
public class FooRepositoryImpl implements FooRepository {

  private JdbcTemplate jdbcTemplate;

  @Override
  public void save(Foo foo) {
    jdbcTemplate.update("INSERT INTO FOO(name) VALUES (?)", foo.getName());
  }

  @Override
  public void delete(String name) {
    jdbcTemplate.update("DELETE FROM FOO WHERE NAME = ?", name);
  }

  @Autowired
  public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
    this.jdbcTemplate = jdbcTemplate;
  }

}
例子1:不使用Spring Testing提供的工具

Spring_1_IT_Configuration.java:

@Configuration
@ComponentScan(basePackageClasses = FooRepository.class)
public class Spring_1_IT_Configuration {

  @Bean(destroyMethod = "shutdown")
  public DataSource dataSource() {

    return new EmbeddedDatabaseBuilder()
        .generateUniqueName(true)
        .setType(EmbeddedDatabaseType.H2)
        .setScriptEncoding("UTF-8")
        .ignoreFailedDrops(true)
        .addScript("classpath:me/chanjar/domain/foo-ddl.sql")
        .build();
  }

  @Bean
  public JdbcTemplate jdbcTemplate() {

    return new JdbcTemplate(dataSource());

  }
}

Spring_1_IT_Configuration中,我们定义了一个H2的DataSource Bean,并且构建了JdbcTemplate Bean。

注意看addScript("classpath:me/chanjar/domain/foo-ddl.sql")这句代码,我们让EmbeddedDatabase执行foo-ddl.sql脚本来建表:

CREATE TABLE FOO (
  name VARCHAR2(100)
);

Spring_1_IT.java:

@ContextConfiguration(classes = Spring_1_IT_Configuration.class)
public class Spring_1_IT extends AbstractTestNGSpringContextTests {

  @Autowired
  private FooRepository fooRepository;

  @Autowired
  private JdbcTemplate jdbcTemplate;

  @Test
  public void testSave() {

    Foo foo = new Foo();
    foo.setName("Bob");
    fooRepository.save(foo);

    assertEquals(
        jdbcTemplate.queryForObject("SELECT count(*) FROM FOO", Integer.class),
        Integer.valueOf(1)
    );

  }

  @Test(dependsOnMethods = "testSave")
  public void testDelete() {

    assertEquals(
        jdbcTemplate.queryForObject("SELECT count(*) FROM FOO", Integer.class),
        Integer.valueOf(1)
    );

    Foo foo = new Foo();
    foo.setName("Bob");
    fooRepository.save(foo);

    fooRepository.delete(foo.getName());
    assertEquals(
        jdbcTemplate.queryForObject("SELECT count(*) FROM FOO", Integer.class),
        Integer.valueOf(0)
    );
  }

}

在这段测试代码里可以看到,我们分别测试了FooRepositorysavedelete方法,并且利用JdbcTemplate来验证数据库中的结果。

例子2:使用Spring Testing提供的工具

在这个例子里,我们会使用JdbcTestUtils来辅助测试。

Spring_2_IT_Configuration.java:

@Configuration
@ComponentScan(basePackageClasses = FooRepository.class)
public class Spring_2_IT_Configuration {

  @Bean
  public DataSource dataSource() {

    EmbeddedDatabase db = new EmbeddedDatabaseBuilder()
        .generateUniqueName(true)
        .setType(EmbeddedDatabaseType.H2)
        .setScriptEncoding("UTF-8")
        .ignoreFailedDrops(true)
        .addScript("classpath:me/chanjar/domain/foo-ddl.sql")
        .build();
    return db;
  }

  @Bean
  public JdbcTemplate jdbcTemplate() {

    return new JdbcTemplate(dataSource());

  }

  @Bean
  public PlatformTransactionManager transactionManager() {
    return new DataSourceTransactionManager(dataSource());
  }

}

这里和例子1的区别在于,我们提供了一个PlatformTransactionManager Bean,这是因为在下面的测试代码里的AbstractTransactionalTestNGSpringContextTests需要它。

Spring_2_IT.java:

@ContextConfiguration(classes = Spring_2_IT_Configuration.class)
public class Spring_2_IT extends AbstractTransactionalTestNGSpringContextTests {

  @Autowired
  private FooRepository fooRepository;

  @Test
  public void testSave() {

    Foo foo = new Foo();
    foo.setName("Bob");
    fooRepository.save(foo);

    assertEquals(countRowsInTable("FOO"), 1);
    countRowsInTableWhere("FOO", "name = "Bob"");
  }

  @Test(dependsOnMethods = "testSave")
  public void testDelete() {

    assertEquals(countRowsInTable("FOO"), 0);

    Foo foo = new Foo();
    foo.setName("Bob");
    fooRepository.save(foo);

    fooRepository.delete(foo.getName());
    assertEquals(countRowsInTable("FOO"), 0);

  }

}

在这里我们使用countRowsInTable("FOO")来验证数据库结果,这个方法是AbstractTransactionalTestNGSpringContextTestsJdbcTestUtils的代理。

而且要注意的是,每个测试方法在执行完毕后,会自动rollback,所以在testDelete的第一行里,我们assertEquals(countRowsInTable("FOO"), 0),这一点和例子1里是不同的。

更多关于Spring Testing Framework与Transaction相关的信息,可以见Spring官方文档 Transaction management。

例子3:使用Spring Boot

Boot_1_IT.java:

@SpringBootTest
@SpringBootApplication(scanBasePackageClasses = FooRepository.class)
public class Boot_1_IT extends AbstractTransactionalTestNGSpringContextTests {

  @Autowired
  private FooRepository fooRepository;

  @Test
  public void testSave() {

    Foo foo = new Foo();
    foo.setName("Bob");
    fooRepository.save(foo);

    assertEquals(countRowsInTable("FOO"), 1);
    countRowsInTableWhere("FOO", "name = "Bob"");
  }

  @Test(dependsOnMethods = "testSave")
  public void testDelete() {

    assertEquals(countRowsInTable("FOO"), 0);

    Foo foo = new Foo();
    foo.setName("Bob");
    fooRepository.save(foo);

    fooRepository.delete(foo.getName());
    assertEquals(countRowsInTable("FOO"), 0);

  }
  
  @AfterTest
  public void cleanDb() {
    flyway.clean();
  }
  
}

因为使用了Spring Boot来做集成测试,得益于其AutoConfiguration机制,不需要自己构建DataSourceJdbcTemplatePlatformTransactionManager的Bean。

并且因为我们已经将flyway-core添加到了maven依赖中,Spring Boot会利用flyway来帮助我们初始化数据库,我们需要做的仅仅是将sql文件放到classpath的db/migration目录下:

V1.0.0__foo-ddl.sql:

CREATE TABLE FOO (
  name VARCHAR2(100)
);

而且在测试最后,我们利用flyway清空了数据库:

@AfterTest
public void cleanDb() {
  flyway.clean();
}

使用flyway有很多好处:

每个sql文件名都规定了版本号

flyway按照版本号顺序执行

在开发期间,只需要将sql文件放到db/migration目录下就可以了,不需要写类似EmbeddedDatabaseBuilder.addScript()这样的代码

基于以上三点,就能够将数据库初始化SQL语句也纳入到集成测试中来,保证代码配套的SQL语句的正确性

可以帮助你清空数据库,这在你使用非内存数据库的时候非常有用,因为不管测试前还是测试后,你都需要一个干净的数据库

参考文档

本章节涉及到的Spring Testing Framework JDBC、SQL相关的工具:

Transaction management

Executing SQL scripts

和flyway相关的:

flyway的官方文档

flway和spring boot的集成

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/70206.html

相关文章

  • SpringSpring BootTestNG测试指南 - 集成测试中用Docker创建数据

    摘要:我们还是会以测试关系型数据库里的来做集成测试代码在这里。这个很有用,如果集成测试失败,那么你还可以连接到数据库查看情况。 原文地址 在测试关系型数据库一篇里我们使用的是H2数据库,这是为了让你免去你去安装/配置一个数据库的工作,能够尽快的了解到集成测试的过程。 在文章里也说了: 在真实的开发环境中,集成测试用数据库应该和最终的生产数据库保持一致 那么很容易就能想到两种解决方案: 开发...

    sshe 评论0 收藏0
  • SpringSpring BootTestNG测试指南 - 使用Mockito

    摘要:例子使用源代码我们先给了一个的实现然后又规定了方法的返回值。源代码也就是说,得益于,我们能够很方便地对依赖关系中任意层级的任意做。 Github地址 Mock测试技术能够避免你为了测试一个方法,却需要自行构建整个依赖关系的工作,并且能够让你专注于当前被测试对象的逻辑,而不是其依赖的其他对象的逻辑。 举例来说,比如你需要测试Foo.methodA,而这个方法依赖了Bar.methodB,...

    Alliot 评论0 收藏0
  • SpringSpring BootTestNG测试指南 - 测试Spring MVC

    摘要:地址提供了,能够很方便的来测试。同时也提供了更进一步简化了测试需要的配置工作。本章节将分别举例说明在不使用和使用下如何对进行测试。例子测试的关键是使用对象,利用它我们能够在不需启动容器的情况下测试的行为。 Github地址 Spring Testing Framework提供了Spring MVC Test Framework,能够很方便的来测试Controller。同时Spring...

    andong777 评论0 收藏0
  • SpringSpring BootTestNG测试指南 - @OverrideAutoConfi

    摘要:因为只有这样才能够在测试环境下发现生产环境的问题,也避免出现一些因为配置不同导致的奇怪问题。而方法则能够不改变原有配置不提供新的配置的情况下,就能够关闭。 Github地址 在Chapter 1: 基本用法 - 使用Spring Boot Testing工具里提到: 除了单元测试(不需要初始化ApplicationContext的测试)外,尽量将测试配置和生产配置保持一致。比如如果生产...

    elisa.yang 评论0 收藏0
  • SpringSpring BootTestNG测试指南 - 使用Spring Boot Test

    摘要:地址前面一个部分讲解了如何使用工具来测试项目,现在我们讲解如何使用工具来测试项目。所以我们可以利用这个特性来进一步简化测试代码。因为只有这样才能够在测试环境下发现生产环境的问题,也避免出现一些因为配置不同导致的奇怪问题。 Github地址 前面一个部分讲解了如何使用Spring Testing工具来测试Spring项目,现在我们讲解如何使用Spring Boot Testing工具来测...

    Anshiii 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<