申明:本文是基于python3.x及selenium3.x.
unittest,也可以称为PyUnit,类似于JUnit,用于python项目中,可以用来创建全面的测试套件,可以用于单元自动化测试(模块)、接口自动化测试(接口)、功能自动化测试(UI)等等。
官方文档:https://docs.python.org/3.6/library/unittest.html
unittest具备创建测试用例、测试套件、测试夹具的能力,包括的组件如下:
- Test Fixture(测试夹具):通过使用测试夹具,可以定义在单个或者多个测试执行之前的准备工作和测试执行后的清理工作。
- Test Case(测试用例):一个测试用例是在unittest中执行测试的最小单元。它通过unittest提供的assert方法来验证一组特定的操作或输入以后得到的具体响应。unittest提供了一个名为TestCase的基础类,可以用来创建测试用例。
- Test Suite(测试套件):一个测试套件是多个测试用例的集合,是针对被测程序的对应功能和模块创建的一组测试,一个测试套件内的测试将一起执行。
- Test Runner(测试执行器):测试执行器负责测试执行调试并且生成测试结果给用户。测试执行器可以使用图形界面、文本界面或者特定的返回值来展示测试执行结果。
- Test Report(测试报告):测试报告用来展示所有执行用例的成功或者失败状态的汇总,执行失败的测试步骤的预期结果与实际结果,还有整体运行状况及运行时间的汇总。注意,unittest本身是没有相应的内置模块来生成友好的报告,但我们可以借用unittest的扩展库HTMLTestRunner来实现,需要单独下载并放到python安装目录下。
TestCase类
我们可以通过继承TestCase类并且在测试类为每一个测试添加测试方法来创建单个测试或者一组测试。每个测试最重要的任务是调用assert方法来比对结果,如调用assEqual()来检验结果,调用assertTrue()来验证条件,或者调用assertRises()来验证预期的异常。
除了添加测试,我们还要添加测试夹具——setUp()方法和tearDown()方法,用来创建或处置测试用用例所需的任何对象和条件。
下面就边实操边说明:
首先,我们需要先引入unittest模块,然后定义一个继承于TestCase类的子类,如下所示:
import unittest from selenium import webdriver class BaiduSearchTest(unittest.TestCase):
setUp()方法
一个测试用例是从setUp()方法开始执行的,用这个方法在每个测试执行前去执行一些初始化的任务。比如创建浏览器实例,访问URL,加载测试数据和打开日志文件等。此方法没有参数,不返回任何值。当定义一个setUp()方法,测试执行器在每次执行测试方法之前优先执行该方法。在下面的例子里,我将用setUp()方法来创建Chrome实例,并在测试开始执行之前访问到被测程序的主页。代码如下:
import unittest from selenium import webdriver class BaiduSearchTest(unittest.TestCase): def setUp(self): #create a new Chrome session self.driver = webdriver.Chrome() self.driver.implicitly_wait(30) self.driver.maximize_window() self.driver.get('https://www.baidu.com')
编写用例
有了setUp()方法,现在可以写一些测试用例来验证被测程序的功能。与setUp()方法相似,test方法也是在TestCase类中实现。重点是测试方法的命名必须以test开头,这种命名约定也通知test runner哪个方法代表测试方法。对于test runner能找到的每个测试方法,都会在执行测试方法之前先执行setUp()方法。这样做有助于确保每个测试方法能够依赖相同的环境,无论类中有多少测试方法。下面将添加一个新测试方法test_search_python(),并使用简单的assertEqual()方法来验证搜索术语返回的结果和预期结果相匹配。代码如下:
import unittest from selenium import webdriver from time import sleep class BaiduSearchTest(unittest.TestCase): def setUp(self): #create a new Chrome session self.driver = webdriver.Chrome() self.driver.implicitly_wait(30) self.driver.maximize_window() self.driver.get('https://www.baidu.com') def test_search_python(self): #get the search textbox search_textbox = self.driver.find_element_by_id('kw') search_textbox.clear() #enter search keyword search_textbox.send_keys("python") #get the and seacrh button and click search_button = self.driver.find_element_by_id('su') search_button.click() #add assert sleep(2) tag = self.driver.find_element_by_link_text("PyPI").text self.assertEqual('PyPI',tag)
tearDown()方法
类似于setUp()方法在每个测试方法之前被调用,TestCase类也会在测试执行完成后调用tearDown()方法来清理所有的初始化值。一旦测试被执行,在setUp()中定义的值将不再需要,所以最好的在完成的时候清理掉。在我的例子中,在测试执行完成后,就不再需要Chrome的实例。我将在tearDown()方法中关闭浏览器,代码接上面的如下:
... def tearDown(self): #close the browser window self.driver.quit()
运行测试
为了通过命令行运行测试,我们可以在测试用例中添加调用简单的unittest.main()方法,并将传递verbosity参数以便在控制台显示详细的测试情况。代码接上面的如下:
... if __name__ == '__main__': unittest.main(verbosity=2)
然后将整个测试代码保存为python脚本,在这个例子里,保存为baidusearch.py。在脚本存放路径下用命令行窗口通过下面的命令在执行该测试。在测试运行结束后,unittest会把测试结果显示在控制台,如下所示:
除了测试结果概要外,当一个测试用例执行失败,针对每个失败也会通过文本信息展示具体哪里有错误。如下所示:
上图展示了具体哪个测试方法执行失败,通过打印信息可以定位具体导致失败的代码。另外,失败自身也会以AssertionError形式显示,上面例子为预期结果与实际结果不匹配。
添加其他测试
下面将为测试类添加其它的测试。规则很简单,新的测试方法命名也要以test开头,代码如下:
... def test_search_selenium(self): search_textbox = self.driver.find_element_by_id('kw') search_textbox.clear() search_textbox.send_keys("python") search_button = self.driver.find_element_by_id('su') search_button.click() sleep(2) tag = self.driver.find_element_by_link_text("Selenium Remote Control").text self.assertIn('Selenium',tag)
运行整个测试类将看到两个Chrome的实例将打开和关闭,这正是setUp()方法和tearDown()方法针对每个测试方法都要执行产生的结果,如下图所示:
更多基于python的unittest信息可以参考:https://docs.python.org/3.6/library/unittest.html