lundi 28 décembre 2015

Writing unittest for python3 shell based on cmd module

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.

Aucun commentaire:

Enregistrer un commentaire