文档

§使用数据库进行测试

虽然可以编写 功能测试 来测试数据库访问代码,方法是启动包含数据库的完整应用程序,但启动完整应用程序通常不可取,因为启动和运行更多组件以测试应用程序的一小部分会很复杂。

Play 提供了许多实用程序来帮助测试数据库访问代码,这些代码允许使用数据库进行测试,但与应用程序的其余部分隔离。这些实用程序可以轻松地与 ScalaTestspecs2 一起使用,并且可以使您的数据库测试更接近轻量级且快速运行的单元测试,而不是重量级且缓慢的功能测试。

§使用数据库

要使用数据库后端进行测试,您只需要

libraryDependencies += javaJdbc % Test

要连接到数据库,至少需要数据库驱动程序名称和数据库的 URL,使用 Database 静态工厂方法。例如,要连接到 MySQL,您可以使用以下方法

Database database =
    Databases.createFrom("com.mysql.jdbc.Driver", "jdbc:mysql://127.0.0.1/test");

这将为运行在 localhost 上的 MySQL test 数据库创建一个数据库连接池,名称为 default。数据库的名称仅在 Play 内部使用,例如,由其他功能(如演变)使用,以加载与该数据库关联的资源。

您可能希望为数据库指定其他配置,包括自定义名称或配置属性(如用户名、密码和 Play 支持的各种连接池配置项),方法是提供自定义名称参数和/或自定义配置参数

Database database =
    Databases.createFrom(
        "mydatabase",
        "com.mysql.jdbc.Driver",
        "jdbc:mysql://127.0.0.1/test",
        ImmutableMap.of(
            "username", "test",
            "password", "secret"));

使用数据库后,由于数据库通常由连接池支持,连接池持有打开的连接并可能运行线程,因此需要关闭数据库。这可以通过调用 `shutdown` 方法来完成。

database.shutdown();

这些方法在与 JUnit 的 `@Before` 和 `@After` 注解结合使用时特别有用,例如

Database database;

@Before
public void createDatabase() {
  database = Databases.createFrom("com.mysql.jdbc.Driver", "jdbc:mysql://127.0.0.1/test");
}

@After
public void shutdownDatabase() {
  database.shutdown();
}

提示:您可以使用此方法将测试数据库配置外部化,使用环境变量或系统属性来配置要使用的数据库以及如何连接到它。这为开发人员提供了最大的灵活性,让他们可以根据自己的意愿设置自己的环境,以及为提供特定环境的 CI 系统提供与开发环境不同的环境。

§使用内存数据库

有些人更喜欢不需要安装数据库等基础设施来运行测试。Play 提供简单的助手来为此目的创建 H2 内存数据库。

Database database = Databases.inMemory();

内存数据库可以通过提供自定义名称、自定义 URL 参数和自定义连接池配置来配置。以下示例显示了提供 `MODE` 参数以告诉 H2 模拟 `MySQL`,以及配置连接池以记录所有语句。

Database database =
    Databases.inMemory(
        "mydatabase", ImmutableMap.of("MODE", "MYSQL"), ImmutableMap.of("logStatements", true));

与通用数据库工厂一样,请确保始终关闭内存数据库连接池。

database.shutdown();

§应用演变

在运行测试时,您通常希望为数据库管理数据库模式。如果您已经在使用演变,那么在测试中重用与开发和生产中使用的相同演变通常很有意义。您可能还想创建仅用于测试的自定义演变。Play 提供了一些方便的助手来应用和管理演变,而无需运行整个 Play 应用程序。

要应用演变,您可以使用 Evolutions 静态类中的 `applyEvolutions`。

Evolutions.applyEvolutions(database);

这将从类路径中的 `evolutions/<databasename>` 目录加载演变,并应用它们。

测试运行后,您可能希望将数据库重置为其原始状态。如果您已以使它们将删除所有数据库表的方式实现了演变的向下脚本,则只需调用 `cleanupEvolutions` 方法即可完成此操作。

Evolutions.cleanupEvolutions(database);

§自定义演变

在某些情况下,您可能希望在测试中运行一些自定义演变。自定义演变可以使用自定义 EvolutionsReader 来使用。最简单的方法是使用 `Evolutions` 上的静态工厂方法,例如 `forDefault` 为默认数据库的简单 Evolution 脚本列表创建演变读取器。例如

Evolutions.applyEvolutions(
    database,
    Evolutions.forDefault(
        new Evolution(
            1,
            "create table test (id bigint not null, name varchar(255));",
            "drop table test;")));

清理自定义演变的方式与清理常规演变相同,使用 `cleanupEvolutions` 方法。

Evolutions.cleanupEvolutions(database);

请注意,您不需要在此处传递自定义演变读取器,因为演变的状态存储在数据库中,包括用于拆卸数据库的向下脚本。

有时将自定义演进脚本放在代码中会不切实际。如果是这种情况,您可以使用 `fromClassLoader` 工厂方法将它们放在测试资源目录中。

Evolutions.applyEvolutions(
    database, Evolutions.fromClassLoader(getClass().getClassLoader(), "testdatabase/"));

这将加载演进,其结构和格式与开发和生产中相同,来自 `testdatabase/evolutions/<databasename>/<n>.sql`。

如果您将演进脚本存储在项目文件夹之外,可以使用 EnvironmentEvolutionsReader 从文件系统上的绝对路径或从项目文件夹的相对路径加载脚本。

import play.Environment;
import play.api.db.evolutions.EnvironmentEvolutionsReader;

// Absolute path
Evolutions.applyEvolutions(
    database,
    new EnvironmentEvolutionsReader(Environment.simple().asScala(), "/opt/db_migration"));

// Relative path (based on your project's root folder)
Evolutions.applyEvolutions(
    database,
    new EnvironmentEvolutionsReader(Environment.simple().asScala(), "../db_migration"));

§与 JUnit 集成

通常,您将拥有许多希望使用相同演进运行的测试,因此将演进设置代码提取到 before 和 after 方法中,以及数据库设置和拆卸代码是有意义的。以下是一个完整的测试示例

import static org.junit.Assert.*;

import java.sql.Connection;
import org.junit.*;
import play.db.Database;
import play.db.Databases;
import play.db.evolutions.*;

public class DatabaseTest {

  Database database;

  @Before
  public void setupDatabase() {
    database = Databases.inMemory();
    Evolutions.applyEvolutions(
        database,
        Evolutions.forDefault(
            new Evolution(
                1,
                "create table test (id bigint not null, name varchar(255));",
                "drop table test;")));
  }

  @After
  public void shutdownDatabase() {
    Evolutions.cleanupEvolutions(database);
    database.shutdown();
  }

  @Test
  public void testDatabase() throws Exception {
    Connection connection = database.getConnection();
    connection.prepareStatement("insert into test values (10, 'testing')").execute();

    assertTrue(
        connection.prepareStatement("select * from test where id = 10").executeQuery().next());
  }
}

下一步:测试 Web 服务客户端


在此文档中发现错误?此页面的源代码可以在 此处 找到。阅读完 文档指南 后,请随时贡献拉取请求。有疑问或建议要分享?转到 我们的社区论坛 与社区进行对话。