A concept,

UnitTest is a module in the Python standard library, which is similar to Junit UnitTest framework in Java. Its module provides many classes and methods to handle various testing work, and can fully combine Selenium, Appium, Request, etc., to achieve UI automation and interface automation.

Before we start, we need to understand a few concepts:

TestCase: a complete test process is a TestCase. All test cases are directly inherited from unittest. TestCase class.TestCase is the smallest test unit and is independent.

Testfixtures: Tests firmware and performs pre-test work, such as cleaning up data, creating temporary databases, directories, and starting certain service processes. When writing test code, there are always some repetitive parts of the code, such as when testing a website login, which are simply divided into three use cases: In all three cases, you need to access the system address first, then enter the account and password, and click Login. After that, the browser closes. We can use setUp() to set the address as a precondition. Close the browser as a post-condition with tearDown(). The test firmware integrates the common parts of the code.

TestSuite: a collection of multiple test cases. A TestSuite, like a test case, can have multiple test cases and can be combined to form more sets of test cases.

–TestRunner: TestRunner that provides an environment for running test cases, executes test cases through the run() method, and outputs the test results when the execution is complete. The TextTextRunner() class of the UnitTest framework, which runs the test cases assembled by Suite through the run() method below the class, as suite test suite.

Ii. UnitTest environment construction

The UnitTest framework is wrapped by default when Python is installed, so you simply need to import UnitTest to call the framework.

Three, test the cat

Import unittest class UnitForTest(unittest.testCase): def setUp(self) -> None: Def tearDown(self) -> None: print('this is tearDown') # def test_1(self): print('this is test1!!! ') def test_3(self): print('this is test3!!! ') def test_2(self): print('this is test2!!! ') if __name__ == '__main__': unittest.main()Copy the code

Running results:

SetUp () is executed once before each individual test case is run, and tearDown() is executed once after each individual test case is run, as shown in the run results above.

Methods similar to setUp() and tearDown() are setUpClass() and tearDownClass(). SetUpClass () is called once before each class execution, and tearDownClass() is called once after each class execution, You must use the @ classMethod decorator when using both methods.

UnitTest syntax rules:

(1) In UnitTest, all use case classes are directly derived from unitTest.TestCase;

(2) In UnitTest, the definitions of test cases all start with test_;

(3) The running order of use cases has nothing to do with the order written in the code, the running order follows a-Z, A-Z, 0-9;

(4) A single test class must run with the unittest.main() method.

TestSuite

Create a new UnitTest class and write a test case named unit_for_testa.py with the following code:

Def setUp(self) -> None: def setUp(self) -> None: Def tearDown(self) -> None: print('this is AtearDown') # def test_1(self): print('this is Atest1!!! ') def test_2(self): print('this is Aest2!!! ') def test_3(self): print('this is Atest3!!! ')Copy the code

Create a new class storage suite named testsuite_demo. py. Run it directly in the UnitTest class and it will not work.

Let’s create a TestSuite(unittest.testsuite ()) and add test cases to the TestSuite using five different methods.

(1) The first way to add a test case — single Add (addTest)

Unittest_demo. unit_for_testA import UnitForTestA # create a test suite Suite. AddTest (UnitForTestA('test_1')) suite. AddTest (UnitForTestA('test_2')) Suite. AddTest (UnitForTestA('test_3')) # run with RuunnerCopy the code

(2) the second way to add test cases — addTests in batches

Unittest_demo. unit_for_testA import UnitForTestA # create a test suite Unittest.testsuite () # add test cases = [UnitForTestA('test_1'), UnitForTestA('test_2'), UnitForTestA('test_3')] suite. AddTests (cases) #Copy the code

(3) add test cases of the third method, according to the path of the file name matches (defaultTestLoader. Discover)

If the file name meets the filter criteria, all test cases in the specified directory will be executed. To see the effect, we will create a new UnitTest class and write the test cases in the file named unit_for_testb.py. The code is as follows:

Def setUp(self) -> None: def setUp(self) -> None: Def tearDown(self) -> None: print('this is BtearDown') # def test_1(self): print('this is Btest1!!! ') def test_2(self): print('this is Best2!!! ') def test_3(self): print('this is Btest3!!! ')Copy the code

The directory structure is shown below

In the test suite class (testsuite_demo.py), add the following code according to the file name in the path:

Unittest # create a TestSuite suite = unittest.testsuite () # add test case test_dir = './' # Under the specified path of all files that match the file name of the test cases (bulk) discover = unittest. DefaultTestLoader. Discover (start_dir = test_dir, Runner = unittest.texttestrunner () runner. Run (discover)Copy the code

The result is shown in the figure below, where the use cases in both files in the current directory are executed.

Start_dir: path of the file to be executed.

Py matches all Python files starting with unit_for_test.

TestLoader().loadTestSFromTestCase ()

Unittest_demo. unit_for_testA import UnitForTestA # create a test suite Unittest.testsuite () # Suite. AddTests (unittest.testLoader ().loadTestSFromTestCase (UnitForTestA)) # Runner = unittest.textTestrunner () runner. Run (suite)Copy the code

TestLoader().loadTestsFromName().loadTestSFromName ()

Unittest # create a TestSuite = unittest.testsuite () # add a fifth method of test cases Suite. AddTests (unittest.testloader ().loadTestsFromName(' unit_for_testa.unitfortesta ')) # unittest.TextTestRunner() runner.run(suite)Copy the code

Skip the use cases

Not every use case must run once when executing a use case. Unittest provides methods for skipping use cases and adding decorators before use cases.

(1) @unittest.skip: this use case is directly skipped;

(2) @unittest.skipUnless(1 >2, ‘1>2 is false ‘) : When the condition is false, skip the use case and do not execute;

(3) @unittest.skipIf(1 <2, ‘1<2 is true ‘) : Skip the use case if the condition is true.

Vi. Data-driven DDT (Data Driven Tests)

Before we get into data-driven, let’s take a look at the following example. The content of the code is extremely simple to simulate the user name and password for an automated test (haha, I know this simulation is a bit strained!!) :

1. Open Baidu;

2. Enter Java in the input box for the first time, clear it, and then enter 123456 to close the browser.

3, Enter Selenium in the input box again, and then clear it. Enter abcdef to close the browser.

import unittest
from selenium import webdriver
import time


class UnitForTestddt(unittest.TestCase):

    def setUp(self) -> None:
        self.driver = webdriver.Chrome()
        self.driver.get('http://www.baidu.com')

    def test_ddt1(self):
        self.driver.find_element_by_id('kw').send_keys('java')
        time.sleep(1)
        self.driver.find_element_by_id('kw').clear()
        time.sleep(1)
        self.driver.find_element_by_id('kw').send_keys('123456')
        time.sleep(1)

    def test_ddt2(self):
        self.driver.find_element_by_id('kw').send_keys('selenium')
        time.sleep(1)
        self.driver.find_element_by_id('kw').clear()
        time.sleep(1)
        self.driver.find_element_by_id('kw').send_keys('abcdef')
        time.sleep(1)

    def tearDown(self) -> None:
        self.driver.quit()


if __name__ == '__main__':
    unittest.main()
Copy the code

As can be seen from the code, a lot of code in the middle test case is repeated, resulting in code redundancy. In order to solve the code redundancy, we adopt data-driven approach. At the same time, in order to facilitate the later code maintenance, data and code separation. UnitTest does not have its own data drive function. If you want to use data drive while using UnitTest, DDT can be used to complete it.

The usage method is as follows:

(1) DDT. data: a decorative test method whose parameters are a series of values, such as tuples;

(2) DDT. file_data: decorated test method. The parameter is the file name, and the test data is stored in the parameter file. The file type can be JSON or YAML;

(3) DDT. Unpack: Used when DDT passes complex data structures, often referred to as unpack.

The following are examples of these methods. Before using DDT, we need to import DDT

1. DDT. Data method

import unittest
from selenium import webdriver
import time
import ddt


@ddt.ddt
class UnitForTestddt(unittest.TestCase):

    def setUp(self) -> None:
        self.driver = webdriver.Chrome()
        self.driver.get('http://www.baidu.com')

    @ddt.data('java', 'selenium', 'python')
    def test_ddt(self, a):
        self.driver.find_element_by_id('kw').send_keys(a)
        time.sleep(1)

    def tearDown(self) -> None:
        self.driver.quit()


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

Copy the code

In the above example, the data is passed in the format of a tuple, which contains three elements. The browser has been opened three times, and Java, Selenium, and Python have been input in three times. It is clear that the code has only written one test case, but the actual result has been executed three times, reducing the code redundancy. Different input conditions are implemented to execute the same test case.

In the use of DDC.data, we should pay attention to the following points:

(1) Import DDT;

(2) add a decorator method before the test class: @ddt.ddt;

(3) Add @ddc.data () before the test case and pass in the data;

2. DDT. Unpack method

Let’s look at another example, as follows:

import unittest
from selenium import webdriver
import time
import ddt

@ddt.ddt
class UnitForTestddt(unittest.TestCase):

    def setUp(self) -> None:
        self.driver = webdriver.Chrome()
        self.driver.get('http://www.baidu.com')

    @ddt.data(['java', '123456'], ['python', '666666'])
    @ddt.unpack
    def test_ddt1(self, username, pwd):
        self.driver.find_element_by_id('kw').send_keys(username)
        time.sleep(1)
        self.driver.find_element_by_id('kw').clear()
        time.sleep(1)
        self.driver.find_element_by_id('kw').send_keys(pwd)
        time.sleep(1)

    def tearDown(self) -> None:
        self.driver.quit()

if __name__ == '__main__':
    unittest.main()
Copy the code

When we pass a list of contents, we need to add @ddt.unpack to unpack, otherwise we will run an error. In this example, we pass two lists, and execute the use case twice, passing Java and Python to username and 123456 666666 to PWD.

3. Read data from the file

In actual projects, test data are usually written in files. The following uses the. TXT file as an example to introduce how to read data from the file.

Create a new testdata. TXT file under the current package with the following contents:

java,123456
python,666666
selenium,777777
Copy the code

Now read the data from testData.txt in the test case as follows:

import unittest from selenium import webdriver import time import ddt @ddt.ddt class UnitForTestddt(unittest.TestCase): Def read_file(): file = open('testdata.txt', 'r', encoding='utf-8') li = [] for file.readlines(): def _file(): file = open('testdata.txt', 'r', encoding='utf-8') li = [] for file.readlines(): li.append(line.strip('\n').split(',')) file.close() return li def setUp(self) -> None: Self.driver = webdriver.chrome () self.driver. Get ('http://www.baidu.com') # @ddt.data(*read_file()) @ddt.unpack def test_ddt1(self, username, PWD): @ddT. data(*read_file()) @ddT. unpack def test_ddt1(self, username, PWD): self.driver.find_element_by_id('kw').send_keys(username) time.sleep(1) self.driver.find_element_by_id('kw').clear() time.sleep(1) self.driver.find_element_by_id('kw').send_keys(pwd) time.sleep(1) def tearDown(self) -> None: self.driver.quit() if __name__ == '__main__': unittest.main()Copy the code

The above method of reading TXT files is based on the original processing methods such as open method and Readlines method in Python, which is not fully compatible. However, DDT is directly compatible with reading YAML files.

4. Read data from YAML file (file_data)

PIP install Pyyaml

Create a new testdata.yaml file under the current package. The contents of the file are as follows:

-
  username: java
  pwd: 123456
-
  username: python
  pwd: 666666
-
  username: selenium
  pwd: 777777
Copy the code

Now read the data from testData.yaml in the test case as follows:

import unittest from selenium import webdriver import time import ddt @ddt.ddt class UnitForTestddt(unittest.TestCase): def setUp(self) -> None: Self.driver = webdriver.chrome () self.driver.get('http://www.baidu.com') # def def ddc.file_data ('testdata.yaml') test_ddt(self,**user): self.driver.find_element_by_id('kw').send_keys(user.get('username')) time.sleep(1) self.driver.find_element_by_id('kw').clear() time.sleep(1) self.driver.find_element_by_id('kw').send_keys(user.get('pwd')) time.sleep(1) def tearDown(self) -> None: self.driver.quit() if __name__ == '__main__': unittest.main()Copy the code

UnitTest Generate test report (HTMLTestRunner)

After the test cases are executed in batches, the generated test report is in text format, which is not intuitive. To better display the test report, it is better to generate HTML format. Unittest can not generate HTML report, need to import a third party module: HTMLTestRunner

Environment set up

(1) Download htmltestrunner. py and import it into the Lib folder in Python.

(2) Modify part of the source code

Since HTMLtestrunner. py was developed based on PYTHon2, some changes to its content were required to support a Python3 environment

Import IO # 539 line self.outputBuffer = stringio.stringio () Self.outputbuffer = IO.StringIO() # Elapsed: self.outputBuffer = IO.StringIO() # Elapsed: %s' % (self.stoptime -self.startTime) print(sys. Stderr, '\nTime Elapsed: %s' % (self.stoptime - self.starttime)) # 642 line if not rmap.has_key(CLS) : Ue = e.ecode ('latin-1'); ue = eCopy the code

HTMLTestRunner import HTMLTestRunner

Example code is as follows:

Unittest # import the test class from unittest_demo.unit_for_testA import UnitForTestA # Import the package from generating the report HTMLTestRunner import HTMLTestRunner import OS # create a TestSuite suite = unittest.testsuite () # add the second method of test cases = [UnitForTestA('test_1'), UnitForTestA('test_2'), UnitForTestA('test_3') = report_name = report_title = report_desc = report_path = './report/' report_file = report_path + 'report.html' if not os.path.exists(report_path): os.mkdir(report_path) else: pass with open(report_file, 'wb') as report: suite.addTests(cases) runner = HTMLTestRunner(stream=report, title=report_title, description=report_desc) runner.run(suite)Copy the code

After the command is executed, a report/report. HTML file is generated in the current directory. Open it in a browser and the interface is as follows:

Eight, afterword.

The above is my understanding of UnitTest in Python as a test person, please kindly give me your advice. I still have a lot of knowledge to learn in software testing, I hope we can work together in the future!!