Documentation Index
Fetch the complete documentation index at: https://docs.prism.byescaleira.com/llms.txt
Use this file to discover all available pages before exploring further.
Assertions
PrismAssertResponse wraps an HTTP response with chainable assertion methods. Instead of manually checking status codes, headers, and body content, chain assertions for readable, expressive tests.
Quick Start
let response = try await client.get("/users")
PrismAssertResponse(response)
.assertStatus(.ok)
.assertHeader("Content-Type", "application/json; charset=utf-8")
.assertBodyContains("Alice")
Available Assertions
Status Code
PrismAssertResponse(response)
.assertStatus(.ok) // 200
.assertStatus(.created) // 201
.assertStatus(.noContent) // 204
.assertStatus(.notFound) // 404
.assertStatus(.badRequest) // 400
PrismAssertResponse(response)
.assertHeader("Content-Type", "application/json; charset=utf-8")
.assertHeader("X-Cache", "HIT")
.assertHeader("X-Request-ID", requestId)
Body Content
PrismAssertResponse(response)
.assertBodyContains("success")
.assertBodyContains("user_id")
JSON Decoding
struct UserResponse: Decodable {
let id: Int
let name: String
}
let user = try PrismAssertResponse(response)
.assertStatus(.ok)
.assertJSON(UserResponse.self)
#expect(user.name == "Alice")
#expect(user.id == 1)
Chaining
Every assertion returns self, so you can chain multiple checks:
let user = try PrismAssertResponse(response)
.assertStatus(.created)
.assertHeader("Content-Type", "application/json; charset=utf-8")
.assertHeader("X-Request-ID", "test-123")
.assertBodyContains("Alice")
.assertJSON(UserResponse.self)
Reading the Body
let assert = PrismAssertResponse(response)
let body = assert.bodyString // String? (UTF-8 decoded)
// The wrapped response is still accessible
let status = assert.response.status
let headers = assert.response.headers
Complete Test Example
@Suite("Todo API")
struct TodoAPITests {
let client: PrismTestClient
init() {
client = PrismTestClientBuilder()
.get("/todos") { _ in
.json(["todos": [["id": 1, "title": "Write tests", "done": false]]])
}
.post("/todos") { request in
let todo = try request.decodeJSON() as CreateTodo
return .json(["id": 2, "title": todo.title], status: .created)
}
.get("/todos/:id") { request in
let id = request.parameters["id"]!
if id == "999" {
return .json(["error": "Not found"], status: .notFound)
}
return .json(["id": id, "title": "Test todo"])
}
.build()
}
@Test func listTodos() async throws {
let response = try await client.get("/todos")
PrismAssertResponse(response)
.assertStatus(.ok)
.assertBodyContains("Write tests")
}
@Test func createTodo() async throws {
let response = try await client.postJSON("/todos", body: CreateTodo(title: "New"))
PrismAssertResponse(response)
.assertStatus(.created)
.assertBodyContains("New")
}
@Test func todoNotFound() async throws {
let response = try await client.get("/todos/999")
PrismAssertResponse(response)
.assertStatus(.notFound)
.assertBodyContains("Not found")
}
@Test func decodeResponse() async throws {
let response = try await client.get("/todos/1")
let todo = try PrismAssertResponse(response)
.assertStatus(.ok)
.assertJSON(TodoResponse.self)
#expect(todo.title == "Test todo")
}
}
Error Types
PrismTestError.emptyBody // assertJSON called on empty response
PrismTestError.serverNotStarted // Server not available
Use assertJSON at the end of your chain — it returns the decoded value, not PrismAssertResponse, so you can’t chain more assertions after it. Put status and header checks first.
Assertions use Swift’s assert(), which is removed in release builds. For tests (which always run in debug mode), this is fine. If you need assertions in release builds, use precondition() or Swift Testing’s #expect.