NEW! Pre-order my latest book, Testing Swift! >>

How to do conditional test tear down using addTeardownBlock()

Paul Hudson       @twostraws

All XCTestCase subclasses have access to setUp() and tearDown() instance methods, plus setUp() and tearDown() class methods for one-time setup and tear down. But sometimes you need to add conditional tear down code: if your test creates a resource that must be destroyed, you can add that as an additional tear down step using the addTeardownBlock() method.

As an example, consider this test method:

func testDatabaseQuery() {
    let database = connectToDatabase()
    let results = database.fetchExampleData()

    XCTAssertEqual(results.count, 1, "We should receive exactly one result.")
}

That connects to a database, runs an example query, and checks the result. However, behind the scenes our database needs to be reset every time a connection is finished. If all our tests relied on the database being created then we could add the cleanup code either to the tearDown() instance method or to the tearDown() class method, but if it’s only used sometimes then that isn’t an option.

Fortunately, the addTeardownBlock() is designed to fix this: you can pass in any code you want to run when your test is being torn down, and it will be run after the current test finishes, but before the tearDown() instance method.

In the case of the connectToDatabase() method that needs clean up, we could write that clean up code directly into each test, but that just duplicates code and is likely to cause problems. So, instead we’re going to add the clean up right inside the connection code, like this:

func connectToDatabase() -> Database {
    let database = Database()
    database.connect()

    addTeardownBlock {
        database.cleanUp()
    }

    return database
}

That database.cleanUp() code will be called only when the surrounding test is complete, so it acts a bit like defer except the scope is the current test.

It’s important to get the order of set up and tear down correct, because various things happen at different times. If you have two tests in your XCTestCase here’s how it would look:

  1. The setUp() class method is called.
  2. The setUp() instance method is called.
  3. One test is run.
  4. Any tear down blocks that were registered are run.
  5. The tearDown() instance method is called.
  6. The setUp() instance method is called again.
  7. The second test is run.
  8. Any tear down blocks that were registered are run.
  9. The tearDown() instance method is called again.
  10. The tearDown() class method is called.

You won’t need to use tear down blocks often, but they are a useful tool to have.

Available from iOS

Did this solution work for you? Please pass it on!

Other people are reading…

About the Swift Knowledge Base

This is part of the Swift Knowledge Base, a free, searchable collection of solutions for common iOS questions.

Take Swift further!

Your Swift skills let you make apps for macOS, watchOS, tvOS, and more, and for one low price you can learn it all with my Swift Platform Pack!

MASTER SWIFT NOW
Buy Testing Swift Buy Practical iOS 12 Buy Pro Swift Buy Swift Design Patterns Buy Swift Coding Challenges Buy Server-Side Swift (Vapor Edition) Buy Server-Side Swift (Kitura Edition) Buy Hacking with macOS Buy Advanced iOS Volume One Buy Advanced iOS Volume Two Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with Swift Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Beyond Code

Was this page useful? Let me know!

Click here to visit the Hacking with Swift store >>