NEW: Got a question? Get help on our new forums! >>

How to do conditional test tear down using addTeardownBlock()

Swift version: 5.1

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.

Hacking with Swift is sponsored by Instabug

SPONSORED Catch bugs as soon as they happen and know exactly why a crash occurred by integrating Instabug's SDK in one minute. You will automatically receive device data, network logs, and reproduction steps with every bug and crash report.

Learn more and get started for free

Sponsor Hacking with Swift and reach the world's largest Swift community!

Support my work on Patreon

Available from iOS

Similar solutions…

About the Swift Knowledge Base

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

BUY OUR BOOKS
Buy Pro Swift Buy Swift Design Patterns Buy Testing Swift Buy Hacking with iOS Buy Swift Coding Challenges Buy Swift on Sundays Volume One Buy Server-Side Swift (Vapor Edition) Buy Advanced iOS Volume One Buy Advanced iOS Volume Two Buy Advanced iOS Volume Three Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with macOS Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Server-Side Swift (Kitura Edition) Buy Beyond Code

Was this page useful? Let us know!

Average rating: 5.0/5