How to do a TDD Setup in a Swift Playground

July 19, 2019 · 3 minute read

I’ve recently been trying my hand at writing unit tests and practicing Test-Driven Development (TDD) using Swift 5. It’s quite easy to write them in an existing app, but I want to separate and try to test functionality outside of one. The hope is to do validation without the distraction. Once I’m done with the Playground tests, I can move the working code and its tests back into the app.

Coding in isolation can help keep functionality isolated, avoid tightly coupling code, and other jargon phrases from interferring with what I’ve already built in the app.

Unfortunately, Playground doesn’t play nice with XCTest and vice versa.

Play Nice

So here’s a setup that will give your Xcode Playground a kick in the rear and set XCTest straight so that they behave. Now you can write your code alongside your unit tests using TDD that validate it. You can download a template Playground from my repository or set up an existing Playground using the code below.

You will need to set up your main Content page as follows:

import Foundation
import PlaygroundSupport
import XCTest

class SomeFunctionalityTests: XCTestCase
{
    override func setUp()
    {
        // Enter test setup here     
    }

    override func tearDown()
    {
        // Put teardown code here. This method is called after the invocation of each test method in the class.
    }
    
    func testShouldFail()
    {
        XCTFail("You must fail to succeed!")
    }
    
    func testShouldPass()
    {
        XCTAssertEqual(2+2, 4, "2+2 should be 4")
    }
}


TestRunner().runTests(testClass: SomeFunctionalityTests.self)


/* --------------------- Create Custom Observer ------------------------ */

class PlaygroundTestObserver: NSObject, XCTestObservation
{
    @objc func testCase(_ testCase: XCTestCase, didFailWithDescription description: String, inFile filePath: String?, atLine lineNumber: Int) {
        print("Test failed on line \(lineNumber): \(testCase.name), \(description)")
    }
}

let observer = PlaygroundTestObserver()
let center = XCTestObservationCenter.shared
center.addTestObserver(observer)

Now I’ll need to run the tests. Enter the TestRunner, which runs the tests from an XCTestCase and reports on the results. This is done using the same XCTestSuite mechanism which Xcode uses to run unit tests. At the end of the run, a message will be printed to the console detailing how many tests were run, for how long, and how many of them failed.

Then create a new swift file in your Sources folder, TestRunner.swift:

import Foundation
import XCTest

public struct TestRunner 
{
    public init() { }
    
    public func runTests(testClass:AnyClass)
    {
        let tests = testClass as! XCTestCase.Type
        let testSuite = tests.defaultTestSuite
        testSuite.run()
        let run = testSuite.testRun as! XCTestSuiteRun
        
        print("Ran \(run.executionCount) tests in \(run.testDuration)s with \(run.totalFailureCount) failures")
    }
}

Now all you need to do is replace the dummy tests with your own. Now you are ready to start working on your app or you can also hone your skillz with some katas without having to create a new app project every time!

Let’s start testing!

Subscribe to my email list for a monthly newsletter and special updates!