Unittesting Python code that uses subprocess.Popen
I have a Python project in which I read external files, process them and write the results to a new file. Input files can be read directly or checked out from the git repository using git show
. The function to call git show
and return stdout looks like this:
def git_show(fname, rev):
'''Runs git show and returns stdout'''
process = subprocess.Popen(['git', 'show', '{}:{}'.format(rev, fname)],
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = process.communicate()
ret_code = process.wait()
if ret_code:
raise Exception(stderr)
return stdout
I have unittests that test all part of the program's processing, that is, everything except reading and writing files. However, I stumbled upon (and fixed) issues regarding encoding the returned string from git_show()
, depending on the Python version and quite possibly the OS and the actual file to read.
I would like to set up unittest git_show()
so that I can make sure the entire application is working, from input to output. However, as far as I know, this is not possible without actually testing the git repository. The whole package is version controlled with git, and I expect if I have a git repository inside a git repository that could lead to problems on its own and a voice in my head tells me that there might not be the best solution anyway.
What is the best way to get the unittesting code that receives input from git show
(and generally, on the command line / Popen.communicate()
)?
source to share
Perhaps you want (one of a combination of) different kinds of tests .
Unit tests
Test a small piece of code in code.
- mock out
subprocess.Popen
- return static values ββto
stdout, stderr
- check the processing is correct.
The sample code is pretty small, you can only check what is stdout
actually returned and what if nonzero throws wait()
an exception.
Something in between
The vectors to be tested, which are given an input signal, the given output must be created
- mock out git, instead use
cat vector1.txt
coded in a specific way - test result
Integration tests
Check how your code connects to external objects, in this case git
. Such tests protect you from accidentally changing the expectation of the internal system. That is, it "hangs" on the API.
- create a tarball with a small git repository
- optionally package git binary into the same tarball
- unpack tarball
- execute git command
- compare the result with the expected
source to share
So I do it with pytest
Example: (contrived)
from subprocess import Popen, PIPE
def test():
p = Popen(["echo", "Hello World!"], stdout=PIPE)
stdout, _ = p.communicate()
assert stdout == b"Hello World!\n"
Output:
$ py.test -x -s test_subprocess.py
======================================= test session starts ========================================
platform linux2 -- Python 2.7.9 -- py-1.4.28 -- pytest-2.7.1
rootdir: /home/prologic/work/circuits, inifile:
plugins: cov
collected 1 items
test_subprocess.py .
===================================== 1 passed in 0.01 seconds =====================================
Or using the unittest standard library :
Example:
#!/usr/bin/env python
from unittest import main, TestCase
from subprocess import Popen, PIPE
class TestProcess(TestCase):
def test(self):
p = Popen(["echo", "Hello World!"], stdout=PIPE)
stdout, _ = p.communicate()
self.assertEquals(stdout, b"Hello World!\n")
if __name__ == "__main__":
main()
Output:
$ python test_subprocess.py
.
----------------------------------------------------------------------
Ran 1 test in 0.001s
OK
source to share