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.

1 Answer 1

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())
Sign up to request clarification or add additional context in comments.

1 Comment

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.