Swift Quick framework memory leak

246 Views Asked by At

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?

1

There are 1 best solutions below

1
Jay Lee On BEST ANSWER

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 XCTestCaseit 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())
            }
        }
    }
}