pointfreeco / swift-snapshot-testing

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

Snapshot is different with what's in simulator window (even with delay) #297

Closed MaximBazarov closed 10 months ago

MaximBazarov commented 4 years ago

Hi there, first of thank you for making such an awesome library!

The issue I struggled with is that I can't understand why the snapshot is different from what is on the screen Heres the simulator's: Screen Shot 2020-02-11 at 11 46 52 AM

and here's the snapshot:

testExample1 1

the only thing that makes a difference is that update

  DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(1)) {
            self.contentView.backgroundColor = .red
            self.label.textColor = .yellow
            self.setNeedsDisplay()
            self.setNeedsLayout()
        }

Test


class SnapshotAfterUpdateTests: XCTestCase {
    private var sut = UIStoryboard(
        name: "Main",
        bundle: Bundle(for: ViewController.self)
        ).instantiateInitialViewController()!

    func testExample1() {
        delay(2) // here's a delay
        assertSnapshot(matching: sut, as: .image)
    }
}

func delay(_ second: TimeInterval) {
    let date = Date(timeIntervalSinceNow: second)
    RunLoop.current.run(until: date)
}

full project to reproduce: https://github.com/MaximBazarov/SnapshotAfterUpdate

Sherlouk commented 4 years ago

Have you tried using the "wait" strategy?

Instead of:

        delay(2) // here's a delay
        assertSnapshot(matching: sut, as: .image)

It would be (I think, no compiler helping here...):

        assertSnapshot(matching: sut, as: .wait(for: 2, on: .image))
MaximBazarov commented 4 years ago

@Sherlouk thank you for the answer, I couldn't find that strategy though.

what fixed this for me is

sut.view.setNeedsDisplay()
delay(0.1)
assertSnapshot(matching: sut, as: .image)

but doesn't seem to be an elegant solution, also I don't think it's even a snapshot-testing framework problem, because when debug sut is in the state as on a screenshot, so somehow it doesn't get rendered by the time

Sherlouk commented 4 years ago

I couldn’t find that strategy though

Perhaps you are not on the latest version? Or it hasn’t been released in a tagged build (try using master rather than a version!)

MaximBazarov commented 4 years ago

@Sherlouk yes, it's in master.

so the result is the same, but doesn't solve the problem alone

func testExample1() {
    sut.view.setNeedsDisplay() // that line has to be here, no idea why yet
    assertSnapshot(matching: sut, as: .wait(for: 2, on: .image))
}
matheusalano commented 4 years ago

Not sure if I'm doing something wrong, but even when I use .wait, it only loads the viewController after the specified time interval.

Let's say I start an animation of 0.3 seconds in the viewDidAppear. How can I make the snapshot wait for this 0.3 seconds?

marcosgriselli commented 3 years ago

@MaximBazarov I just ran into a similar issue and noticed that asserting using UIViewController will never update. No matter how long you tell the .wait expectation to wait. On the other hand, asserting using UIView works flawlessly. I tried your project and assertSnapshot(matching: sut.view, as: .wait(for: 2, on: .image)) generates the expected screenshot.

@matheusalano you'll find this approach useful as well. Assert the view controller's view using the .wait strategy and you should be fine.