1

I am writing a program which creates a subprocess.Popen pipeline. I'm trying to mock subprocess.Popen such that each call returns a distinct MagicMock so I can ensure methods are called on specific (or all) processes in the pipeline.

I also want this mock to be autospec'd based on subprocess.Popen but keep getting an error that I can't autospec based on a mock.

Currently my code is as follows:

@pytest.fixture
def Popen(mocker: 'pytest_mock.MockFixture'):
    def popen_factory(*args, **kwargs):
        popen = mocker.MagicMock()  # mocker.create_autospec(subprocess.Popen)
        popen.stdin = open(os.devnull, "wb")
        popen.stdout = open(os.devnull, "rb")
        popen.wait.return_value = 0
        return popen

    Popen = mocker.patch.object(subprocess, 'Popen', autospec=True)
    Popen.side_effect = popen_factory
    yield Popen
nyanpasu64
  • 2,805
  • 2
  • 23
  • 31

1 Answers1

0

Since mocker.patch.object(subprocess, 'Popen', autospec=True) overwrites subprocess.Popen, I have to assign the existing value to a local variable, and use that instead.

@pytest.fixture
def Popen(mocker: 'pytest_mock.MockFixture'):
    real_Popen = subprocess.Popen

    def popen_factory(*args, **kwargs):
        popen = mocker.create_autospec(real_Popen)
        popen.stdin = open(os.devnull, "wb")
        popen.stdout = open(os.devnull, "rb")
        popen.wait.return_value = 0
        return popen

    Popen = mocker.patch.object(subprocess, 'Popen', autospec=True)
    Popen.side_effect = popen_factory
    yield Popen
nyanpasu64
  • 2,805
  • 2
  • 23
  • 31
  • you know what this needs? A Rust borrow checker so when `popen_factory` holds a & reference to `subprocess`, `mocker.patch.object(&mut subprocess, 'Popen'...)` can't overwrite `subprocess.Popen`. – nyanpasu64 Dec 05 '19 at 10:17