python modules in help

unittest

why unit test ?

unit test is to test the components of a program automatically. a few good reasons of unit tests:

  • test driving practice
  • early sanity checking
  • for regression test, that the new changes won’t break early work
  • unit test is to test the isolated piece of code, usually white box test written by developer; functional test is to test the function requirements, usually black box test written by testers.

the testcase output can be OK, FAIL, ERROR.

assertion functions

  • basic boolean asserts:

    assertEqual(arg1, arg2, msg=None)
    assertIsNot(arg1, arg2, msg=None)
    
  • comparative asserts:

    assertAlmostEqual(first, second, places=7, msg=None, delta=None)
    
  • asserts for collections:

    assertListEqual(list1, list2, msg=None)
    assertTupleEqual(tuple1, tuple2, msg=None)
    assertSetEqual(set1, set2, msg=None)
    assertDictEqual(dic1, dic2, msg=None)    
    

    command line interface

  • run all unittests

    python3 -m unittest discover -v -c
    
  • run single test module

    python3 -m unittest -v -c tests/test_XXXX.py
    
  • run individual test case

    python3 -m unittest -v tests.test_XXX.TestCaseXXX.test_XXX
    

how to write a unittest

unittest.TestCase

any user defined test class should first derived from unittest.TestCase

setUp() & tearDown()

setUp() provides the way to set up things before running any method starting with test_xx() in user defined test class. tearDown() is where to end the setting ups

depends on other modules/class

usually the class/methods we try to test have depends on other modules/class, but as unit test want to isolate these depends, so either mock the other methods or fake data. in general there are three types of depends:

pure variable

this is the most simple case, then you can directly define the same pure type variable in your test_xx() method

methods in other modules

here need to use patch, to mock the method and define its return value is your test_xx() method, then in the class where call the real method will be replace by this mocked one.

methods in other class

here need to use mock, either MagicMock or Mock will works.

mock & patch

mock module is built in unittest.mock after a later Python version, if not, mock module can be installed by pip install mock, then directly import mock in code.

the philosophy of unit test is to isolate classes test, but in reality most classes have some instances of other classes, so to test the ego class isolated from other classes, that’s where mock helps.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from mock import MagicMock, patch
class TestScenario(TestCase):
def test_mock(self):
sim.method = MagicMock(return_value="xx")
agent = sim.method()
self.assertEqual(agent, "xx")
@patch("module/add_agent")
def test_patch(self, mock_add_agent):
mock_add_agent.return_value = "xx"
agent = module.add_agent()
self.assertEqual(agent, "xx")

Mock helps to mock the method in a class, while patch helps to mock a global method in a module.

automatical unittest generator

there are projects assit to generate unittest code automatically, e.g auger

test with while Loop

TODO

test with raise Error

TODO

coverage.py

  • (one time only) install coverage.py

    pip3 install –user coverage

  • run all tests with coverage

    ~/.local/bin/coverage run -m unittest discover

  • generate html report

    ~/.local/bin/coverage html –omit “~/.local/“,”tests/

ps, output is in htmlcov/index.html

logging

logging() supports more detail message type(info, warn, error, debug) than print(), and the message format is more strict with \%. while print() works both formatted message and message as string simply:

1
2
print("1+1 = ", num)
print("1+1 = %d", % num)

the following is a general log class, which can plug in any existing Python project:


import logging 
import os 

class log(object):
    def __init__(self, logger_name):
        self.log = logging.getLogger(logger_name)
        self.log.setLevel(logging.DEBUG)
        console_handler = logging.StreamHandler()
        console_handler.setLevel(logging.DEBUG)
        self.log.addHandler(console_handler)

    def set_output_file(self, filename):
        file_handler = logging.FileHandler(filename)
        file_handler.setLevel(logging.INFO)
        formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
        file_handler.setFormatter(formatter)
        self.log.addHandler(file_handler)

def main():
    test = log('scenario-mm')
    file_name = "record.log" 
    dir_name = "/home/python_test/"
    try:
        os.makedirs(dir_name)
    except OSError:
        pass 
    file_path =  os.path.join(dir_name, file_name)
    test.set_output_file(file_path)
    test.log.debug("debug in episode 1...")
    test.log.info("info ...")
    test.log.warning("warn ...")
    test2 = log("zjjj")
    test2.set_output_file('record.log')
    test2.log.info("test2 info")
    test2.log.warning("test2 warn")


if __name__ == "__main__":
    main()

pygame

refer

python automatic test series

auger: automated Unittest generation for Python

unittest official

unittest in lg-sim project

coverage.py official

unittest + mock + tox