我想为Vapor 3服务器编写一些集成测试,并且每次运行测试时都需要具有干净的Postgre数据库。我该如何实现?似乎迁移不是正确的方法,因为如果数据库还不存在,迁移就已经运行了一次。
答案 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)
参加聚会很晚,但是revert
和migrate
命令也可以这样做。此代码执行与@ 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
}
}
参加聚会很晚,但是以下代码确实执行了还原和迁移
命令:(我正在使用Quick
和Nimble
,所以beforeSuite
。这里有注释的代码,因为除非您在configure.swift
以上使用,否则您可以取消注释代码并使用{{ 1}}。)
CommandConfig
答案 3 :(得分:0)
对于那些正在寻求另一种不涉及注册新迁移方法的人(对我来说,这增加了更多的代码复杂性),您可以使用 Pre-Action 脚本作为测试目标(⌘+ <)
通过使用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 build
和vapor 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数据库,准备在测试开始之前建立连接。