在第一节中我们知道如何使用元素定位,我们可以依靠该方法进行编写一些脚本对网页进行测试。但这对于工作中要用到的还只是九牛一毛,为此我们需要掌握自动化测试的核心——单元测试。说起来每一种编程语言都有自己的单元测试框架,比如说java中的junit。
一. Unittest主要结构
1.四个基本概念
Fixture:执行单个或多个测试用例时的准备工作和执行结束后的一些相关清理工作,他为用户准备了一些方法。
Case:是一个独立的测试单元,也可把它看作成一条测试用例,它继承于Unittest提供的基类TestCase从而共享Fixture中的内容。
Suite: 是一组测试用例的集合,通过使用该方法使用户可以选择性的执行测试用例,在测试过程的不同阶段,并不是每次都要执行全部的测试用例。
Runner:是一个统筹测试执行并生成执行结果的组件,它可以使用图形界面、文本界面返回一个特殊值标识执行测试结果
2.举例说明
首先我们来看这个例子,class类中定义了两个函数,分别用来搜索两个不同的关键词。区别在于第二个的断言中判断的是网页中是否存在第一个的关键词,很明显第二个必然报错,这里我们就是想看一下它的错误结果。
代码中函数中关于元素的定位在第一讲中已经说过,这里我们主要来看四个基本概念在代码中的使用。Fixture在代码中的体现就是setUp和tearDown函数,前者是每一个测试用例之前的准备工作,如果在其执行过程中出现异常则该测试用例不会被执行,后者是测试用例之后的一个清理工作。Case用于继承上述中的方法,Suite用来将定义好的测试方法装在到TestSuite()中,Runner用来执行所装载的测试方法。
from selenium import webdriver
from selenium.webdriver.common.by import By
import unittest
import time
class Search_KeyWords(unittest.TestCase):
def setUp(self):
self.driver = webdriver.Chrome()
self.driver.implicitly_wait(30)
self.url = 'https://www.baidu.com'
def test_search_spank(self):
driver = self.driver
driver.get(self.url)
driver.find_element(by=By.ID, value='kw').clear()
driver.find_element(by=By.ID, value='kw').send_keys("sp")
driver.find_element(by=By.ID, value='su').click()
time.sleep(3)
try:
self.assertTrue('spank' in driver.page_source)
except AssertionError as e:
raise e
def test_search_selenium(self):
driver = self.driver
driver.get(self.url)
driver.find_element(by=By.ID, value='kw').clear()
driver.find_element(by=By.ID, value='kw').send_keys("hello")
driver.find_element(by=By.ID, value='su').click()
time.sleep(3)
try:
self.assertTrue('sp' in driver.page_source)
except AssertionError as e:
raise e
def tearDown(self):
self.driver.quit()
if __name__ == "__main__":
suite = unittest.TestSuite()
suite.addTest(Search_KeyWords('test_search_spank'))
suite.addTest(Search_KeyWords('test_search_selenium'))
runner = unittest.TextTestRunner()
runner.run(suite)
【注1】断言,这玩意用于判断一个表达式,在表达式条件为 false 的时候触发异常。断言可以在条件不满足程序运行的情况下直接返回错误,而不必等待程序运行后出现崩溃的情况,例如我们的代码只能在 Linux 系统下运行,可以先判断当前系统是否符合条件。
![](/qrcode.jpg)
在看第二个例子之前,让我们先了解冒泡排序,所谓冒泡排序就是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。下面的算法定义了升序和降序两种排序方式,可以直接使用。
class BubbleSort(object):
def __init__(self, mylist):
self.mylist = mylist
self.length = len(mylist)
# 定义升序, 将小的放在前面
def ascending_order(self):
for i in range(self.length-1):
for j in range(self.length-1-i):
if self.mylist[j] > self.mylist[j+1]:
self.mylist[j], self.mylist[j+1] = self.mylist[j+1], self.mylist[j]
return self.mylist
# 定义降序,将大的放在前面
def descending_order(self):
for i in range(self.length-1):
for j in range(self.length-1-i):
if self.mylist[j] < self.mylist[j + 1]:
self.mylist[j], self.mylist[j + 1] = self.mylist[j + 1], self.mylist[j]
return self.mylist
下面编写测试用例来进行测试,通过给出两对数组,每一对数组中有一个是正确的。这里设置了一对升序,一对降序,正确的结果如下图所示,当然如果你设置的和待检测的不同,就会和图一一样报错。
import unittest
from learn_public.BubbleSort import BubbleSort
class TestBubbleSort(unittest.TestCase):
@classmethod
def setUpClass(cls):
print("execute setUpClass\n")
@classmethod
def tearDownClass(cls):
print("execute tearDownClass\n")
def setUp(self):
self.list1 = [2, 10, 25, 30, 45, 100, 325]
self.list3 = [325, 10, 25, 45, 30, 100, 2]
self.list4 = [11, 3, 41, 101, 327, 26, 46]
self.list2 = [327, 101, 46, 41, 26, 11, 3]
def tearDown(self):
print("execute tearDown\n")
def test_descending_order(self):
bls = BubbleSort(self.list4)
self.list5 = bls.descending_order()
try:
self.assertEqual(self.list5, self.list2)
except AssertionError as e:
raise e
def test_ascending_order(self):
bls = BubbleSort(self.list3)
self.list6 = bls.ascending_order()
try:
self.assertEqual(self.list6, self.list1)
except AssertionError as e:
raise e
if __name__ == '__main__':
unittest.main()
【注2】@classmethod 是python中的一种类方法。在普通情况下,不使用类方法对类进行实例化,类本身是不具有记忆性的。只是当一个静态模板被套用多次而已,类方法对类属性进行的处理是有记忆性的。
二.上下文管理器
上下文管理器subTest(),它能让用户无须定义多个测试方法就能完成多组参数的单元测试工作,极大的提高了单元测试的工作效率。这样说可能大家无法理解什么叫无需定义多个测试方法,你在上面不是定义了几个么,来看下面这个例子:
import unittest
class NumbersTest(unittest.TestCase):
def test_even(self):
for i in range(0, 6):
with self.subTest(i=0):
self.assertEqual(i % 2, 0)
if __name__ == '__main__':
unittest.main()
这里是检验0~5六个数除以2后取余的结果,我们很容易知道偶数是0应该输出正确的结果,奇数是1肯定报错啦。按照常规的写法在循环中,应该对每一个元素都进行一次断言,但这样会在循环到1时就报错了。而使用subTest()方法则可以查看6个数字的结果,如下图所示我们可以看见有三个错误的测试。