Test-Driven Development — Shared state in a XCTestCase class
In a XCTestCase class, you cannot share variables in the class scope. In a test case class lifecycle, each test method is invoked in a separate instance of the test case class.

Example
class Test: XCTestCase {
override func setUp() {
print("ℹ️ object identifier: \(ObjectIdentifier(self))")
}
func testExample() {
print("✅ testExample")
XCTAssertTrue(true)
} func testExample2() {
print("✅ testExample2")
XCTAssertTrue(true)
}
}
Here, we have 2 test methods and since the setUp method is called before the invocation of each test method in the class, we can check with ObjectIdentifier that the instances of the test case class are identical.
Apple Documentation about ObjectIdentifier:
A unique identifier for a class instance or metatype.
From the compilation of the above example, you get in the debug area:
ℹ️ object identifier: ObjectIdentifier(0x0000600003819140)
✅ testExample
ℹ️ object identifier: ObjectIdentifier(0x000060000381ade0)
✅ testExample2
It shows that the setUp method is actually run before each test method and each test method is invoked in a separate instance of the test case class because the identifiers are different.
Now, you are going to ask me what is the point of using setUp and tearDown methods…
Why do we need setUp and tearDown methods?
Most of the time, you don’t need these methods. One of the reason why you would need these methods is if you want to test the full lifecycle of an object. When an object is deallocated, you can make sure that there is no crash in your application. ARC takes care of deallocating objects but by removing a strong reference in the tearDown method, you test the full lifecycle of the object.
class Test: XCTestCase {
var person: Person!
override func setUp() {
person = Person()
} override func tearDown() {
person = nil
} func testExample() {
print("✅ testExample")
XCTAssertTrue(true)
}
}
Here, in the tearDown method, when you remove the strong reference from person object, if there is anything wrong, you will know about it.
Another reason why you would need these methods is if you need global state such as configuring or cleaning up:
- some data in your local database,
- service…
Testing the full lifecycle of an object, you make sure there is no side effect in your application.