After implementing unit tests in a Swift Testing file, I wanted to check for possible memory leaks using the following strategy (credit to this YouTube video for teaching it)
weak var
pointing to your class (a ViewModel in my case) upon initializing your test suite struct
.#expects()
check (XCTAssertNil()
in XCTest terms), to ensure the strong reference has been let go of in memory (thereby preventing a memory leak).import Testing
@testable import Quote_Droplet
@Suite("Single Quote View Model Tests") struct SingleQuoteViewModel_Tests {
// other instance variables
let sut: SingleQuoteViewModel
weak var weakSUT: SingleQuoteViewModel?
@MainActor init () {
// other instance variables
self.sut = SingleQuoteViewModel(
// parameters passed into the ViewModel from the other instance variables
)
self.weakSUT = self.sut
}
deinit {
#expect(weakSUT == nil)
}
// test functions
}
The issue is, that I get the compilation error, Deinitializer cannot be declared in struct 'SingleQuoteViewModel_Tests' that conforms to 'Copyable'
The fix suggested by the compiler is to "Consider adding ~Copyable
to struct SingleQuoteViewModel_Tests
". However, I believe the answer I've put below, as sourced from Apple's documentation is more applicable to what I'm trying to accomplish.
TL;DR: change the test struct
to a class
. Then, if your tests are failing, mark the test suite as .serialized
(see this timestamped section of a YouTube video for the reason why):
Before:
@Suite("Single Quote View Model Tests") struct SingleQuoteViewModel_Tests {
After (Fixed):
@Suite("Single Quote View Model Tests", .serialized) final class SingleQuoteViewModel_Tests {
My source for switching struct
-> final class
is from the Apple documentation on Swift Testing, which makes that suggestion here: "If teardown is needed, declare your test suite as a class or as an actor rather than as a structure and implement deinit".
However, there's a caveat to this that Apple doesn't mention explicitly in that section, and what the YouTube video linked above explains in more detail, regarding parallel unit test execution messing with expected behaviour.
Tip: if you don't need inheritance, then adding final
for compiler optimization is a good idea (see this Stackoverflow answer explaining why).