Introducing the addModuleCleanup / doModuleCleanups function for unittest

If you look at the official Japanese translation of unittest documentation while studying around python unittest and pytest, Since there were addModuleCleanup and doModuleCleanups functions added from python3.8, I will introduce them. What is a unittest? It might be better to get an overview of unittest in another article rather than reading this article.

TL;DR

You can specify the function to call when an error occurs with the setUpModule function.

unittest review

For example, try running the following code. This is a test to confirm addition and subtraction. To check when the function is being executed

print(sys._getframe().f_code.co_name)The function name is output at.



```python
import sys
import unittest

# TODO(hatsumi)~ I will add the code later ~

def createConnection():
    print(sys._getframe().f_code.co_name)
#    raise Exception

def closeConnection():
    print(sys._getframe().f_code.co_name)

def setUpModule():
    createConnection()

def tearDownModule():
    closeConnection()

class TestOperator(unittest.TestCase):

    @classmethod
    def setUpClass(self):
        print(sys._getframe().f_code.co_name)

    @classmethod
    def tearDownClass(self):
        print(sys._getframe().f_code.co_name)

    def setUp(self):
        print(sys._getframe().f_code.co_name)

    def tearDown(self):
        print(sys._getframe().f_code.co_name)

    def test_add(self):
        print(sys._getframe().f_code.co_name)
        self.assertEqual(2 + 3, 5)

    def test_sub(self):
        print(sys._getframe().f_code.co_name)
        assert 3 - 2 == 1


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

As a result of execution, the following should be output.

createConnection
setUpClass
setUp
test_add
tearDown
.setUp
test_sub
tearDown
.tearDownClass
closeConnection

You can see that setup and teardown are called in the order of module-> class-> method.

Main subject

If an exception occurs in the setUpModule function, the tearDownModule function will not be called. Instead, the function added by the addModuleCleanup function is executed by the doModuleCleanups function.

In the above code, the setUpModule function outputs createConnection and the tearDownModule function outputs closeConnection. If you uncomment the setUpModule function and execute `` `raise Exception```, the tearDownModule function will not be called. It may be good if you manage the processing of database connections and temporary things on the assumption that the tearDownModule function is called.

Implementation

Registration of one function

In order to execute the function, it is necessary to register the function in advance. To do this, use the addModuleCleanup function. Add the following code to the TODO comment part of the above code. Register the print_number function.

def print_number():
    print(sys._getframe().f_code.co_name)

unittest.addModuleCleanup(print_number)

When an Exception occurs as a result of execution, the print_number function is called. The character string is also displayed in createConnection. ..

print_number
createConnection
E
======================================================================
ERROR: setUpModule (__main__)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test_module.py", line 25, in setUpModule
    createConnection()
  File "test_module.py", line 19, in createConnection
    raise Exception
Exception

----------------------------------------------------------------------
Ran 0 tests in 0.001s

Registration of multiple functions

It is also possible to register multiple functions. However, in that case, it will be executed by LIFO. Add the following code to the TODO comment part of the above code.

unittest.addModuleCleanup((lambda : print('a')))
unittest.addModuleCleanup((lambda : print('b')))
unittest.addModuleCleanup((lambda : print('c')))

Execution result

c
b
a
createConnection
E

It is registered in the order of a-> b-> c and output in the order of c-> b-> a.

Execution of registered function

The registered function is executed by the doModuleCleanups function. The sharp one may have noticed that when running in LIFO, it pops from the list. So if you run the doModuleCleanups function first, nothing will be done because the list of functions to run is a shell.

Add the following code to the TODO comment part of the above code.

unittest.addModuleCleanup((lambda : print('a')))
unittest.addModuleCleanup((lambda : print('b')))
unittest.addModuleCleanup((lambda : print('c')))

unittest.case.doModuleCleanups()
print()

Output result

c
b
a

createConnection
E

Exception occurs after one line is freed by `print ()`. The function registered again will not be executed. Also, although it is a doModuleCleanups function, `case``` is omitted in the official documentation, so you should actually call it ```unittest.case.doModuleCleanups ()`. (Error?)

Summary and digression

There should have been no individual articles (as of May 4, 2020), so I created one. Doesn't it feel like this feature isn't available in pytest? I hope you find it useful.

If you are kind, please answer the following questions ← ・ ・ ・

When registering multiple functions, I originally wrote as follows, Since the id is the same, I output lambda for each abc like the article. How can I create an instance individually ...? (Is it one-liner Jamuripo?)

#With this, all 4 are output ...
for i in range(5):
    unittest.addModuleCleanup((lambda : print(i)))

URL

--Please go to the official for detailed explanation → https://docs.python.org/ja/3/library/unittest.html

Recommended Posts

Introducing the addModuleCleanup / doModuleCleanups function for unittest
Introducing the BOT framework Minette for Python
[Python] I tried substituting the function name for the function name
The first GOLD "Function"
About the Unfold function
The image display function of iTerm is convenient for image processing.