一站式做网站服务品牌网站建设推荐乐云seo
web/
2025/10/9 7:32:25/
文章来源:
一站式做网站服务,品牌网站建设推荐乐云seo,网站开发过程的基本环节,宜兴做网站哪个好如果您曾经编写过测试数据库交互的代码#xff0c;例如数据访问对象#xff0c;那么您很可能遇到了测试中最长期的烦恼之一#xff1a;为了准确地测试这些交互#xff0c;需要一个数据库。 为了本文的方便#xff0c;让我们考虑一个将PostgreSQL用作其环境的一部分的应用… 如果您曾经编写过测试数据库交互的代码例如数据访问对象那么您很可能遇到了测试中最长期的烦恼之一为了准确地测试这些交互需要一个数据库。 为了本文的方便让我们考虑一个将PostgreSQL用作其环境的一部分的应用程序因为这是示例所使用的。 同样尽管H2被广泛提及但这绝不是要贬低它的意思-在正确的位置使用它是一个很好的工具。 问题 已经提出了解决该问题的各种方法但是似乎总是存在一些缺点。 一种测试方法是使用内存数据库例如H2。 优点 数据库在虚拟机本地 数据库生命周期由构建过程管理 初始状态由构建过程或测试管理 缺点 您没有准确地对环境建模 并非支持生产数据库的所有功能 不同的数据类型意味着不同的列定义 涉及相同表的多个测试不能并行运行而不会产生冲突 如果您认为这些约束是不可接受的则可以考虑保留一个众所周知的正在运行的PostgreSQL数据库实例用于测试。 优点 与生产数据库100兼容 缺点 不保证初始数据状态 同一版本中涉及相同表的多个测试不能并行运行而不会产生冲突 并行构建可能导致结果不一致 运行本地测试的开发人员可能会破坏持续的集成构建 这种方法的进一步改进是使每个开发人员都拥有自己的PostgreSQL数据库实例。 优点 与生产数据库100兼容 开发人员构建不会干扰持续集成构建 缺点 不保证初始数据状态 同一版本中涉及相同表的多个测试不能并行运行而不会产生冲突 并行构建可能导致结果不一致 开发人员必须保持其数据库实例为最新或必须添加工具来对此进行管理 使用这些方法中的每一种我都认为不利之处足以部分或完全抵消优点。 外卖 分解最后三段我们可以看到以下功能是理想的 数据库应绑定到测试而不是虚拟机 这意味着现在可以进行测试并行化了 数据库生命周期应由内部版本管理 数据库应与生产中使用的数据库相同 我最喜欢的新解决方案 使用TestContainers 我们可以勾选每个功能。 使用JUnit Rule TestContainers将启动每个测试的Docker映像该映像提供一个寿命与测试一样长的数据库。 由于每个Docker实例都是完全隔离的因此可以并行运行测试以加快构建速度。 最后一点非常重要因为如上所述似乎总是存在一些缺点。 在这种情况下启动Docker映像及其包含的所有内容的开销将增加您的总体构建时间。 我会并且确实认为增加的测试时间几乎不会影响拥有我们所有所需功能的好处。 TestContainers开箱即用支持的每个数据库都有一个特定规则该规则可用于获取连接到数据库所需的所有详细信息。 public class FooDaoTest {Rulepublic PostgreSQLContainer postgres new PostgreSQLContainer();Beforepublic void setUp() {// populate database// postgres.getDriverClassName()// postgres.getJdbcUrl()// postgres.getUsername()// postgres.getPassword()}
}或者... 根据文档 可以通过将JDBC URL更改为包含tc:来启动新容器例如jdbc:tc:postgresql://hostname/databasename 。 但是由于驱动程序中存在这一行 因此在我的应用程序中失败了。 if (!url.startsWith(jdbc:postgresql:)) { 轶事 在这里我花了10分钟时间将应用程序从使用H2切换为使用Dockerized PostgreSQL这使我的生活变得更加简单。 我们使用jOOQ进行数据库交互发现自己不得不删除一些非常好的jOOQ功能因为H2不支持它们。 让我重复一遍。 由于测试环境的限制我们面临着不断变化的生产代码 。 那是不可能的而且永远不会是一个可以接受的情况因此TestContainers的发现既是偶然的也是节省时间的。 太好了因为它确实提供了我们所需的东西但是省时 我刚才说这会增加测试时间怎么说呢 很简单–我不需要花时间查看是否有H2模式可以支持我正在使用的功能。 我发现自己写的代码以后必须删除因为H2不允许这样做 我可以编写测试和与DB相关的代码然后就完成了。 哇您没有提到Play的整个博客文章吗 不。 根据我刚刚提到的应用程序这是在Play上使用它的一种简便方法。 首先创建一个将TestContainer与Play的数据库支持结合在一起的mixin。 package be.objectify.tcexample.db;import com.google.common.collect.ImmutableMap;
import org.testcontainers.containers.PostgreSQLContainer;
import play.db.Database;
import play.db.Databases;
import play.db.evolutions.Evolutions;public interface DbTestSupport {default Database create(final PostgreSQLContainer postgres) throws Exception {final Database database Databases.createFrom(default,postgres.getDriverClassName(),postgres.getJdbcUrl(),ImmutableMap.of(username, postgres.getUsername(),password, postgres.getPassword()));Evolutions.applyEvolutions(database);return database;}default void destroy(final Database database) {Evolutions.cleanupEvolutions(database);database.shutdown();}
} 我在这里使用mixin的原因是因为倾向于在接口旁边定义DAO测试-请参阅我的[上一篇文章]http://www.objectify.be/wordpress/2013/06/01/a-good-lazy-way -to-write-tests /。 如果可以将测试定义为mixins那就更好了因为可以将通用数据库设置代码放入一个通用类中然后可以将其扩展以实现测试mixins但是JUnit无法识别以这种方式定义的测试。 因此抽象测试类不知道它具有需要数据库的实现-它仅测试接口的约定。 package be.objectify.tcexample;import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;public abstract AbstractUserDaoTest {Testpublic void testFoo() {assertThat(dao().something()).isEqualTo(whatever);}// many, many more testspublic abstract UserDao dao();
} 通过我们特定于数据库的实现作为后盾我们现在可以确保我们的实现按照合同要求的方式运行。 package be.objectify.tcexample.db;import be.objectify.tcexample.AbstractUserDaoTest;
import be.objectify.tcexample.UserDao;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.testcontainers.containers.PostgreSQLContainer;
import play.db.Database;public class JooqUserDaoTest extends AbstractUserDaoTest implements DbTestSupport,TestData {Rulepublic PostgreSQLContainer postgres new PostgreSQLContainer();private Database database;Beforepublic void setup() throws Exception {// the database has all evolutions applieddatabase create(postgres); // load some test dataloadTestData(database); }Afterpublic void tearDown() {destroy(database);}Overridepublic UserDao dao() {return new JooqUserDao(database);}
} 现在我们的JooqUserDao实现将针对生产中使用的数据库类型的真实实例运行。 JooqUserDaoTest使用的TestData接口只是将某些数据加载到数据库中的另一个mixin。 实现并不是特别重要因为它很大程度上取决于您自己的要求但是它看起来可能像这样。 package be.objectify.tcexample.db;import org.jooq.impl.DSL;
import play.db.Database;import java.sql.Connection;
import java.sql.Timestamp;
import java.time.Instant;import static be.objectify.tcexample.db.jooq.generated.Tables.ACCOUNT;public interface TestData {default void loadTestData(Database database) {database.withConnection((Connection conn) - {DSL.using(conn).insertInto(ACCOUNT,ACCOUNT.ID,ACCOUNT.KEY,ACCOUNT.CREATED_ON).values(1,test-account-a,Timestamp.from(Instant.now())).execute();DSL.using(conn).insertInto(ACCOUNT,ACCOUNT.ID,ACCOUNT.KEY,ACCOUNT.CREATED_ON).values(2,test-account-b,Timestamp.from(Instant.now())).execute();});}
}翻译自: https://www.javacodegeeks.com/2017/03/database-testing-testcontainers.html
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/89507.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!