Python unit testing code which calls OS/Module level python functions

python, python-2.7, python-unittest, unit-testing

Solution

The short answer is to monkey patch these system calls.

There are some good examples in the answer to How to display the redirected stdin in Python?

Here is a simple example for `raw_input()` using a `lambda` that throws away the prompt and returns what we want.

System Under Test

$ cat ./name_getter.py
#!/usr/bin/env python

class NameGetter(object):

    def get_name(self):
        self.name = raw_input('What is your name? ')

    def greet(self):
        print 'Hello, ', self.name, '!'

    def run(self):
        self.get_name()
        self.greet()

if __name__ == '__main__':
    ng = NameGetter()
    ng.run()

$ echo Derek | ./name_getter.py 
What is your name? Hello,  Derek !

Test case:

$ cat ./t_name_getter.py
#!/usr/bin/env python

import unittest
import name_getter

class TestNameGetter(unittest.TestCase):

    def test_get_alice(self):
        name_getter.raw_input = lambda _: 'Alice'
        ng = name_getter.NameGetter()
        ng.get_name()
        self.assertEquals(ng.name, 'Alice')

    def test_get_bob(self):
        name_getter.raw_input = lambda _: 'Bob'
        ng = name_getter.NameGetter()
        ng.get_name()
        self.assertEquals(ng.name, 'Bob')

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

$ ./t_name_getter.py -v
test_get_alice (__main__.TestNameGetter) ... ok
test_get_bob (__main__.TestNameGetter) ... ok

----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK

Problem

I have a python module/script which does a few of these - At various nested levels inside the script I take command line inputs, validate them, apply sensible defaults - I also check if a few directories exist The above are just two examples. I am trying to find out what is the best "strategy" to test this. What I have done is that I have constructed wrapper functions around `raw_input` and `os.path.exists` in my module and then in my test I override these two functions to take input from my array list or do some mocked behaviour. This approach has the following disadvantages - Wrapper functions just exist for the sake of testing and this pollutes the code - I have to remember to use the wrapper function in the code everytime and not just call `os.path.exists` or `raw_input` Any brilliant suggestions?

Original source

Related problems