pointfreeco / swift-snapshot-testing

📸 Delightful Swift snapshot testing.
https://www.pointfree.co/episodes/ep41-a-tour-of-snapshot-testing
MIT License
3.74k stars 570 forks source link

assertSnapshot causes memory leak #620

Open yonicsurny opened 2 years ago

yonicsurny commented 2 years ago

Describe the bug When using assertSnapshot the view is not released at the end of the test

To Reproduce You can reproduce this with the attached project: SnapshotIssue.zip

final class SnapshotIssueTests: XCTestCase {

    func testExample() {
        let controller = UIStoryboard(name: "Main", bundle: Bundle(for: ViewController.self)).instantiateInitialViewController()!

        trackForMemoryLeaks(controller)

        assertSnapshot(matching: controller, as: .image)
    }
}

private extension SnapshotIssueTests {

    func trackForMemoryLeaks(_ instance: AnyObject, file: StaticString = #filePath, line: UInt = #line) {
        addTeardownBlock { [weak instance] in
            XCTAssertNil(instance, "Instance should have been deallocated. Potential memory leak!", file: file, line: line)
        }
    }
}

Expected behavior When snapshot test is done, the view should be released

Screenshots

Screenshot

Environment

jamajk commented 2 years ago

Does this issue persist when using SwiftUI views?

yonicsurny commented 2 years ago

I have not tried with a SwiftUI view. But I can say that this doesn't happen with plain UIViews. It seems to affect only UIViewControllers.

adozenlines commented 1 year ago

I have had a similar issue with 1.10.0 where when the view is passed, the assert snapshot returns and when there is a view controller with a hard dependency on a UINavigationController and the UI navigation controls making the controller a child of the UINavigationController resolves or at least permits the snapshot test to return.

fruitcoder commented 5 months ago

Do you have any way to workaround this issue? This seems to happen in my case with a SwiftUI view and TCA. The store is somehow still around after the test is finished and causes test failures coming from Unimplemented dependencies (which seem to have escaped the task local scope by then).

My chain of events ist basically

start of snapshot test
snapshot test passed
start of another test (testing the reducer's functionality using TestStores)
action received in store from snapshot test
Unimplemented: @Dependency(\.date)
fruitcoder commented 5 months ago

@stephencelis I noticed you also use snapshot tests for the GameOverView in isowords (which also has long running tasks added to a task group from the task action). But you don't run in to issues because you don't create a ViewState in the view store's observation closure which accesses a Dependency inside its init. (This is how it used to look pre observation).