tophat / syrupy

:pancakes: The sweeter pytest snapshot plugin
https://tophat.github.io/syrupy
Apache License 2.0
501 stars 33 forks source link

Stuck on a loop generating a snapshot, when using mocks #781

Closed jandom closed 1 year ago

jandom commented 1 year ago

What's your question?

hi there, i've tried to mix syrup with unittest.mock patch and appear to have stumbled on a problem

Here is my simple example

from unittest.mock import patch, mock_open

def foo():
    lines = [f"line {i}"for i in range(10)]
    with open("/tmp/hello.txt", "w") as f:
        f.writelines(lines)
    return lines

@patch("builtins.open", new_callable=mock_open)
def test_foo(mock_builtins_open, snapshot):
    assert foo() == snapshot

This will never pass, syrup always reports that

E This snapshot needs to be regenerated. This is typically due to a major Syrupy update.

You can generate the snapshot on a loop, nothing happens until the mock is removed

from unittest.mock import patch, mock_open

def foo():
    lines = [f"line {i}"for i in range(10)]
    with open("/tmp/hello.txt", "w") as f:
        f.writelines(lines)
    return lines

def test_foo(snapshot):
    assert foo() == snapshot

Then everything works but obviously I want to avoid writing to the file in the test. Is there any way to mix mock and syrup?

noahnu commented 1 year ago

syrupy writes files so you can't mock file IO interactions.

In general, I wouldn't recommend mocking built-ins at all and instead would recommend writing to temp directories.

If you absolutely must mock open, then you need to scope the mock to just the code you're testing. E.g.:

def test_foo(snapshot):
    with patch("builtins.open", new_callable=mock_open) as mock_builtins_open:
        result = foo()
    assert result == snapshot
jandom commented 1 year ago

Thanks @noahnu, that's awesome and it worked for me. Not knowing the internals of pytest, I sort of assumed some isolation between a pytest fixture and the mocking... :P