XCTest
02 / 02

XCTest Basics: TestCase, Assertions & Async

XCTest: TestCase, Assertions & Async

XCTest is Apple's native testing framework for Swift and Objective-C, integrated directly into Xcode. Tests run from the IDE (Cmd+U), with results shown inline in the editor and the Test Navigator -- no third-party test runner setup required.

Writing a Test Case

import XCTest
@testable import MyApp

class CalculatorTests: XCTestCase {
    var sut: Calculator!

    // Re-creates fresh state before EACH test method
    override func setUp() {
        sut = Calculator()
    }

    override func tearDown() {
        sut = nil
    }

    func testAddition() {
        // Arrange-Act-Assert
        let result = sut.add(2, 3)
        XCTAssertEqual(result, 5)
    }

    func testDivisionByZeroThrows() {
        XCTAssertThrowsError(try sut.divide(10, by: 0)) { error in
            XCTAssertEqual(error as? CalculatorError, .divisionByZero)
        }
    }
}

Common Assertions

  • XCTAssertEqual(a, b) / XCTAssertNotEqual(a, b) -- value equality with a descriptive failure message.

  • XCTAssertTrue(x) / XCTAssertFalse(x) -- Boolean checks.

  • XCTAssertNil(x) / XCTAssertNotNil(x) -- more concise and standardized than a manual if + XCTFail.

  • XCTAssertThrowsError / XCTAssertNoThrow -- verifies a throwing function's error behavior.

  • XCTUnwrap(optional) -- safely unwraps, failing the TEST (not crashing the process) if nil, unlike force-unwrapping with !.

Testing Async Code with XCTestExpectation

func testFetchUserSucceeds() {
    let expectation = XCTestExpectation(description: "fetch completes")

    networkClient.fetchUser(id: 1) { user in
        XCTAssertEqual(user?.name, "Alice")
        expectation.fulfill()
    }

    // Pauses the test until fulfill() is called, or fails on timeout
    wait(for: [expectation], timeout: 5)
}

Keep your own version of these notes — editable, searchable, and organised by your stack.

Start free