Chapters

Hide chapters

SwiftUI Cookbook

Live Edition · iOS 16.4 · Swift 5.8.1 · Xcode 14.3.1

Unit Testing Strategies in SwiftUI
Written by Team Kodeco

Unit tests are designed to verify that individual parts of your code work as expected. These are typically small, quick and isolated tests that cover a single function or method. In SwiftUI, unit testing often involves testing models, logic or custom methods outside of the views.

Let’s consider a simple SwiftUI app with a counter that can be incremented and decremented. First, you’ll build the view and its associated view model.

Here’s a simple view model for your counter:

class CounterViewModel: ObservableObject {
  @Published var count: Int = 0

  func increment() {
    count += 1
  }

  func decrement() {
    count -= 1
  }
}

This view model exposes two methods to increment and decrement the count and a published property to store the count’s current value.

Next, let’s define the view:

struct ContentView: View {
  @StateObject var viewModel = CounterViewModel()

  var body: some View {
    VStack {
      Text("Count: \(viewModel.count)")
      Button("Increment", action: viewModel.increment)
      Button("Decrement", action: viewModel.decrement)
    }
  }
}

Here’s what your preview should look like:

A simple counter app in SwiftUI.
A simple counter app in SwiftUI.

The view is simple, with two buttons to control the count and a text label to display it.

Unit Testing the View Model

You can write unit tests to ensure that the increment and decrement functions work correctly.

Creating a Test Case

Here’s how you might write a unit test for the CounterViewModel. First, ensure your project has a unit testing bundle. If it doesn’t you can add one by selecting FileNewTarget and choosing the Unit Testing Bundle template.

Then create a new file called CounterViewModelTests.swift inside your tests group and add the following code:

import XCTest
@testable import SwiftUIToolkit

class CounterViewModelTests: XCTestCase {
  func testIncrement() {
    let viewModel = CounterViewModel()
    viewModel.increment()
    XCTAssertEqual(viewModel.count, 1)
  }

  func testDecrement() {
    let viewModel = CounterViewModel()
    viewModel.decrement()
    XCTAssertEqual(viewModel.count, -1)
  }

  func testIncrementAndDecrement() {
    let viewModel = CounterViewModel()
    viewModel.increment()
    viewModel.increment()
    viewModel.decrement()
    XCTAssertEqual(viewModel.count, 1)
  }
}

Understanding the Tests

  • testIncrement: Tests that the increment method increases the count by 1.
  • testDecrement: Tests that the decrement method decreases the count by 1.
  • testIncrementAndDecrement: Tests a sequence of incrementing and decrementing to ensure the logic handles multiple calls correctly.

Note: If your test target and main target have different names, make sure to replace SwiftUIToolkit with the actual name of your main target.

Running the Tests

To run the tests, press Command-U in Xcode, or select the play button next to the test class or individual test methods.

Here’s what Xcode’s test navigator looks like after running the tests:

Unit testing success in Xcode.
Unit testing success in Xcode.

Unit testing is a vital practice that helps you write more robust and maintainable code. In this example, you’ve explored how to test a simple view model in SwiftUI. These principles can be extended to more complex logic, such as networking or data transformations, to ensure your code behaves as expected under various conditions.

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2024 Kodeco Inc.