3

I'm trying to write some unittests for my project but I'm having problems with writing unittests for functionality from cmd module.

I followed example from this question: Create automated tests for interactive shell based on Python's cmd module

Let's consider following:

#!/usr/bin/env python3

import cmd
import sys


class Interpreter(cmd.Cmd):
    def __init__(self, stdin=sys.stdin, stdout=sys.stdout):
        cmd.Cmd.__init__(self, stdin=stdin, stdout=stdout)

    def do_show(self, args):
        print("Hello world!")

if __name__ == "__main__":
    interpreter = Interpreter()
    interpreter.onecmd("show")

And this is my unittest:

import unittest
import unittest.mock
import main
import sys


class CmdUiTest(unittest.TestCase):
    def setUp(self):
        self.mock_stdin = unittest.mock.create_autospec(sys.stdin)
        self.mock_stdout = unittest.mock.create_autospec(sys.stdout)

    def create(self):
        return main.Interpreter(stdin=self.mock_stdin, stdout=self.mock_stdout)

    def _last_write(self, nr=None):
        """:return: last `n` output lines"""
        if nr is None:
            return self.mock_stdout.write.call_args[0][0]
        return "".join(map(lambda c: c[0][0], self.mock_stdout.write.call_args_list[-nr:]))

    def test_show_command(self):
        cli = self.create()
        cli.onecmd("show")
        self.assertEqual("Hello world!", self._last_write(1))

If I understand right, in the unittest mock of sys.stdin and sys.stdout is being created and with method _last_write() I should be able to access list of arguments that were written onto mocked stdout using self.mock_stdout.write.call_args_list[-nr:]

Result of the test

/home/john/rextenv/bin/python3 /home/john/pycharm/helpers/pycharm/utrunner.py /home/john/PycharmProjects/stackquestion/tests/test_show.py::CmdUiTest::test_show_command true
Testing started at 20:55 ...
Hello world!

Process finished with exit code 0

Failure
Expected :'Hello world!'
Actual   :''
 <Click to see difference>

Traceback (most recent call last):
  File "/home/john/PycharmProjects/stackquestion/tests/test_show.py", line 25, in test_show_command
    self.assertEqual("Hello world!", self._last_write(1))
AssertionError: 'Hello world!' != ''
- Hello world!
+ 

As you can see the Hello world! from do_show() is actually printed onto stdout. But for some reason self.mock_stdout.write.call_args_list always returns empty list.

(Btw. I'm running tests from Pycharm, but I also tried executing them from shell, no difference)

All I need is to be able to somehow test functionality of my cmd interpreter. Just compare print output.

I also tried to mock builtin print but that broke my test even more (the actual code and test is more complex). But I don't believe mocking print and checking called_with() is really not necessary or correct solution. Mocking stdout should be possible.

Community
  • 1
  • 1
J91321
  • 697
  • 1
  • 7
  • 19

1 Answers1

3

There is a difference with orld Not sure if this is what you wanted, the last_write is definitely not working!

F
======================================================================
FAIL: test_show_command (__main__.CmdUiTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "./int.py", line 32, in test_show_command
    self.assertEqual('Hello World!', fakeOutput.getvalue().strip())
AssertionError: 'Hello World!' != 'Hello world!'
- Hello World!
?       ^
+ Hello world!
?       ^


----------------------------------------------------------------------
Ran 1 test in 0.003s

FAILED (failures=1)

Change to use unitte.mock.patch - my python version is 3.5

from unittest.mock import patch
from io import StringIO


    # not working for reasons unknown
    def _last_write(self, nr=None):
        """:return: last `n` output lines"""
        if nr is None:
            return self.mock_stdout.write.call_args[0][0]
        return "".join(map(lambda c: c[0][0], self.mock_stdout.write.call_args_list[-nr:]))

    # modified with unittest.mock.patch
    def test_show_command(self):
        # Interpreter obj
        cli = self.create()
        with patch('sys.stdout', new=StringIO()) as fakeOutput:
            #print ('hello world')
            self.assertFalse(cli.onecmd('show'))
        self.assertEqual('Hello World!', fakeOutput.getvalue().strip())
Gang
  • 2,658
  • 3
  • 17
  • 38
  • 1
    It'll do thank you. Though it's strange why _last_write() is not working. It seems cmd module skips .write() altogether when something else than stdout is supplied. Probably bug in the cmd module. – J91321 Dec 29 '15 at 13:15