.st0{fill:#FFFFFF;}

This Swift XCTestCase Template Is Streamlined to Help Your Flow 

 February 9, 2021

by  Jon Reid

Say you have a project named SampleProject. And you want to create a new unit test suite. So you Command-N to make a new file, and select “Unit Test Case Class.”

New file: Unit Test Case Class

If we give it the name AppleTests, here’s what Apple provides:

//
// AppleTests.swift
// SampleProjectTests
//
// Created by Jon Reid on 12/12/20.
//
 
import XCTest
 
class AppleTests: XCTestCase {
    override func setUpWithError() throws {
        // Put setup code here. This method is called before the invocation of each test method in the class.
    }
 
    override func tearDownWithError() throws {
        // Put teardown code here. This method is called after the invocation of each test method in the class.
    }
 
    func testExample() throws {
    // This is an example of a functional test case.
        // Use XCTAssert and related functions to verify your tests produce the correct results.
    }
 
    func testPerformanceExample() throws {
        // This is an example of a performance test case.
        self.measure {
            // Put the code you want to measure the time of here.
        }
    }
 
}

It’s instructive… the first time. After that, it’s only noisy. So I use a customized file template for new unit test suites. Command-N and select “Swift XCTest Test Suite.”

New file: Swift XCTest Test Suite

It suggests a file name ending with Tests. If we give it the name QualityCodingTests, here’s what I provide:

@testable import SampleProject
import XCTest
 
final class QualityCodingTests: XCTestCase {
 
    func test_zero() throws {
        XCTFail("Tests not yet implemented in QualityCodingTests")
    }
}

Isn’t that better? You can download it here:

Curious about the problems I have with Apple’s template and the decisions I made for my custom template? Read on…

What's In Apple’s Template?

Let’s look more closely at what each file template provides. We’ll start with Apple’s “Unit Test Case Class” template.

Prompting for Unnecessary Inputs

If you select the template, Xcode displays a large dialog:

New file dialog in Xcode

It feels like this large, clunky dialog handles various dynamic options. For test suites:

  • It asks for a class name but doesn’t suggest any pattern.
  • It asks if we want it to be a subclass of XCTestCase, which of course we do.
  • It asks for the programming language.

What do we get next? Another dialog. Xcode prompts us for the location, group, and target.

File Comment Block

Then we get the file content. It starts with a file comment block:

//
// AppleTests.swift
// SampleProjectTests
//
// Created by Jon Reid on 12/12/20.
//

What do you do with these? I delete them, every time. They serve no useful purpose in a project. Even if you work at a company that requires a standard file comment block at the top of each file, it doesn’t look like this. Delete.

Import Lacking Production Code

Next, we have the import statements. Or rather, import statement, singular:

import XCTest

This is incomplete. To access your production code, you need to @testable import the module.

Placeholders for Set-Up and Tear-Down

After the class declaration, we get placeholders for set-up and tear-down:

override func setUpWithError() throws {
    // Put setup code here. This method is called before the invocation of each test method in the class.
}
 
override func tearDownWithError() throws {
    // Put teardown code here. This method is called after the invocation of each test method in the class.
}

These are instructive, with explanatory comments. But I prefer not to create set-up and tear-down when I start creating a new test suite. I don’t want to make assumptions about what belongs there. Instead, I code a test, then another test. Then I can begin to see what might belong in set-up.

Set-up is there to serve the tests. Wait until you have tests so you can discover what belongs there. Delete them, comments and all.

…Wait, Really Delete Those Placeholders?

You may resist the idea of deleting these function placeholders. You may want them there because you don’t want to type them in later. That’s where my test-oriented code snippets come in. I’m lazy, and don’t enjoy typing the same things over and over. So my code snippets define:

test case, set-up, tear-down, test outlet, test button, and more

These code snippets are available separately by subscribing to Quality Coding:

Two Test Placeholders, Including a Performance Test

Finally, we get a place to put our test. But again, they are mini-tutorials:

func testExample() throws {
    // This is an example of a functional test case.
    // Use XCTAssert and related functions to verify your tests produce the correct results.
}
 
func testPerformanceExample() throws {
    // This is an example of a performance test case.
    self.measure {
        // Put the code you want to measure the time of here.
    }
}

Instructive code with explanatory comments is nice the first time. After that, it’s noise. I want to start writing a new test case by typing something new, not by deleting comments.

And can I tell you how many performance test cases I’ve written? Zero. They probably have their place, just not for my needs. Delete.

I want less code, not more.

What’s In My Template?

Now let’s look at the workflow of my “Swift XCTest Test Case” template.

Simple Prompt Suggesting Naming Pattern

Here’s what Xcode shows when you select my template:

New file prompt with "Tests" naming suggestion

First, notice that it suggests a naming pattern. I use the suffix Tests to name test suites because a test suite holds a group of test cases. Just hit Up-Arrow to move the cursor to the beginning of the field, and start typing the rest.

Note that it doesn’t ask you what to subclass, or what programming language to use. You already selected “Swift XCTest Test Case” so we know. (The download also includes an Objective-C version.)

Then specify the location, group, and target.

No File Comment Block

The file doesn’t start with a file comment block. There’s nothing to delete. Move along, move along.

Useful import Statements

The first thing in the file is not one, but two import statements:

@testable import SampleProject
import XCTest

The file template makes an educated guess about the name of your production code module. It assumes it’s the same as your project name.

This isn’t always true, of course. But even when it’s wrong, at least it shows that you should use @testable import to access the production code.

Class Declared final

The class declaration has a subtle difference from Apple’s template.

final class QualityCodingTests: XCTestCase {

I like to declare my test suites as final. Why? It’s very unusual to subclass test suites, so we don’t need dynamic dispatch to call test helpers. Private test helpers become direct function calls instead of dynamic messaging.

I doubt this makes much difference. But why leave any performance on the table? I want tests to run as fast as they possibly can.

Test Zero

Before I write the first test, I like to execute what I call Test Zero:

func test_zero() throws {
    XCTFail("Tests not yet implemented in QualityCodingTests")
}

This is a trick I describe in my book iOS Unit Testing by Example. Test Zero helps check that the new test suite does nothing well. It’s the first check of our new infrastructure.

Once the test fails correctly, I delete it. Then using my test-oriented code snippets, I type “test” to begin writing a test case. The test suite is gloriously empty.

I’m a lazy programmer and don’t want to waste my time deleting things I don’t need, and typing things I do need. I hope you find my XCTestCase file template useful! 

Be lazy. Don't waste time deleting test code you don't need, and typing test code you do need. This XCTestCase template helps.

Click to Tweet

Jon Reid

About the author

Programming was fun when I was a kid. But working in Silicon Valley, I saw poor code lead to fear, with real human costs. Looking for ways to make my life better, I learned about Extreme Programming, including unit testing, test-driven development (TDD), and refactoring. Programming became fun again! I've now been doing TDD in Apple environments for 19 years. I'm committed to software crafting as a discipline, hoping we can all reach greater effectiveness and joy.

  • Great writeup, Jon!

    I’ve been creating my own file templates ever since I learned about them from your wonderful Objective-C example. :]

    One suggestion for improving the Swift template would be to make the template icon a different color than the Objective-C version. I went with orange to feel more Swifty, and it really makes a big difference in quickly identifying it from the other templates.

  • {"email":"Email address invalid","url":"Website address invalid","required":"Required field missing"}
    >