简体   繁体   English

是否可以对使用子进程的代码运行单元测试?

[英]Is it possible to run unit tests on code that uses subprocess?

My Django app has some management commands that start other management commands using subprocess.Popen(['manage.py', '<command>', ...) .我的 Django 应用程序有一些管理命令,这些命令使用 subprocess.Popen subprocess.Popen(['manage.py', '<command>', ...)启动其他管理命令。 They are kept like this mainly to run processes in parallel and prevent one command from interfering with another.它们保持这样主要是为了并行运行进程并防止一个命令干扰另一个命令。

However, when writing unit tests these new processes don't use the test environment and database and therefore fail.但是,在编写单元测试时,这些新流程不使用测试环境和数据库,因此会失败。

For now, I've added a setting that runs call_command() instead of Popen() (see below):现在,我添加了一个运行call_command()而不是 Popen( Popen()的设置(见下文):

        if settings.TESTING:
            self.returncode = call_command(self.command, *params)
        else:
            self.process = subprocess.Popen(['python', 'manage.py', self.command] + params)

            # ... later on:
            self.process.wait()
            self.returncode = self.process.returncode

However the subprocess calls and the logic involved in waiting remains untested.然而,子流程调用和等待所涉及的逻辑仍未测试。 Is it possible to test subprocess calls within the unit tests?是否可以在单元测试中测试子进程调用?

Note: the test database is not an in-memory SQLite - I know that wouldn't work.注意:测试数据库不是内存中的 SQLite - 我知道那行不通。 It's an actual database that is created and destroyed every run.它是一个实际的数据库,每次运行都会创建和销毁。

If you considered it as a unit-test, then you could mock your command calls to return mock data to make sure that command caller works just fine.如果您将其视为单元测试,那么您可以模拟您的命令调用以返回模拟数据,以确保命令调用者工作正常。

And for the coverage for the command code, you could also write another unit-test only for that command by using call_command method which provided by Django.对于命令代码的覆盖率,您还可以使用call_command方法仅为该命令编写另一个单元测试。

Like so:像这样:

class TestYourCommandCaller(TestCase):
    ...

    @mock.patch('subprocess.Popen')
    def test_your_business_which_call_the_command(self, mock_process):
        # Mock your process

        call_your_function_which_create_processes()

        # Assert your process have been called

and another test for your command to also coverage command code:和另一个测试你的命令也覆盖命令代码:

class TestYourCommand(TestCase):
    def test_command_call(self):
        call_command('command_name', *params)

        # Assert the result

Yes, you can do this.是的,你可以这样做。 Currently I have code that runs several programs via subprocess.目前我有通过子进程运行多个程序的代码。 I am using unittest to make sure the functions that do this produce expected output.我正在使用 unittest 来确保执行此操作的函数产生预期的 output。 Maybe some purists would consider this is an improper use of unit tests, but it works for me for automating my tests.也许一些纯粹主义者会认为这是对单元测试的不当使用,但它对我的测试自动化很有用。 A fairly simple example follows:一个相当简单的例子如下:

call_ls.py: call_ls.py:

import subprocess
def call_ls():
    subproc = subprocess.Popen(['ls', 'empty_dir'], 
                                stdout=subprocess.PIPE,
                                stderr=subprocess.STDOUT)
    result = subproc.communicate()
    return result

test_call_ls.py: test_call_ls.py:

import unittest
import call_ls

class TestCallLs(unittest.TestCase):
    def test_call_ls(self):
        test_out = call_ls.call_ls()
        self.assertEqual((b'', None), test_out)

if __name__ == '__main__':
    unittest.main()

The tuple in the assert is for handling what subprocess.communicate() returns.断言中的元组用于处理 subprocess.communicate() 返回的内容。 As you'd expect, the directory 'empty_dir' has nothing in it.如您所料,目录“empty_dir”中没有任何内容。 Running the test gives expected results:运行测试给出了预期的结果:

$ python test_call_ls.py 
.  
---------------------------------------------------------------------- 
Ran 1 test in 0.006s 

OK

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM