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 deinit
ed.
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())
}
}
}
}