What it is
Quick is a behaviour-driven testing framework for Swift and Objective-C; Nimble is its matcher library, providing expressive assertions and async expectations.
Specs nest describe, context and it blocks with beforeEach setup. Nimble matchers read as sentences and handle asynchronous expectations.
Installation
.package(url: "https://github.com/Quick/Quick.git", from: "7.6.0")Getting started
The smallest useful thing you can do with it, and what each part means.
swift
import Quick
import Nimble
final class BookServiceSpec: QuickSpec {
override class func spec() {
var service: BookService!
var repository: MockRepository!
beforeEach {
repository = MockRepository()
service = BookService(repository: repository)
}
describe("fetching a book") {
context("when it exists") {
beforeEach { repository.stubbed = Book(id: 1, title: "Dune") }
it("returns the title") {
expect(service.title(for: 1)).to(equal("Dune"))
}
}
context("when it is missing") {
beforeEach { repository.stubbed = nil }
it("throws notFound") {
expect { try service.require(1) }
.to(throwError(BookError.notFound))
}
}
}
}
}Advanced usage
Where the library earns its place over a simpler alternative.
swift
// Polls until the condition holds or the timeout expires.
await expect(viewModel.books).toEventually(haveCount(3), timeout: .seconds(2))
await expect(viewModel.isLoading).toEventually(beFalse())
// Async/await support.
await expect { try await service.fetch() }.to(haveCount(3))
// A reusable custom matcher.
func bePublished(inYear year: Int) -> Matcher<Book> {
Matcher { actual in
guard let book = try actual.evaluate() else {
return MatcherResult(status: .fail, message: .expectedTo("be non-nil"))
}
return MatcherResult(
bool: book.year == year,
message: .expectedTo("be published in \(year), got \(book.year)")
)
}
}Errors and fixes
The failures you are most likely to hit, and what actually resolves them.
- Tests pass alone but fail together
- State declared outside beforeEach persists between examples. Reset everything in beforeEach.
- toEventually never succeeds
- The value updates on a queue the test does not pump, or the timeout is too short. Check which thread the update happens on.
Best practices
- Use beforeEach for setup so each example starts clean; shared mutable state across examples causes order-dependent failures.
- Prefer toEventually over fixed sleeps for async assertions.
- Consider Nimble alone with XCTest if the team does not want the BDD structure.
- Evaluate Swift Testing for new projects — it now covers much of this natively.
Background
Why it exists, and what it was reacting to.
Modelled on RSpec, Quick brought nested describe and context blocks to Swift. Nimble is usable on its own with XCTest, and many teams adopt only the matchers.
