-
-
Notifications
You must be signed in to change notification settings - Fork 337
Update testing documentation to include withApp(configure:) method #1086
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
19f45cd
Update testing documentation to include withApp(configure:) method
maartene 8ae4148
Update docs/advanced/testing.md
maartene b418bf2
Update docs/advanced/testing.md
maartene 7d4e6e7
Merge branch 'main' into main
maartene dbb5267
Improved based on feedback
maartene bfc034b
Merge branch 'main' of https://github.com/maartene/documentation
maartene 606ed26
Re-added hint for SQLite and make sure we tell people to check the da…
maartene 4eb32e6
Structure now describes both choosing right database and the migrate/…
maartene f324dd2
fix typo in configure
maartene 759a712
Merge branch 'main' into main
fpseverino File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -56,35 +56,26 @@ To ensure your tests run in a serialized manner (e.g., when testing with a datab | |
|
||
### Testable Application | ||
|
||
Define a private method function `withApp` to streamline and standardize the setup and teardown for our tests. This method encapsulates the lifecycle management of the `Application` instance, ensuring that the application is properly initialized, configured, and shut down for each test. | ||
To provide a streamlined and standardized setup and teardown of tests, `VaporTesting` offers the `withApp` helper function. This method encapsulates the lifecycle management of the `Application` instance, ensuring that the application is properly initialized, configured, and shut down for each test. | ||
|
||
In particular it is important to release the threads the application requests at startup. If you do not call `asyncShutdown()` on the app after each unit test, you may find your test suite crash with a precondition failure when allocating threads for a new instance of `Application`. | ||
Pass your application's `configure(_:)` method to the `withApp` helper function to make sure all your routes get correctly registered: | ||
|
||
```swift | ||
private func withApp(_ test: (Application) async throws -> ()) async throws { | ||
let app = try await Application.make(.testing) | ||
do { | ||
try await configure(app) | ||
try await test(app) | ||
} | ||
catch { | ||
try await app.asyncShutdown() | ||
throw error | ||
@Test func someTest() async throws { | ||
try await withApp(configure: configure) { app in | ||
// your actual test | ||
} | ||
try await app.asyncShutdown() | ||
} | ||
``` | ||
|
||
Pass the `Application` to your package's `configure(_:)` method to apply your configuration. Then you test the application calling the `test()` method. Any test-only configurations can also be applied. | ||
|
||
#### Send Request | ||
|
||
To send a test request to your application, use the `withApp` private method and inside use the `app.testing().test()` method: | ||
|
||
```swift | ||
@Test("Test Hello World Route") | ||
func helloWorld() async throws { | ||
try await withApp { app in | ||
try await withApp(configure: configure) { app in | ||
try await app.testing().test(.GET, "hello") { res async in | ||
#expect(res.status == .ok) | ||
#expect(res.body.string == "Hello, world!") | ||
|
@@ -131,22 +122,28 @@ app.testing(method: .running(port: 8123)).test(...) | |
|
||
#### Database Integration Tests | ||
|
||
Configure the database specifically for testing to ensure that your live database is never used during tests. | ||
Configure the database specifically for testing to ensure that your live database is never used during tests. For example, when you are using SQLite, you could configure your database in the `configure(_:)` function as follows: | ||
|
||
```swift | ||
app.databases.use(.sqlite(.memory), as: .sqlite) | ||
``` | ||
public func configure(_ app: Application) async throws { | ||
// All other configurations... | ||
|
||
Then you can enhance your tests by using `autoMigrate()` and `autoRevert()` to manage the database schema and data lifecycle during testing: | ||
if app.environment == .testing { | ||
app.databases.use(.sqlite(.memory), as: .sqlite) | ||
} else { | ||
app.databases.use(.sqlite(.file("db.sqlite")), as: .sqlite) | ||
} | ||
} | ||
``` | ||
|
||
By combining these methods, you can ensure that each test starts with a fresh and consistent database state, making your tests more reliable and reducing the likelihood of false positives or negatives caused by lingering data. | ||
!!! warning | ||
Make sure you run your tests against the correct database, so you prevent accidentally overwriting data you do not want to lose. | ||
|
||
Here's how the `withApp` function looks with the updated configuration: | ||
Then you can enhance your tests by using `autoMigrate()` and `autoRevert()` to manage the database schema and data lifecycle during testing. To do so, you should create your own helper function `withAppIncludingDB` that includes the database schema and data lifecycles: | ||
|
||
```swift | ||
private func withApp(_ test: (Application) async throws -> ()) async throws { | ||
private func withAppIncludingDB(_ test: (Application) async throws -> ()) async throws { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
let app = try await Application.make(.testing) | ||
app.databases.use(.sqlite(.memory), as: .sqlite) | ||
do { | ||
try await configure(app) | ||
try await app.autoMigrate() | ||
|
@@ -162,6 +159,21 @@ private func withApp(_ test: (Application) async throws -> ()) async throws { | |
} | ||
``` | ||
|
||
And then use this helper in your tests: | ||
```swift | ||
@Test func myDatabaseIntegrationTest() async throws { | ||
try await withAppIncludingDB { app in | ||
try await app.testing().test(.GET, "hello") { res async in | ||
#expect(res.status == .ok) | ||
#expect(res.body.string == "Hello, world!") | ||
} | ||
} | ||
} | ||
``` | ||
|
||
By combining these methods, you can ensure that each test starts with a fresh and consistent database state, making your tests more reliable and reducing the likelihood of false positives or negatives caused by lingering data. | ||
|
||
|
||
## XCTVapor | ||
|
||
Vapor includes a module named `XCTVapor` that provides test helpers built on `XCTest`. These testing helpers allow you to send test requests to your Vapor application programmatically or running over an HTTP server. | ||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.