I'm using Quick to test my Swift code.
However, I think it doesn't release objects defined in describe scope:
class MyClass {
deinit {
print(self, #function)
}
}
final class MyClassSpec: QuickSpec {
override func spec() {
describe("") {
let foo = MyClass()
it("") {
print(foo)
expect(true).to(beTrue())
}
}
}
}
I don't see any output from print inside deinit, and a debug breakpoint inside the deinit does not get catched.
If I move foo inside it, the deinit is called.
Is this a bug in Quick, or is it normal for deinit not to be called in a test suite?
Apparently the code I wrote was not only retaining the object but was also an anti-pattern.
Even a plain old XCTestCase retains an object:
class MyClass {
deinit {
print(self, #function)
}
}
final class MyClassTest: XCTestCase {
let foo = MyClass()
func testMyClass() {
print(foo)
XCTAssert(true)
}
}
deinit is not called for foo.
This is due to a nature of XCTestCase—it never really gets deinited.
So one should always use setUp & tearDown to manage everything (or more accurately, objects with reference semantics).
I believe this directly translates to QuickSpec as well, so I should always use beforeEach & afterEach in order to manage the objects.
To "fix" the problem, I should test like:
final class MyClassSpec: QuickSpec {
override func spec() {
describe("") {
let foo: MyClass!
beforeEach { foo = MyClass() }
afterEach { foo = nil }
it("") {
print(foo)
expect(true).to(beTrue())
}
}
}
}