基于Python的cmd模块为交互式shell创建自动化测试

时间:2022-09-02 13:48:42

I am building an interactive shell using Python 3 and the cmd module. I have already written simple unit tests using py.test to test the individual functions, such as the do_* functions. I'd like to create more comprehensive tests that actually interact with the shell itself by simulating a user's input. For example, how could I test the following simulated session:

我正在使用Python 3和cmd模块构建交互式shell。我已经使用py.test编写了简单的单元测试来测试各个函数,例如do_ *函数。我想创建更全面的测试,通过模拟用户的输入实际上与shell本身进行交互。例如,我如何测试以下模拟会话:

bash$ console-app.py
md:> show options
  Available Options:
  ------------------
  HOST      The IP address or hostname of the machine to interact with
  PORT      The TCP port number of the server on the HOST
md:> set HOST localhost
  HOST => 'localhost'
md:> set PORT 2222
  PORT => '2222'
md:>

2 个解决方案

#1


You can mock input or input stream passed to cmd to inject user input but I find more simple and flexible test it by onecmd() Cmd API method and trust how Cmd read input. In this way you cannot care how Cmd do the dirty work and test directly by users command: I use cmd both by console and socket and this I cannot care where the stream come from.

您可以模拟传递给cmd的输入或输入流以注入用户输入,但我发现通过onecmd()Cmd API方法更简单灵活地测试它并信任Cmd如何读取输入。通过这种方式,您无法关心Cmd如何通过用户命令直接执行脏工作和测试:我通过控制台和套接字使用cmd,这我不关心流来自何处。

Moreover I use onecmd() to test even do_* (and occasionally help_*) methods and make my test less coupled to the code.

此外,我使用onecmd()来测试甚至do_ *(偶尔帮助*)方法,并使我的测试更少地耦合到代码。

Follow a simple example of how I use it. create() and _last_write() are helper methods to build a MyCLI instance and take the last output lines respectively.

按照我如何使用它的简单示例。 create()和_last_write()是帮助方法,用于构建MyCLI实例并分别获取最后一行输出。

from mymodule import MyCLI
from unittest.mock import create_autospec

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

    def create(self, server=None):
        return MyCLI(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_active(self):
        """Tesing `active` command"""
        cli = self.create()
        self.assertFalse(cli.onecmd("active"))
        self.assertTrue(self.mock_stdout.flush.called)
        self.assertEqual("Autogain active=False\n", self._last_write())
        self.mock_stdout.reset_mock()
        self.assertFalse(cli.onecmd("active TRue"))
        self.assertTrue(self.mock_stdout.flush.called)
        self.assertEqual("Autogain active=True\n", self._last_write())
        self.assertFalse(cli.onecmd("active 0"))
        self.assertTrue(self.mock_stdout.flush.called)
        self.assertEqual("Autogain active=False\n", self._last_write())

    def test_exit(self):
        """exit command"""
        cli = self.create()
        self.assertTrue(cli.onecmd("exit"))
        self.assertEqual("Goodbay\n", self._last_write())

Take care that onecmd() return True if your cli should terminate, False otherwise.

如果你的cli应该终止,请注意onecmd()返回True,否则返回False。

#2


Use python mock library to simulate user input. Here you will find similar problems with examples 1, 2.

使用python模拟库来模拟用户输入。在这里,您将发现与示例1,2类似的问题。

#1


You can mock input or input stream passed to cmd to inject user input but I find more simple and flexible test it by onecmd() Cmd API method and trust how Cmd read input. In this way you cannot care how Cmd do the dirty work and test directly by users command: I use cmd both by console and socket and this I cannot care where the stream come from.

您可以模拟传递给cmd的输入或输入流以注入用户输入,但我发现通过onecmd()Cmd API方法更简单灵活地测试它并信任Cmd如何读取输入。通过这种方式,您无法关心Cmd如何通过用户命令直接执行脏工作和测试:我通过控制台和套接字使用cmd,这我不关心流来自何处。

Moreover I use onecmd() to test even do_* (and occasionally help_*) methods and make my test less coupled to the code.

此外,我使用onecmd()来测试甚至do_ *(偶尔帮助*)方法,并使我的测试更少地耦合到代码。

Follow a simple example of how I use it. create() and _last_write() are helper methods to build a MyCLI instance and take the last output lines respectively.

按照我如何使用它的简单示例。 create()和_last_write()是帮助方法,用于构建MyCLI实例并分别获取最后一行输出。

from mymodule import MyCLI
from unittest.mock import create_autospec

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

    def create(self, server=None):
        return MyCLI(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_active(self):
        """Tesing `active` command"""
        cli = self.create()
        self.assertFalse(cli.onecmd("active"))
        self.assertTrue(self.mock_stdout.flush.called)
        self.assertEqual("Autogain active=False\n", self._last_write())
        self.mock_stdout.reset_mock()
        self.assertFalse(cli.onecmd("active TRue"))
        self.assertTrue(self.mock_stdout.flush.called)
        self.assertEqual("Autogain active=True\n", self._last_write())
        self.assertFalse(cli.onecmd("active 0"))
        self.assertTrue(self.mock_stdout.flush.called)
        self.assertEqual("Autogain active=False\n", self._last_write())

    def test_exit(self):
        """exit command"""
        cli = self.create()
        self.assertTrue(cli.onecmd("exit"))
        self.assertEqual("Goodbay\n", self._last_write())

Take care that onecmd() return True if your cli should terminate, False otherwise.

如果你的cli应该终止,请注意onecmd()返回True,否则返回False。

#2


Use python mock library to simulate user input. Here you will find similar problems with examples 1, 2.

使用python模拟库来模拟用户输入。在这里,您将发现与示例1,2类似的问题。