Swift Testing basicsswift-6.4/ios-26
Lesson 1 / 5
Unit 12 · Swift Testing

Swift Testing basics

Note

Unit 12 · Swift Testing. This is the framework Segue itself uses to grade every exercise you submit, so you've been reading its output since Unit 1 — now you write it.

Swift Testing is the modern, first-party testing framework: a test is just a function marked @Test, and an assertion is just #expect(...) wrapped around an ordinary boolean expression. There's no base class to subclass, no test-prefix naming rule, no ceremony. If you can write a function and a comparison, you can write a test.

The smallest possible test

import Testing

@Test func additionWorks() {
    #expect(1 + 1 == 2)
}

Three things are load-bearing: you import Testing, you mark the function @Test, and you state what you expect with #expect. The macro takes any Bool expression. When it's false, the test fails and — because #expect is a macro, not a plain function — the report shows you the actual values on each side of the ==, not just "expected true, got false."

#expect is a macro (the # prefix), which is why it can see into your expression and report 1 + 1 → 2, expected 3. That richer failure message is a real day-to-day advantage.

Testing your own code

A test exercises a function and asserts on the result. Say you have this in your module:

func greeting(for name: String) -> String {
    "Hello, \(name)!"
}

The test names the behavior and checks it:

import Testing

@Test("greeting includes the name")
func greetingIncludesName() {
    #expect(greeting(for: "Ada") == "Hello, Ada!")
}

The string in @Test("…") is a display name. Use it — a test list that reads "greeting includes the name" beats one that reads greetingIncludesName(). The function name still exists for the compiler; the display name is for humans reading results.

Grouping with @Suite

Related tests live together in a suite. A struct (or actor) marked @Suite groups its @Test methods, and — this is the quiet superpower — Swift Testing makes a fresh instance of the suite for every test, so tests can't leak state into each other.

@Suite("Greeting")
struct GreetingTests {
    let name = "Ada"   // rebuilt fresh for each test below

    @Test("includes the name")
    func includesName() {
        #expect(greeting(for: name) == "Hello, Ada!")
    }

    @Test("is never empty")
    func neverEmpty() {
        #expect(!greeting(for: name).isEmpty)
    }
}

Stored properties become your per-test setup; the initializer runs before each test, and (if you add one) deinit runs after. No shared mutable state, no ordering surprises.

Tip

You don't always need a @Suite. Free @Test functions at file scope are perfectly valid and are what the Segue runner uses for most exercises. Reach for a suite when tests share setup (a common fixture) or when grouping makes the results easier to read.

How Segue grades you with this

Every graded exercise ships a hidden @Test suite you never see. When you press Run, your submission is compiled into a library module named Solution, and the hidden tests do @testable import Solution and call your code:

import Testing
@testable import Solution   // your submission, exposed to the tests

@Test("fizzbuzz(15) is FizzBuzz")
func fizzbuzzFifteen() {
    #expect(fizzbuzz(15) == "FizzBuzz")
}

@testable import exposes your internal (default-visibility) declarations to the test target, which is why exercise starters never mark the graded function private. Pass all the hidden @Tests and the exercise is green. That's the whole grader — the same framework in this lesson, pointed at your code.

Your turn

Implement FizzBuzz as a pure function and watch a small suite check each rule.

FizzBuzz as testable logic
Edit the code on the right, then run the hidden tests.

Knowledge check

Q: What are the three things every Swift Testing file needs to run one check? import Testing, a function marked @Test, and an assertion — #expect(someBool).

Q: Why can two tests in the same @Suite not corrupt each other's state? Swift Testing constructs a brand-new instance of the suite type for each test, so stored properties are fresh every time. There's no shared instance to leak through.

Solution.swiftSwift Testing
ConsoleReady · runs in a sandboxed Swift container
Press Run to compile against the hidden test suite.