在Vapor 3中删除测试数据库

时间:2018-11-19 20:12:25

标签: swift vapor

我想为Vapor 3服务器编写一些集成测试,并且每次运行测试时都需要具有干净的Postgre数据库。我该如何实现?似乎迁移不是正确的方法,因为如果数据库还不存在,迁移就已经运行了一次。

5 个答案:

答案 0 :(得分:5)

看看https://github.com/raywenderlich/vapor-til/tree/master/Tests

这需要在运行测试之前运行数据库,但它会在每次测试运行开始时还原所有迁移,从而为您每次提供一个干净的数据库。 (具体是here

根目录中还有一个docker-compose.yml,用于在Linux上建立一个完全隔离的测试环境

答案 1 :(得分:1)

我找到了一种资源占用较少的解决方案,然后每次都还原所有迁移。

RSpec的配置(use_transactional_fixtures)允许将每个测试包装在SQL事务中。测试结束后,它将回滚事务,结果是还原测试期间发生的所有更改。相关documentation is here

我们可以在Vapor中实现类似的解决方案。我的示例测试如下所示。

final class VaporTests: XCTestCase {

  var app: Application!

  override func setUp() {
    super.setUp()

    app = try! Application.buildForTesting()
    let conn = try! app.requestPooledConnection(to: .psql).wait()
    try! conn.simpleQuery("BEGIN TRANSACTION").wait()
    try! app.releasePooledConnection(conn, to: .psql)
  }

  override func tearDown() {
    let conn = try! app.requestPooledConnection(to: .psql).wait()
    try! conn.simpleQuery("ROLLBACK").wait()
    try! app.releasePooledConnection(conn, to: .psql)

    super.tearDown()
  }

  func testExample() throws {
    let request = HTTPRequest(method: .GET, url: "my/endpoint/example")
    let wrapper = Request(http: request, using: app)

    let response = try ExampleController().example(wrapper).wait()

    XCTAssertEqual(response, .ok)
  }
}

为确保我不会遇到并发问题,我将测试应用程序中的数据库池限制为1个连接。

func configure(_ config: inout Config, _ env: inout Environment, _ services: inout Services) throws {
  // ... other configurations

  let poolConfig = DatabaseConnectionPoolConfig(maxConnections: 1)
  services.register(poolConfig)
}

非常感谢Jakub Jatczak帮助我了解在Rails中是如何发生的。

答案 2 :(得分:1)

参加聚会很晚,但是revertmigrate命令也可以这样做。此代码执行与@ 0xTim给出的答案相似的命令。但是我已经使用了Console.framework

通常,我们使用如下所示的configure.swift文件:

import FluentPostgreSQL
import Vapor

public func configure(_ config: inout Config, _ env: inout Environment, _ services: inout Services) throws {
    // Register providers first
    try services.register(FluentPostgreSQLProvider())

    ...


    /// Configure commands
    var commandConfig = CommandConfig.default()
    commandConfig.useFluentCommands()
    services.register(commandConfig)

    ...

    /// Configure migrations
    services.register { container -> MigrationConfig in
        var migrationConfig = MigrationConfig()
        try migrate(migrations: &migrationConfig)
        return migrationConfig
    }
}

参加聚会很晚,但是以下代码确实执行了还原和迁移 命令:(我正在使用QuickNimble,所以beforeSuite。这里有注释的代码,因为除非您在configure.swift以上使用,否则您可以取消注释代码并使用{{ 1}}。)

CommandConfig

答案 3 :(得分:0)

对于那些正在寻求另一种不涉及注册新迁移方法的人(对我来说,这增加了更多的代码复杂性),您可以使用 Pre-Action 脚本作为测试目标(⌘+ <)

Pre-Action Script

通过使用bash脚本,您可以创建一个全新的postgresql数据库,该数据库将用于构建仅用于测试的项目:

# Variables 
export IS_TEST=true
export DB_USERNAME="`whoami`"
export DB_DBNAME="BARTENDER_TEST_DB"


#Creating dedicated Postgres DB 
echo "deleting & recreating $DB_DBNAME for user $DB_USERNAME"
psql postgres<< EOF
  DROP DATABASE "$DB_DBNAME";
  CREATE DATABASE "$DB_DBNAME";
  \list
EOF

然后在configure.swift文件中创建与新创建的数据库匹配的PostgreSQLDatabaseConfig

    if let _ = Environment.get("IS_TEST") { // IS_TEST is defined only in Pre-Action script 

        guard let username = Environment.get("DB_USERNAME") else {
            fatalError("Failed to create PostgresConfig - DB_USERNAME in Environment variables")
        }
        guard let databasename = Environment.get("DB_DBNAME") else {
            fatalError("Failed to create PostgresConfig - DB_DBNAME in Environment variables")
        }

        postgresqlConfig = PostgreSQLDatabaseConfig(
            hostname: "127.0.0.1",
            port: 5432,
            username: username,
            database: databasename,
            password: nil
        )
    } 
    else { /* your other config here */ }

    let database = PostgreSQLDatabase(config: postgresqlConfig)
    ... 

我从中发现的最大优点是,我什至可以通过另一个项目触发vapor buildvapor run,而这将为我的持续集成测试创建一个全新的Webservice环境,只需插入正确的环境变量

答案 4 :(得分:0)

我发现的最简单的方法是将PostgresKit添加到测试目标,并使用它来建立连接以调用我的“清理”查询。

java_binary(
  name = ...,
  srcs = ...,
  deps = ...,
  plugins = [":my_plugin"],
)

java_plugin(
  name = "my_plugin",
  srcs = ...,
  deps = ...,
  processor_class = "...",
)

这是我的@testable import App import Vapor import XCTest import PostgresKit final class UserTests: XCTestCase { var pools: EventLoopGroupConnectionPool<PostgresConnectionSource>! var postgresDb: PostgresDatabase! var eventLoopGroup: EventLoopGroup! override func setUp() { let configuration = PostgresConfiguration( hostname: "localhost", username: "postgres", password: "password", database: "db_name" ) eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 2) pools = EventLoopGroupConnectionPool( source: PostgresConnectionSource(configuration: configuration), on: eventLoopGroup ) postgresDb = pools.database(logger: Logger.init(label: "TestLogger")) } override func tearDown() { let _ = try! postgresDb.query("DELETE FROM \(User.schema)").wait() try! pools.syncShutdownGracefully() try! eventLoopGroup.syncShutdownGracefully() } func testUploadUser() throws { let app = Application(.testing) defer { app.shutdown() } try configure(app) try app.testable(method: .running).test(.POST, "api/users", beforeRequest: { req in try req.content.encode(["firstName" : "Dwide", "lastName" : "Shrewd"]) }, afterResponse: { res in XCTAssertEqual(res.status, .ok) let user = try res.content.decode(User.self) XCTAssertEqual(user, User(id: user.id, firstName: "Dwide", lastName: "Shrewd")) }) } }

Package.swift

与之前的答案一样,这需要一个已经迁移的独立的Postgres数据库,准备在测试开始之前建立连接。