嵌入式 PostgreSQL for Java JUnit 测试

Posted

技术标签:

【中文标题】嵌入式 PostgreSQL for Java JUnit 测试【英文标题】:Embedded PostgreSQL for Java JUnit tests 【发布时间】:2012-12-28 03:39:00 【问题描述】:

是否有嵌入式 PostgreSql 以便我们可以对 PostgreSql 驱动的应用程序进行单元测试?

由于 PostgreSql 有一些方言,所以最好使用嵌入式 PostgreSql 本身而不是其他嵌入式数据库。

Embedded 并不一定意味着它必须嵌入到 JVM 进程中。它也不一定需要使用内存中的持久性。它应该由依赖管理(Maven、Gradle)自动加载,以便单元测试可以在每台机器上运行,而无需安装和配置本地 PostgreSQL 服务器。

【问题讨论】:

【参考方案1】:

这是一个“嵌入式”PostgresSQL 服务器,专为从 Java 进行单元测试而设计:

https://github.com/yandex-qatools/postgresql-embedded

嵌入式 postgresql 将为在单元测试中运行 postgres 二进制文件提供一种平台中立的方式。大部分代码都是从Flapdoodle OSS's embed process

顺便说一句,Mongo、Redis、Memcached 和 nodejs 也存在类似的项目。

【讨论】:

为什么没有标记为正确答案?这个库有缺点吗?它具有 postgre 的所有功能吗?可以用模块等装饰吗? 我可以证明它运作良好。标准 Postgres。用法示例:github.com/icgc-dcc/dcc-submission/blob/develop/…github.com/icgc-dcc/dcc-submission/blob/… “嵌入”的含义实际上还不清楚,但对我来说,它就像 H2 一样在内存中运行。而 yandex-qatools/postgresql-embedded 不符合这个定义。实际上它会下载指定的 postgres 版本,将其解压缩到临时目录并启动服务器,所有这些都会导致磁盘开销,这在非企业环境中是难以接受的。我正在判断这个示例项目github.com/scottmf/postgresql-integration-test,它仍然是一个很好的例子。如果我错了,请纠正我 我团队的一些成员实施了github.com/opentable/otj-pg-embedded,它似乎为他们提供了很好的服务。使用 Spring 配置时,它具有更清晰的界面。 如果您尝试在默认配置之外使用此解决方案(从其他地方下载包等),此解决方案会出现问题【参考方案2】:

不,在进程内可加载数据库即库的意义上,没有嵌入式 PostgreSQL。 PostgreSQL 是面向进程的;每个后端都有一个线程,它会产生多个进程来完成工作。作为图书馆,它没有意义。

The H2 database支持a limited subset of the PostgreSQL SQL dialect和PgJDBC驱动的使用。

可以做的是initdb a new temporary database,在随机端口上用pg_ctl启动它,这样它就不会与其他实例冲突,运行你的测试,然后用pg_ctl停止最后删除临时数据库。

强烈建议您在非默认端口上运行临时 postgres, 这样您就不会冒险与运行测试的机器上任何本地安装的 PostgreSQL 发生冲突.

(is "在ecpg 的意义上嵌入了 PostgreSQL,本质上是一个 PostgreSQL client 嵌入在 C source code 中作为基于预处理器C 语言扩展。它仍然需要一个正在运行的服务器,而且使用起来有点讨厌,不太推荐。它的存在主要是为了使从其他各种数据库的移植更容易。)

【讨论】:

我也会这样做。在没有任何安装程序的情况下设置 Postgres 的批处理文件/shell 脚本大约需要 5 行代码。尽管在每个单元测试运行中执行 initdb 可能会太慢。 @a_horse_with_no_name 是的,如果在构建过程中丢失了 initdb,您可以随时解压缩预 initdb 的数据库或从干净的​​模板中复制。不过升级 Pg 时会很痛苦。就我个人而言,我发现 initdb 足够快,但我不再使用旋转磁盘了。至于批处理文件 - 主要挑战是确保您使用非冲突端口(特别是在运行并发测试时)并确保在完成后终止服务器。特别是在 Windows 上,在服务器停止之前您无法删除数据目录。 您说“将面向过程的部分打包为库没有意义”。为什么不?现在,您可以使用 Java 库自动化 IE。当然,进程将在幕后产生,但您仍然可以轻松地与 Java 库交谈来实现它。 PostgreSQL 应该也是如此。你可以通过嵌入一个 PostgreSQL 安装程序来接近这个目标,但是有很多样板只是为了设置它。 PostgreSQL 是否可以即时启动,无论它之前是否已安装在机器上?也许这就是 OP 所需要的。我知道这就是我需要的。我不希望我的单元测试依赖于预安装的 PostgreSQL,因为这意味着依赖于不同团队中进程的正确运行,特别是维护 TeamCity 代理的团队。另外,我希望新的过程方法允许完整的服务器配置,这在预先存在的安装中通常是困难的(可能作为 root)。 @MihaiDanila 绝对是,这就是我的建议。只需捆绑 Pg 二进制文件的 .zip,然后按需使用 pg_ctl 启动 Pg。您可以在initdbinitdb 参数之后编辑配置,通过环境变量或参数控制端口等内容到pg_ctl 等。【参考方案3】:

我尝试了@btiernay (yandex-qatools) 建议的项目。我花了好几天的时间,没有任何冒犯,这是过度设计的解决方案,在我的情况下不起作用,因为我想从内部存储库下载二进制文件而不是去公共互联网。理论上它支持它,但实际上它不支持。

OpenTable 嵌入式 PostgreSQL 组件

我最终使用了otj-pg-embedded,它就像一个魅力。在 cmets 中提到过,所以我想我也会在这里提一下。

我将它用作独立数据库,而不是通过规则用于单元测试和本地开发。

依赖关系:

<dependency>
    <groupId>com.opentable.components</groupId>
    <artifactId>otj-pg-embedded</artifactId>
    <version>0.7.1</version>
</dependency>

代码:

@Bean
public DataSource dataSource(PgBinaryResolver pgBinaryResolver) throws IOException 
    EmbeddedPostgres pg = EmbeddedPostgres.builder()
        .setPgBinaryResolver(pgBinaryResolver)
        .start();


    // It doesn't not matter which databse it will be after all. We just use the default.
    return pg.getPostgresDatabase();


@Bean
public PgBinaryResolver nexusPgBinaryResolver() 
    return (system, machineHardware) -> 
        String url = getArtifactUrl(postgrePackage, system + SEPARATOR + machineHardware);
        log.info("Will download embedded Postgre package from: ", url);

        return new URL(url).openConnection().getInputStream();
    ;


private static String getArtifactUrl(PostgrePackage postgrePackage, String classifier) 
    // Your internal repo URL logic

【讨论】:

是否可以从otj-pg-embedded 中的.sql 文件创建架构。我没有使用弹簧。没有Rule,我没有找到任何示例。 见 dzone.com 文章:Using Embedded PostgreSQL Databases For Testing 多年来一直使用 otj-pg-embedded 并取得了巨大的成功。像魅力一样工作【参考方案4】:

您可以使用 PostgreSQL 的 container 实例。 由于旋转容器只需几秒钟,这对于单元测试应该足够了。 此外,如果您需要持久化数据,例如为了调查,您不需要保存整个容器,只需要保存数据文件,这些文件可以映射到容器之外。 可以在here 找到如何执行此操作的示例之一。

【讨论】:

【参考方案5】:

如果您希望从集成(或类似)测试套件运行 postgres 的进程内版本,postgresql-embedded 对我来说很好。

我写了一个small maven plugin,它可以用作一个maven 包装器,它围绕一个postgresql-embedded 的分叉版本。

【讨论】:

再次提醒,如果您超出默认配置,此解决方案会出现问题 很公平。但是,在 maven 插件中,我专门添加了一个配置选项,用于将二进制文件的下载 URL 设置为默认位置以外的位置。 请查看github.com/aramcodz/embedded-postgres-maven-plugin并查找“downloadUrl”插件配置参数。 你试过了吗?因为底层库有bug。没有插件可以解决这个问题 - 库本身有错误。尝试清除 postgre 二进制文件的本地缓存,在与 Internet 断开连接时从本地 url (file:///...) 下载。我敢打赌它不会启动(至少它对我来说失败了,我将它调试到我清楚地看到一个忽略传递给 initDb 命令的任何设置的错误) @JanZyka,是的,我刚试了一下,发现从本地下载成功了。我清理了二进制文件的本地缓存(移动到另一个本地文件夹(远离 ~/.embedpostgresql),并在 maven 配置中使用了 file:////my_local_folder/dev/tmp/ 的 downloadUrl。这是一个 sn-p输出:'Download Version9.2.4-1:OS_X:B64 START Download Version9.2.4-1:OS_X:B64 DownloadSize: 64139929 Download Version9.2.4-1:OS_X:B64 0% 1%...94% 95% 96% 97% 98% 99% 100% 下载版本9.2.4-1:OS_X:B64 以 62636kb/s 下载 下载版本9.2.4-1:OS_X:B64完成'【参考方案6】:

我在测试中使用 PostgreSQL 的 container 实例。 https://www.testcontainers.org/#about https://www.testcontainers.org/modules/databases/jdbc/

依赖:

        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>5.7.2</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-params</artifactId>
            <version>5.7.2</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>5.7.2</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.testcontainers</groupId>
            <artifactId>testcontainers</artifactId>
            <version>1.15.3</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.testcontainers</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>1.15.3</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.testcontainers</groupId>
            <artifactId>postgresql</artifactId>
            <version>1.15.3</version>
            <scope>test</scope>
        </dependency>

然后做测试:

@SpringBootTest
@ActiveProfiles("test")
@Testcontainers
class ApplicationTest 
    @Container
    static PostgreSQLContainer<?> postgreSQL = new PostgreSQLContainer<>("postgres:12.7");

    @DynamicPropertySource
    static void postgreSQLProperties(DynamicPropertyRegistry registry) 
        registry.add("spring.datasource.username", postgreSQL::getUsername);
        registry.add("spring.datasource.password", postgreSQL::getPassword);
    


    @Test
    void someTests() 

    

在 application-test.yml 中:

source:
  datasource:
    url: jdbc:tc:postgresql:12.7:///databasename

【讨论】:

以上是关于嵌入式 PostgreSQL for Java JUnit 测试的主要内容,如果未能解决你的问题,请参考以下文章

写一个求100以内素数的C语言与汇编的嵌入式编程

java 大量for循环如何优化

java中for循环的优化

初学java---第五课《for循环——乘法口诀》

Java for 循环

java中for循环执行过程