簡體   English   中英

刪除 Vapor 3 中的測試數據庫

[英]Deleting test database in Vapor 3

我想為 Vapor 3 服務器編寫一些集成測試,並且每次運行測試時我都需要擁有干凈的 Postgre 數據庫。 我怎樣才能做到這一點? 如果數據庫尚不存在,遷移似乎不是正確的方法,因為它們已經運行過一次。

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

這需要在運行測試之前運行一個數據庫,但它會在每次測試運行開始時恢復所有遷移,從而每次都為您提供一個干凈的數據庫。 (具體在這里

根目錄中還有一個docker-compose.yml用於在 Linux 上啟動一個完全隔離的測試環境

我找到了一個資源密集度較低的解決方案,然后每次都恢復所有遷移。

RSpec 有一個配置( use_transactional_fixtures ),允許將每個測試包裝在 SQL 事務中。 測試結束后,它將回滾事務,從而恢復測試期間發生的所有更改。 相關文檔在這里

我們可以在 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 中是如何發生的。

參加聚會很晚,但按照以下方式進行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否則您可以取消注釋代碼並直接使用CommandConfig 。 )

import Quick
import Vapor
import Console
import FluentPostgreSQL
...
        configuration.beforeSuite {
            let console: Console = Terminal()
//            var commandConfig = CommandConfig()
//            commandConfig.use(RevertCommand(), as: "revert")
//            commandConfig.use(MigrateCommand(), as: "migrate")
            var config = Config.default()
            var env = Environment.testing
            var services = Services.default()

            do {
//                try App.configure(&config, &env, &services)
                let container = try Application(config: config, environment: env, services: services)
                let commandConfig = try container.make(CommandConfig.self)
                let commands = try commandConfig.resolve(for: container).group()
                var input = CommandInput(arguments: ["vapor","revert","--all", "-y"])
                try console.run(commands, input: &input, on: container).wait()
                input = CommandInput(arguments: ["vapor","migrate","-y"])
                try console.run(commands, input: &input, on: container).wait()
            } catch let error {
                console.error(error.localizedDescription)
                exit(1)
            }
        }

對於那些正在尋求另一種不涉及注冊新遷移的方法(對我來說,增加更多代碼復雜性)的人,您可以使用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 run從另一個項目中觸發vapor buildvapor run ,這將為我的持續集成測試創建一個全新的 Web 服務環境,只需插入正確的環境變量

我發現的最簡單的方法是將PostgresKit添加到測試目標並使用它來設置連接以調用我的“清理”查詢。

@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

// swift-tools-version:5.2
import PackageDescription

let package = Package(
    name: "MyVaporProject",
    platforms: [
       .macOS(.v10_15)
    ],
    dependencies: [
        // 💧 A server-side Swift web framework.
        .package(url: "https://github.com/vapor/vapor.git", from: "4.0.0"),
        .package(url: "https://github.com/vapor/fluent.git", from: "4.0.0"),
        .package(url: "https://github.com/vapor/fluent-postgres-driver.git", from: "2.0.0"),
        .package(url: "https://github.com/vapor/postgres-kit.git", from: "2.0.0")
    ],
    targets: [
        .target(
            name: "App",
            dependencies: [
                .product(name: "Fluent", package: "fluent"),
                .product(name: "FluentPostgresDriver", package: "fluent-postgres-driver"),
                .product(name: "Vapor", package: "vapor")
            ],
            swiftSettings: [
                // Enable better optimizations when building in Release configuration. Despite the use of
                // the `.unsafeFlags` construct required by SwiftPM, this flag is recommended for Release
                // builds. See <https://github.com/swift-server/guides#building-for-production> for details.
                .unsafeFlags(["-cross-module-optimization"], .when(configuration: .release))
            ]
        ),
        .target(name: "Run", dependencies: [.target(name: "App")]),
        .testTarget(
            name: "AppTests",
            dependencies: [
                .target(name: "App"),
                .product(name: "XCTVapor", package: "vapor"),
                .product(name: "PostgresKit", package: "postgres-kit")
            ]
        )
    ]
)

與之前的答案一樣,這需要一個獨立的 Postgres 數據庫,已經遷移,准備在測試開始之前進行連接。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM