0

Trying to test stdout of a local method in an object that verifies users account.

As an example,

class Foo: def __init__(self, bar, baz): self.bar = bar if baz: self.something = self.load_something() else: print('Error initializing') def load_something(self): return '' def make_subprocess_call(self): return 'stdout', 'stderr' def confirm_something(self): subprocess_stdout, subprocess_stderr = self.make_subprocess_call() if subprocess_stdout: print('good output') else: print('err output', subprocess_stderr) 

Then to test:

from Foo_Class_File import Foo import mock import pytest @mock.patch('Foo_Class_File.Foo.load_something', return_value = {'a': 'b'}) @mock.patch('Foo_Class_File.Foo.make_subprocess_call', return_value=('more stdout', 'more_stderr')) def test_confirm_something(testdir, capsys): test_foo = Foo(testdir, True) test_foo.confirm_something() out, err = capsys.readouterr() assert out == 'more stdout' assert err == 'more_stderr' 

Then running:

python3 -m pytest test_foo_file.py 

gives:

============================================================================== FAILURES =============================================================================== _______________________________________________________________________ test_confirm_something ________________________________________________________________________ testdir = <MagicMock name='make_subprocess_call' id='140533346667632'>, capsys = <MagicMock name='load_something' id='140533346970400'> @mock.patch('Foo_Class_File.Foo.load_something', return_value = {'a': 'b'}) @mock.patch('Foo_Class_File.Foo.make_subprocess_call', return_value=('more stdout', 'more_stderr')) def test_confirm_something(testdir, capsys): test_foo = Foo(testdir, True) test_foo.confirm_something() > out, err = capsys.readouterr() E ValueError: not enough values to unpack (expected 2, got 0) blah.py:10: ValueError ------------------------------------------------------------------------ Captured stdout call ------------------------------------------------------------------------- good output ======================================================================= short test summary info ======================================================================= FAILED test_foo_file.py::test_confirm_something - ValueError: not enough values to unpack (expected 2, got 0) ========================================================================== 1 failed in 0.11s ========================================================================== 

I'm just really confused because it says that is capturing the stdout?

I think it has to do with instantiating the object and then calling the method, as I have no problem capturing with capsys if there is just the object, but as soon as I try to instantiate it and call a method, what you see above it what happens. I couldn't find a similar case online or in the documentation, not for lack of trying (or perhaps my google foo is weak today).

Apologies if I am doing something silly but at this point I am concerned for the safety of my keyboard and figured I would reach out. Any help/suggestions are appreciated. You can run this test case I posted, it should give you the same error as posted.

1 Answer 1

2

Your problem is that you supplied patch decorators, but did not add the respective arguments to the test function. This would be correct:

@mock.patch('Foo_Class_File.Foo.load_something', return_value = {'a': 'b'}) @mock.patch('Foo_Class_File.Foo.make_subprocess_call', return_value=('more stdout', 'more_stderr')) def test_confirm_something(mocked_call, mocked_load, testdir, capsys): # note the additional arguments test_foo = Foo(testdir, True) test_foo.confirm_something() out, err = capsys.readouterr() assert out == 'good output\n' assert err == '' 

Or, alternatively:

@mock.patch('Foo_Class_File.Foo.load_something') @mock.patch('Foo_Class_File.Foo.make_subprocess_call') def test_confirm_something(mocked_call, mocked_load, testdir, capsys): mocked_call.return_value = ('more stdout', 'more_stderr') mocked_load.return_value = {'a': 'b'} test_foo = Foo(testdir, True) ... 

What happened in your code is that the fixtures testdir and capsys has been used as the mocked parameters instead (which are expected as the first arguments in the test function), leading to both being a mock object instead of the fixture.

Side note: it would be nice if pytest gave a warning in this case, something like "You are using a fixture name as a name for a patched object, maybe you forgot to add the patched object argument?"...

Note that I have replaced the assertions in your example code with the assertions that would actually pass using your code.

Sign up to request clarification or add additional context in comments.

3 Comments

In retrospect I probably should've noticed testdir and capsys getting assigned those MagicMock types pertaining to the decorators. In any case, thanks for your help and the detailed solution :).
In retrospect all seems to be more clear ;) Glad I could help!
actually, equally hilarious follow up, I just switched off the decorators and installed the mocker fixture, but appreciate the help in understanding pytest :)

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.