In 2021, Apple launched Swift concurrency to an adoring viewers; lastly, builders might write Swift code to implement concurrency in Swift apps! At WWDC 2024, builders acquired one other recreation changer: Swift Testing. It’s so a lot enjoyable to make use of, you’ll be leaping away from bed each morning, keen to put in writing extra unit exams for all of your apps! No extra gritting your enamel over XCTAssert-this-and-that. You get to put in writing in Swift, utilizing Swift concurrency, no much less. Swift Testing is a factor of magnificence, and Apple’s testing crew is rightfully pleased with its achievement. You’ll have the ability to write exams quicker and with higher management, your exams will run on Linux and Home windows, and Swift Testing is open supply, so you might help to make it even higher.
Swift Testing vs. XCTest
Right here’s a fast checklist of variations:
- You mark a perform with
@Check
as an alternative of beginning its identify withcheck
. - Check features may be occasion strategies, static strategies, or international features.
- Swift Testing has a number of traits you should use so as to add descriptive details about a check, customise when or whether or not a check runs, or modify how a check behaves.
- Checks run in parallel utilizing Swift concurrency, together with on units.
- You employ
#anticipate(...)
orattempt #require(...)
as an alternative ofXCTAssertTrue
,...False
,...Nil
,...NotNil
,...Equal
,...NotEqual
,...An identical
,...NotIdentical
,...GreaterThan
,...LessThanOrEqual
,...GreaterThanOrEqual
or...LessThan
.
Maintain studying to see extra particulars.
Getting Began
Word: You want Xcode 16 beta to make use of Swift Testing.
Click on the Obtain Supplies button on the prime or backside of this text to obtain the starter initiatives. There are two initiatives so that you can work with:
Migrating to Swift Testing
To begin, open the BullsEye app in Xcode 16 beta and find BullsEyeTests within the Check navigator.
These exams test that BullsEyeGame
computes the rating appropriately when the consumer’s guess is larger or decrease than the goal.
First, remark out the final check testScoreIsComputedPerformance()
. Swift Testing doesn’t (but) assist UI efficiency testing APIs like XCTMetric
or automation APIs like XCUIApplication
.
Return to the highest and exchange import XCTest
with:
import Testing
Then, exchange class BullsEyeTests: XCTestCase {
with:
struct BullsEyeTests {
In Swift Testing, you should use a struct, actor, or class. As traditional in Swift, struct
is inspired as a result of it makes use of worth semantics and avoids bugs from unintentional state sharing. For those who should carry out logic after every check, you’ll be able to embody a de-initializer. However this requires the kind to be an actor or class — it’s the commonest purpose to make use of a reference kind as an alternative of a struct.
Subsequent, exchange setUpWithError()
with an init
technique:
init() {
sut = BullsEyeGame()
}
This allows you to take away the implicit unwrapping from the sut
declaration above:
var sut: BullsEyeGame
Remark out tearDownWithError()
.
Subsequent, exchange func testScoreIsComputedWhenGuessIsHigherThanTarget() {
with:
@Check func scoreIsComputedWhenGuessIsHigherThanTarget() {
and exchange the XCTAssertEqual
line with:
#anticipate(sut.scoreRound == 95)
Equally, replace the second check perform to:
@Check func scoreIsComputedWhenGuessIsLowerThanTarget() {
// 1. given
let guess = sut.targetValue - 5
// 2. when
sut.test(guess: guess)
// 3. then
#anticipate(sut.scoreRound == 95)
}
Then, run BullsEyeTests within the traditional method: Click on the diamond subsequent to BullsEyeTests within the Check navigator or subsequent to struct BullsEyeTests
within the editor. The app builds and runs within the simulator, after which the exams full with success:
Now, see how simple it’s to alter the anticipated situation: In both check perform, change ==
to !=
:
#anticipate(sut.scoreRound != 95)
To see the failure message, run this check after which click on the crimson X:
And click on the Present button:
It reveals you the worth of sut.scoreRound
.
Undo the change again to ==
.
Discover the opposite check teams are nonetheless there, they usually’re all XCTests. You didn’t should create a brand new goal to put in writing Swift Testing exams, so you’ll be able to migrate your exams incrementally. However don’t name XCTest assertion features from Swift Testing exams or use the #anticipate
macro in XCTests.
Including Swift Testing
Shut BullsEye and open TheMet. This app has no testing goal, so add one:
Testing System defaults to Swift Testing:
Now, have a look at your new goal’s Basic/Deployment Information:
Not surprisingly, it’s iOS 18.0. However TheMet’s deployment is iOS 17.4. You possibly can change one or the opposite, however they should match. I’ve modified TheMet’s deployment to iOS 18.
Open TheMetTests within the Check navigator to see what you bought:
import Testing
struct TheMetTests {
@Check func testExample() async throws {
// Write your check right here and use APIs like `#anticipate(...)` to test anticipated situations.
}
}
You’ll want the app’s module, so import that:
@testable import TheMet
You’ll be testing TheMetStore
, the place all of the logic is, so declare it and initialize it:
var sut: TheMetStore
init() async throws {
sut = TheMetStore()
}
Press Shift-Command-O, kind the, then Choice-click TheMetStore.swift to open it in an assistant editor. It has a fetchObjects(for:)
technique that downloads at most maxIndex
objects. The app begins with the question “rhino”, which fetches three objects. Exchange testExample()
with a check to test that this occurs:
@Check func rhinoQuery() async throws {
attempt await sut.fetchObjects(for: "rhino")
#anticipate(sut.objects.rely == 3)
}
Run this check … success!
Write one other check:
@Check func catQuery() async throws {
attempt await sut.fetchObjects(for: "cat")
#anticipate(sut.objects.rely <= sut.maxIndex)
}
Parameterized Testing
Once more, it succeeds! These two exams are very related. Suppose you need to check different question phrases. You can preserve doing copy-paste-edit, however among the best options of Swift Testing is parameterized exams. Remark out or exchange your two exams with this:
@Check("Variety of objects fetched", arguments: [
"rhino",
"cat",
"peony",
"ocean",
])
func objectsCount(question: String) async throws {
attempt await sut.fetchObjects(for: question)
#anticipate(sut.objects.rely <= sut.maxIndex)
}
And run the check:
The label and every of the arguments seem within the Check navigator. The 4 exams ran in parallel, utilizing Swift concurrency. Every check used its personal copy of sut
. If one of many exams had failed, it would not cease any of the others, and also you’d have the ability to see which of them failed, then rerun solely these to search out the issue.