Skip to content

Instantly share code, notes, and snippets.

@benburry
Last active October 7, 2019 05:05
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save benburry/8905974 to your computer and use it in GitHub Desktop.
Save benburry/8905974 to your computer and use it in GitHub Desktop.
Python 2 - mock & unittest example for Popen
from subprocess import Popen, PIPE
def shell_out(command):
return Popen(command.split(' '), stdout=PIPE,stderr=PIPE).communicate()[0].strip('\n').split('\n')
def main():
return shell_out('echo one\ntwo\nthree\n')
if __name__ == '__main__':
print main()
import unittest
from echo import main
from mock import patch, Mock
NEW_OUTPUT = """
eggs
cheese
shop
"""
# Mocking Popen directly - need to construct a Mock to return, and adjust its communicate() return_value
# The benefit of this approach is in not needing to do the strip/split on your fake return string
class MockPopen(unittest.TestCase):
# run before each test - the mock_popen will be available and in the right state in every test<something> function
def setUp(self):
# The "where to patch" bit is important - http://www.voidspace.org.uk/python/mock/patch.html#where-to-patch
self.popen_patcher = patch('echo.Popen')
self.mock_popen = self.popen_patcher.start()
self.mock_rv = Mock()
# communicate() returns [STDOUT, STDERR]
self.mock_rv.communicate.return_value = [NEW_OUTPUT, None]
self.mock_popen.return_value = self.mock_rv
# run after each test
def tearDown(self):
self.popen_patcher.stop()
def testPopen(self):
output = main()
self.assertTrue(self.mock_popen.called)
self.assertTrue('eggs' in output)
self.assertEqual(3, len(output))
def testMoon(self):
self.mock_rv.communicate.return_value[0] += 'moonlanguage'
output = main()
self.assertTrue(self.mock_popen.called)
self.assertTrue('moonlanguage' in output)
self.assertEqual(4, len(output))
# I'd use this approach instead.
# Much easier (although you do need to mimic what shell_out is doing and convert your fake string into an array)
class MockShellOut(unittest.TestCase):
def setUp(self):
# The "where to patch" bit is important - http://www.voidspace.org.uk/python/mock/patch.html#where-to-patch
self.shellout_patcher = patch('echo.shell_out')
self.mock_shellout = self.shellout_patcher.start()
# convert the fake return value into an array by calling split()
self.mock_shellout.return_value = NEW_OUTPUT.strip('\n').split('\n')
def tearDown(self):
self.shellout_patcher.stop()
def testShellOut(self):
output = main()
self.assertTrue(self.mock_shellout.called)
self.assertTrue('eggs' in output)
self.assertEqual(3, len(output))
def testMoon(self):
self.mock_shellout.return_value += ['moonlanguage']
output = main()
self.assertTrue(self.mock_shellout.called)
self.assertTrue('moonlanguage' in output)
self.assertEqual(4, len(output))
if __name__ == '__main__':
# the unittest runner looks for any functions called test<something> in classes that extend TestCase
# so you can add as many test<something> functions to the classes above as you like, and they'll all get run
unittest.main()
@proofit404
Copy link

Thanks, good tip.

@AlexanderRossa
Copy link

This has been quite useful for gaining more understanding of the mocking process. Thanks for that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment