为啥每次单元测试后我的数据库都没有被清除?
Posted
技术标签:
【中文标题】为啥每次单元测试后我的数据库都没有被清除?【英文标题】:Why is my database not being cleared after each unit test?为什么每次单元测试后我的数据库都没有被清除? 【发布时间】:2021-12-28 19:59:59 【问题描述】:我正在尝试在没有创建用户并且可以第一次注册时运行测试。测试第一次完美运行并通过。问题是,如果我第二次运行它,我的容器不会被正确删除,我们最终会得到 HTTPStatus.conflict
而不是 .ok
,所以用户确实已经存在。
我在我的 MacBook 上运行一个 Docker 容器,设置了 docker-compose-testing
、docker-compose
和 testing.Dockerfile
。
运行测试时也会触发此错误:
caught error: "server: syntax error at end of input (scanner_yyerror)"
这里缺少什么? autoRevert()
和 autoMigrate()
不会清除我的数据库吗?
func testSignUpRoute_userDoesNotExistInDatabase_userSignUpAndHTTPStatusIsOk() throws
// Configuration in setUp and tearDown.
var app = Application(.testing)
defer app.shutdown()
try configure(app)
try app.autoRevert().wait()
try app.autoMigrate().wait()
// Given
let expected: HTTPStatus = .ok
let headers = [
"email": "foo@email.com",
"password": "fooEncrypted"
]
// When
try app.test(.POST, "users/signup") request in
try request.content.encode(headers)
afterResponse: response in
let result = response.status
// Then
XCTAssertEqual(result, expected, "Response status must be \(expected)")
这是我在 configure.swift
文件中进行迁移的顺序:
public func configure(_ app: Application) throws
app.migrations.add(UserModelMigration_v1_0_0())
app.migrations.add(AddressModelMigration_v1_0_0())
这就是我恢复所有模型的方式。 AddressModel
和 CompanyModel
是 @OptionalChild
的 UserModel
。看来问题是从这里来的,但我不能指出来。
struct UserModelMigration_v1_0_0: Migration
func prepare(on database: Database) -> EventLoopFuture<Void>
database.schema(UserModel.schema)
.id()
.field(UserModel.Key.email, .string, .required)
.field(UserModel.Key.password, .string, .required)
.unique(on: UserModel.Key.email)
.create()
func revert(on database: Database) -> EventLoopFuture<Void>
database
.schema(UserModel.schema)
.update()
struct AddressModelMigration_v1_0_0: Migration
func prepare(on database: Database) -> EventLoopFuture<Void>
database.schema(AddressModel.schema)
.id()
.field(AddressModel.Key.streetLineOne, .string, .required)
.field(AddressModel.Key.city, .string, .required)
.field(AddressModel.Key.userID, .uuid, .required,
.references(UserModel.schema, .id,
onDelete: .cascade,
onUpdate: .cascade))
.unique(on: AddressModel.Key.userID)
.create()
func revert(on database: Database) -> EventLoopFuture<Void>
database
.schema(AddressModel.schema)
.update()
这是我的docker-compose-testing.yml
文件
version: '3'
services:
testingonlinux:
depends_on:
- postgres
build:
context: .
dockerfile: testing.Dockerfile
environment:
- DATABASE_HOST=postgres
- DATABASE_PORT=5434
postgres:
image: "postgres"
environment:
- POSTGRES_DB=foo_db_testing
- POSTGRES_USER=foo_user
- POSTGRES_PASSWORD=foo_password
这是我的docker-compose.yml
文件
version: '3.8'
volumes:
db_data:
x-shared_environment: &shared_environment
LOG_LEVEL: $LOG_LEVEL:-debug
DATABASE_HOST: db
DATABASE_NAME: vapor_database
DATABASE_USERNAME: vapor_username
DATABASE_PASSWORD: vapor_password
services:
app:
image: FooServerSide:latest
build:
context: .
environment:
<<: *shared_environment
depends_on:
- db
ports:
- '8080:8080'
# user: '0' # uncomment to run as root for testing purposes even though Dockerfile defines 'vapor' user.
command: ["serve", "--env", "production", "--hostname", "0.0.0.0", "--port", "8080"]
migrate:
image: FooServerSide:latest
build:
context: .
environment:
<<: *shared_environment
depends_on:
- db
command: ["migrate", "--yes"]
deploy:
replicas: 0
revert:
image: FooServerSide:latest
build:
context: .
environment:
<<: *shared_environment
depends_on:
- db
command: ["migrate", "--revert", "--yes"]
deploy:
replicas: 0
db:
image: postgres:12-alpine
volumes:
- db_data:/var/lib/postgresql/data/pgdata
environment:
PGDATA: /var/lib/postgresql/data/pgdata
POSTGRES_USER: vapor_username
POSTGRES_PASSWORD: vapor_password
POSTGRES_DB: vapor_database
ports:
- '5432:5432'
还有我的 testing.Dockerfile
FROM swift:5.5
WORKDIR /package
COPY . ./
CMD ["swift", "test", "--enable-test-discovery"]
【问题讨论】:
这是一个码头工人还是一个快速的问题?另外,您似乎还问了多个问题。 关于没有被删除的容器,执行后可以随时清理容器-serverfault.com/questions/750175/… 【参考方案1】:对我来说突出的问题是您的还原方法似乎实际上并未删除数据库中的数据或删除表。
我有类似的测试使用 Vapor 中的恢复功能,迁移如下所示:
public struct ListV1: Migration
public func prepare(on database: Database) -> EventLoopFuture<Void>
return database.schema("Lists")
.id()
.field("listId", .int, .required, .custom("UNIQUE"))
.field("listString", .string, .required)
.field("createdAt", .datetime)
.field("updatedAt", .datetime)
.create()
public func revert(on database: Database) -> EventLoopFuture<Void>
return database.schema("Lists").delete()
将您的还原功能更改为使用.delete()
(如下所示)可能会解决您的问题:
struct AddressModelMigration_v1_0_0: Migration
...
func revert(on database: Database) -> EventLoopFuture<Void>
database
.schema(AddressModel.schema)
.delete()
【讨论】:
【参考方案2】:您为什么不将您的 App 类放在代表 sut(被测系统)的测试 var 中并覆盖您的测试的 setup()
和 tearDown()
方法?
如:
final class AppTests: XCTestCase
var sut: Application!
override setup()
super.setup()
sut = Application(.testing)
override tearDown()
sut.shutdown()
sut = nil
super.tearDown()
// MARK: - Given
// MARK: - When
func whenMigrationsAreSet() async throws
sut.migrations.add(UserModelMigration_v1_0_0())
sut.migrations.add(AddressModelMigration_v1_0_0())
sut.migrations.add(CompanyModelMigration_v1_0_0())
try await app.autoRevert()
try await app.autoMigrate()
// MARK: - Tests
func testSignUpRoute_whenUserDoesNotExist_thenUserSignUpAndHTTPStatusIsOk() async throws
try await whenMigrationsAreSet()
let headers = [
"email": "foo@email.com",
"password": "fooEncrypted",
]
let requestExpectation = expectation("request completed")
let responseExpectation = expectation("got response")
var result: HTTPStatus?
try sut.test(.POST, "users/signup") request in
try request.content.encode(headers)
requestExpectation.fullfill()
afterResponse response in
result = response.status
responseExpectation.fullfill()
wait(for: [requestExpectation, responseExpectation], timeout: 0.5, enforceOrder: true)
XCTAssertEqual(result, .ok)
无论如何,我也对您的测试增加了期望,因为它似乎正在以旧方式完成一些异步工作。
【讨论】:
应用程序已经在setUp和tearDown中。我将它们放在测试方法中以简化 *** 中的代码,但它的工作原理相同。您提供的代码没有在您这边进行测试,否则,您会注意到它由于多种原因无法正常工作。请确保在发布您的答案之前先通过测试。 所以说清楚,你有什么要求?我们必须为您提供一个在不知道它测试什么的情况下通过的测试,或者您的问题是关于“我如何编写一个在完成后重置 SUT 状态的测试”?您提供的代码似乎有一个超出测试范围的断言,因为这样的断言被放入一个您可能没有等待的闭包中。 如前所述,我想在每次单元测试后清除 Docker 容器中的所有数据。 然后你想运行代码来清除tearDown()
中的Docker容器中的数据库条目。或者您可以模拟此类服务,使其使用内存存储而不是持久存储,并仅在您的测试中使用该模拟。例如要测试CoreData
,一种常见的方法是在测试中创建它的堆栈作为内存。无论如何,如果没有您的Application
类的更多详细信息,基本上不可能理解哪种方法是可行的,以及如何最终将数据库恢复为空状态。以上是关于为啥每次单元测试后我的数据库都没有被清除?的主要内容,如果未能解决你的问题,请参考以下文章