0

I am trying to listen to the print function from __main__.py. I use the patch function from unittest.mock.

def main_tester(command):
    fake_command()
    capturedoutput = io.StringIO()
    sys.stdout = capturedoutput
    with patch('sys.argv', command.split(" ")):
        from SIESTAstepper import __main__ as rtmain
    sys.stdout = sys.__stdout__
    return capturedoutput.getvalue()

The problem is, it returns an empty string "" instead of the captured output.

The code is from here if you are willing to see the full project.

Eftal Gezer
  • 191
  • 1
  • 8
  • There is no `print` statement anywhere in that `__main__.py`. – Daniil Fajnberg Sep 25 '22 at 15:48
  • @DaniilFajnberg But the functions used in `__main__.py` has `print` statements. I can listen elsewhere but not `__main__.py`. – Eftal Gezer Sep 25 '22 at 17:49
  • I don't get it. If there are specific `print` statements that you want to check, why not just mock those directly? That is what the [`assert_called_...`](https://docs.python.org/3/library/unittest.mock.html#unittest.mock.Mock.assert_called) methods of the `Mock` class are for. To verify that a mocked object has been called with specific arguments. What is the point of the `StringIO` monkey-patching of the `sys.stdout`? – Daniil Fajnberg Sep 25 '22 at 18:05
  • @DaniilFajnberg I am new in unit testing. This is my first time hearing `assert_called_... `. I was patching the `sys.argv` to be able to use `__main__.py`. Is not it the correct way? – Eftal Gezer Sep 25 '22 at 18:11

1 Answers1

0

Your __main__ module should probably not unconditionally call the main function. It is common practice to put that in the if __name__ == '__main__': block. It is one of the most viewed threads on this platform.

If you put the main call behind that if-statement, you can safely import from your __main__ module without the import causing the execution of the main function.

Then you can properly test main because you can import it in your test module and simply call it with whatever arguments you want for testing purposes.

To test specific print statements, you can patch/mock the print function in the module being tested. Something like this for example:

from unittest.mock import MagicMock, patch

@patch("module_being_tested.print")
def test_some_function_that_should_call_print(mock_print: MagicMock) -> None:
    ...
    some_function(some_argument)
    mock_print.assert_called_once_with(
        "A string it should have been called with"
    )

In your comment you mentioned you were new to unit testing. I just wanted to draw your attention to the definition of unit testing. Because it seems like main_tester is supposed to test behavior that is not even part of the __main__.main function at all. You should make sure that you actually isolate the units (the functions) you are testing from all your other code. That is where mocking comes in handy.

When you are testing main, you should actually only test main and not other functions being called by main.

Daniil Fajnberg
  • 12,753
  • 2
  • 10
  • 41
  • How can I return the print output in this function? Or, what should I return to assert? – Eftal Gezer Sep 25 '22 at 19:12
  • I have implemented ``` @patch("SIESTAstepper.__main__.print") def main_tester(mock_print: MagicMock, command=None) -> None: fake_command() from SIESTAstepper.__main__ import main as rtmain rtmain(args=command.split(" ")) mock_print.assert_called_once_with() ``` so far. – Eftal Gezer Sep 25 '22 at 19:14