An article to understand unittest unit testing framework

Posted by datoshway on Thu, 24 Feb 2022 08:20:05 +0100

For Python 2.1 and later versions, unittest is put into the python development package as a standard module.

01 use unittest to write test cases [at the end of the paper, a learning resource for automated testing is prepared for free sharing]

Rules:

  • import unittest
  • To create a test class, you must inherit unittest Testcase class
  • Create a test method that starts with "test"
from calculator import Calculator
    import unittest
     
    class TestAdd(unittest.TestCase):
        def test_add(self):
            c =Calculator(3,5)
            result = c.add()
            self.assertEqual(result,8)
     
    if __name__ =='__main__':
        unittest.main() //Execute the test case through the main() method; Execute the use cases in the order of the names and ASCII values of the test classes and methods

Execution result of unittest:

  • “.” Indicates that the test case has passed the execution
  • "F" indicates execution failure
  • "E" indicates an execution error
  • "s" means run skip

02 three important concepts

  • Test Case is the smallest test unit, i.e. test method. unittest provides a TestCase base class. The test class we create should inherit this base class, which can be used to create new test cases.
  • Test Suite is a collection of test cases, test suites, or both, used to assemble a set of tests to run. Use the TestSuite class to create a Test Suite.
  • Test Runner test runner is a component used to coordinate the execution of tests and provide results to users. unittest provides the TextTestRunner class to run test cases.

03 test case execution sequence

unittest loads test cases (including test directories, test files, test classes, and test methods) in the order of ASCII code by default, that is, it is not executed from top to bottom in the order in which test cases are created. The order of execution of the discover() and main() methods is the same. Therefore, if you want a test file to be executed first, you can control the naming.

How to control the execution sequence of test cases?

  • You can load test cases in a certain order through the addTest() method of TestSuite class, so that the cases you want to be executed first can be loaded first.
  from calculator import Calculator
    import unittest
     
    class TestAdd(unittest.TestCase):
        def test_add(self):
            c =Calculator(3,5)
            result = c.add()
            self.assertEqual(result,8)
     
        def test_add_decimals(self):
            c=Calculator(3.5,5.5)
            result=c.add()
            self.assertEqual(result,9)
     
    class TestSub(unittest.TestCase):
        def test_sub(self):
            c =Calculator(5,1)
            result = c.sub()
            self.assertEqual(result,4)
     
    if __name__ =='__main__':
        #Create test suite
        suit = unittest.TestSuite()
        suit.addTest(TestSub("test_sub")) //Add test case
        suit.addTest(TestAdd("test_add_decimals"))
     
        #Create test runner
        runner = unittest.TextTestRunner()
        runner.run(suit)

04 execute multiple test cases

unittest. defaultTestLoader. The discover () method can find test cases from multiple files.

This class loads test cases according to various standards and returns them to the test suite

discover(start_dir, pattern='Test*.py', top_level_dir=None)

  • start_dir: module name to be tested / test case directory; The discover() method will automatically find the test case file based on this parameter
  • pattern: matching principle of test case file name
  • top_level_dir: the top-level directory of the test module. If there is no top-level directory, it defaults to None
import unittest
    test_dir='Test' //File directory
     
    suits =unittest.defaultTestLoader.discover(test_dir, pattern='Test*.py')
     
    if __name__=='__main__':
        runner = unittest.TextTestRunner()
        runner.run(suits)

If you want discover() to find the test files in the subdirectory, you have to mark the subdirectory as a python module (the _init_. Py file under the subdirectory)

05 skip test and expected failure

import  unittest
    class MyTest(unittest.TestCase):
     
        @unittest.skip("Skip this use case")
        def test_skip(self):
            print('aaa')
     
        @unittest.skipIf(3>2,"Skip test when condition is true")
        def test_skip_if(self):
            print('bbb')
     
        @unittest.skipUnless(3>2,"Skip test when condition is false")
        def test_skip_unless(self):
            print('ccc')
     
    // Mark the test as failed regardless of the execution result
        @unittest.expectedFailure 
        def test_fail(self):
            print('ddd')
     
    if __name__=="__main__":
        unittest.main()

Execution result:

E:\selenium> & "D:/Program Files/Python39/python.exe" e:/selenium/Test/unittest/skip_fail.py
    ddd
    ussccc
    .
    ----------------------------------------------------------------------
    Ran 4 tests in 0.001s
     
    FAILED (skipped=2, unexpected successes=1)

The above four decorators are also applicable to the test class.

06Setup and Teardown

  • setUpModule/tearDownModule is executed at the beginning and end of the whole module
  • setUpClass/ tearDownClass is executed at the beginning and end of the test class
  • setUp/tearDown is executed at the beginning and end of the test case
import unittest
    def setUpModule():
        print(" Module start .....")
     
    def tearDownModule():
        print(" Module end ...")
     
    class MyTest(unittest.TestCase):
     
        @classmethod
        def setUpClass(cls):
            print("Class Start...")
        
        @classmethod
        def tearDownClass(cls):
            print("Class end...")
     
        def setUp(self):
            print("test case start ...")
     
        def tearDown(self):
            print("test case end ...")
     
        def testcase1(self):
            print("this is first test case")
        
        def testcase2(self):
            print("this is second test case")
     
    if __name__ == "__main__":
        unittest.main()

The results are as follows:

E:\selenium> & "D:/Program Files/Python39/python.exe" e:/selenium/Test/unittest/setup.py
    Module start .....
    Class Start...
    test case start ...
    this is first test case
    test case end ...
    .test case start ...
    this is second test case
    test case end ...
    .Class end...
     Module end ...
     
 ----------------------------------------------------------------------
    Ran 2 tests in 0.002s
     
    OK

07Web automated testing

 import unittest
    from selenium import webdriver
    from time import sleep
     
    class Baidu(unittest.TestCase):
     
        @classmethod
        def setUpClass(cls):
            cls.driver = webdriver.Chrome()
            cls.driver.implicitly_wait(5)
            cls.driver.get("http://www.baidu.com")
            cls.driver.maximize_window()
        @classmethod
        def tearDownClass(cls):
            cls.driver.quit()
     
        def search(self,text):
            self.driver.find_element_by_id("kw").clear()
        self.driver.find_element_by_id("kw").send_keys(text)
            self.driver.find_element_by_id("su").click()
            sleep(3)
     
        def test_search_selenium(self):
            search_key="selenium"
            self.search(search_key)
            self.assertEqual(self.driver.title,search_key+"_Baidu search")
     
        def test_search_python(self):
            search_key="python"
            self.search(search_key)
            self.assertEqual(self.driver.title,search_key+"_Baidu search")
     
    if __name__ =="__main__":
        unittest.main()

08Parameterized

Parameterized is a parameterized Library of python, and supports unittest and pytest unit test frameworks at the same time.

  • pip install parameterized
  • parameterized.expand() loads data, and each tuple in the list is a test case
import unittest
    from time import sleep
    from selenium import webdriver
    from parameterized import parameterized
     
    class TestBaidu(unittest.TestCase):
        @classmethod
        def setUpClass(cls):
            cls.driver=webdriver.Chrome()
            cls.driver.get("http://www.baidu.com")
            cls.driver.maximize_window
     
        @classmethod
        def tearDownClass(cls):
            cls.driver.quit()
     
        def search(self,search_key):
            self.driver.find_element_by_id("kw").clear()
            self.driver.find_element_by_id("kw").send_keys(search_key)
            self.driver.find_element_by_id("su").click()
            sleep(5)
     
        @parameterized.expand(
            [
                ("case1","selenium"),
                ("case2","python")
            ]
        )
        def test_search(self,name,search_key):
            self.search(search_key)
            self.assertEqual(self.driver.title,search_key+"_Baidu search")
     
    if __name__ == '__main__':  
        unittest.main(verbosity=2)  

09DDT

Data driven tests is an extension library designed for unittest unit testing framework.

Installation:

pip install ddt

Import:

from ddt import ddt, data, file_data,unpack

Usage rules:

  • The test class must be decorated with @ ddt
  • There are different forms of tuples and parameterized data, such as dictionaries at the bottom
  • @Data (data list / tuple / dictionary. The key of the dictionary is consistent with the name of the formal parameter in the method)
  • @unpack decoration test method
import unittest
    from time import sleep
    from selenium import webdriver
    from ddt import ddt,data,file_data,unpack
     
    @ddt
    class TestBaidu(unittest.TestCase):
        @classmethod
        def setUpClass(cls):
            cls.driver=webdriver.Chrome()
            cls.driver.get("http://www.baidu.com")
            cls.driver.maximize_window
     
        @classmethod
        def tearDownClass(cls):
            cls.driver.quit()
     
        def search(self,search_key):
            self.driver.find_element_by_id("kw").clear()
            self.driver.find_element_by_id("kw").send_keys(search_key)
            self.driver.find_element_by_id("su").click()
            sleep(5)
     
        @data(
                ("case1","selenium"),
                ("case2","python")
        )
        @unpack
        def test_search1(self,name,search_key):
            print("The first set of test cases:",name)
            self.search(search_key)
            self.assertEqual(self.driver.title,search_key+"_Baidu search")
     
        @data(
                ["case1","selenium"],
                ["case2","python"]
        )
        @unpack
        def test_search2(self,name,search_key):
            print("The second set of test cases:",name)
            self.search(search_key)
            self.assertEqual(self.driver.title,search_key+"_Baidu search")
     
        @data(
                {"key":"selenium"},
                {"key":"python"}
        )
        @unpack
        def test_search3(self,key):
            print("The third group of test cases:",key)
            self.search(key)
            self.assertEqual(self.driver.title,key+"_Baidu search")
     
    if __name__ == '__main__':  
        unittest.main(verbosity=2)  

When an error is reported as follows, the file name is also ddt:

PS E:\selenium> & "D:/Program Files/Python39/python.exe" e:/selenium/Test/unittest/ddt.py
    Traceback (most recent call last):
      File "e:\selenium\Test\unittest\ddt.py", line 4, in <module>
        from ddt import ddt,data,file_data,unpack
      File "e:\selenium\Test\unittest\ddt.py", line 4, in <module>
        from ddt import ddt,data,file_data,unpack
   
 ImportError: cannot import name 'ddt' from partially initialized module
 'ddt' (most likely due to a circular import) 
(e:\selenium\Test\unittest\ddt.py)    

10 parameterization of data files

@file_ The content in the data () decorator is the file name. Support json format and yaml format.

import unittest
    from time import sleep
    from selenium import webdriver
    from ddt import ddt,data,file_data,unpack
     
    @ddt
    class TestBaidu(unittest.TestCase):
        @classmethod
        def setUpClass(cls):
            cls.driver=webdriver.Chrome()
            cls.driver.get("http://www.baidu.com")
            cls.driver.maximize_window
     
        @classmethod
        def tearDownClass(cls):
            cls.driver.quit()
     
        def search(self,search_key):
            self.driver.find_element_by_id("kw").clear()
            self.driver.find_element_by_id("kw").send_keys(search_key)
            self.driver.find_element_by_id("su").click()
            sleep(5)
     
        @file_data('ddt_data.json')
        @unpack
        def test_search1(self,search_key):
            print("The first set of test cases:",search_key)
            self.search(search_key)
            self.assertEqual(self.driver.title,search_key+"_Baidu search")
     
        @file_data('ddt_data_yml.yaml')
        @unpack
        def test_search2(self,case):
            search_key=case[0]["search_key"]
            print("The second set of test cases:",search_key)
            self.search(search_key)
            self.assertEqual(self.driver.title,search_key+"_Baidu search")
    if __name__ == '__main__':  
        unittest.main(verbosity=2)  
ddt_data.json
    {
        "case1": {"search_key": "python"},
        "case2": {"search_key": "ddt"}
    }
ddt_data_yml.yaml
    case1:
     - search_key: "python"
    case2:
     - search_key: "ddt" 

The following is the supporting materials. For friends who do [software testing], it should be the most comprehensive and complete war preparation warehouse. This warehouse has also accompanied me through the most difficult journey. I hope it can also help you!

Finally, it can be in the official account: programmer Hao! Get a 216 page interview document of Software Test Engineer for free. And the corresponding video learning tutorials for free!, It includes basic knowledge, Linux essentials, Shell, Internet program principles, Mysql database, special topics of packet capture tools, interface test tools, test advanced Python programming, Web automation test, APP automation test, interface automation test, advanced continuous integration of test, test architecture, development test framework, performance test, security test, etc.

If my blog is helpful to you and you like my blog content, please click "like", "comment" and "collect" for three times! Friends who like software testing can join our testing technology exchange group: 779450660 (there are various software testing resources and technical discussions)

Topics: Python unit testing software testing unittest