Search code examples
pythonunit-testingstatic-methodsassertprocedure

Python: how to create a positive test for procedures?


I have a class with some @staticmethod's that are procedures, thus they do not return anything / their return type is None.

If they fail during their execution, they throw an Exception.

I want to unittest this class, but I am struggling with designing positive tests.

For negative tests this task is easy:

assertRaises(ValueError, my_static_method(*args))
assertRaises(MyCustomException, my_static_method(*args))

...but how do I create positive tests? Should I redesign my procedures to always return True after execution, so that I can use assertTrue on them?


Solution

  • Without seeing the actual code it is hard to guess, however I will make some assumptions:

    1. The logic in the static methods is deterministic.
    2. After doing some calculation on the input value there is a result and some operation is done with this result.
    3. python3.4 (mock has evolved and moved over the last few versions)

    In order to test code one has to check that at least in the end it produces the expected results. If there is no return value then the result is usually stored or send somewhere. In this case we can check that the method that stores or sends the result is called with the expected arguments.

    This can be done with the tools available in the mock package that has become part of the unittest package.

    e.g. the following static method in my_package/my_module.py:

    import uuid
    
    class MyClass:
    
        @staticmethod
        def my_procedure(value):
            if isinstance(value, str):
                prefix = 'string'
            else:
                prefix = 'other'
    
            with open('/tmp/%s_%s' % (prefix, uuid.uuid4()), 'w') as f:
                f.write(value)
    

    In the unit test I will check the following:

    1. open has been called.
    2. The expected file name has been calculated.
    3. openhas been called in write mode.
    4. The write() method of the file handle has been called with the expected argument.

    Unittest:

    import unittest
    from unittest.mock import patch
    
    from my_package.my_module import MyClass
    
    
    class MyClassTest(unittest.TestCase):
        @patch('my_package.my_module.open', create=True)
        def test_my_procedure(self, open_mock):
            write_mock = open_mock.return_value.write
            MyClass.my_procedure('test')
            self.assertTrue(open_mock.call_count, 1)
            file_name, mode = open_mock.call_args[0]
            self.assertTrue(file_name.startswith('/tmp/string_'))
            self.assertEqual(mode, 'w')
            self.assertTrue(write_mock.called_once_with('test'))