我们不是在学习Web自动化么?为什么要去学习单元测试框架。其实单元测试框架并非只能用于代码级别的测试,对于单元测试框架来讲,笔者认为它主要完成以下三件事。
提供用例组织与执行:当你的测试用例只有几条时,可以不必考虑用例的组织,但是,当测试用例达到成百上千条时,大量的测试用例堆砌在一起,就产生了扩展性与维护性等问題,此时需要考虑用例的规范与组织问题了,单元测试框架就是用来解决这个问題的。
提供丰富的比较方法:不论是功能测试,还是单元测试,在用例执行完成之后都需要将实际结果与预期结果进行比较,从而断定用例是否执行通过、单元测试框架一般会提供丰富的断言方法。例如,判断相等/不等、包含/不包含、True/False的断言方法等.
提供丰富的日志:当测试用例执行失败时能抛出清晰的失败原因,当所有用例执行完成后能提供丰富的执行结果。例如,总执行时间、失败用例数、成功用例数等
一般的单元测试框架都会提供这些功能,从单元测试框架的这些特性来看,它同样适用于Web自动化用例的开发与执行。
什么是单元测试?
单元测试负责对最小的软件设计单元(模块)进行验证,它使用软件设计文裆中对模块的描述作为指南,对重要的程序分支进行测试以发现模块中的错误。在Python语言下有诸多单元测试枢架,如doctest、unittest、pytest、nose等,unittest框架 (原名PyUnit框架)为Python语言自带的单元测试框架,Python2.1及其以后的版本已将unittest作为一个标淮模块放入Python开发包中。
学习Unittest需要知道4个重要的概念:test fixture、test case、test suite 和testrunner。
1、Test Case
一个Test Case的实例就是一个测试用例。什么是测试用例呢?就是一个完整的测试流程,包括测试前准备环境的搭建(setUp)、实现测试过程的代码(run),以及测试后环境的还原(tearDown)。单元测试(unit test)的本质也就在这里,一个测试用例就是一个完整的测试单元,通过运行这个测试单元,可以对某一个功能进行验证。
2、Test Suite
一个功能的验证往往需要多个测试用例,可以把多个测试用例集合在一起来执行,这就产生了测试套件TestSuite的概念。Test Suite用来组装单个测试用例。可以通过addTest () 加载TestCase到TestSuite中,从而返回一个TestSuite实例。
3、Test Runner
测试的执行也是单元测试中非常重要的一个概念,一般单元测试框架中都会提供丰富的执行策略和执行结果。在unittest单元测试框架中,通过TextTestRunner类提供的run()力法来执行test suite/test case。test runner可以使用图形界面、文本界面,或返回一个特殊的值等方式来表示测试执行的结果。
4、Test Fixture
对一个测试用例环境的搭建和销毁,就是一个Fixture,通过覆盖Test Case的setUp()和tear Down()方法来实现。有什么用呢?比如说在这个测试用例中需要访问数据库,那么可以在setUp()中通过建立数据库连接来进行初始化,在tearDown()中清除数据库产生的数据,然后关闭连接等。
注意:tearDown()的过程很重要,要为下一个tset case留下一个干净的环境。
***使用unittest单元测试框架,记得导入
import unittest
1、断言方法
在执行用例的过程中,最终用例是否执行通过,是通过判断测试得到的实际结果与预期结果是否相等决定的。unittest框架的TestCase类提供下面这些方法用于测试结果的判断。
-assertEqual(first, second, msg=None)
断言第一个参数和第二个参数是否相等,如果不相等则测试失败。msg为可选参数,用于定义测试失败时打印的信息。
import unittest
#断言方法
class testInput(unittest.TestCase):
def setUp(self):
print("test start")
number = input("Enter a number:")
self.number = int(number)
def test_case(self):
self.assertEqual(self.number,10,msg="Your input is not 10")
def tearDown(self):
print("test end")
pass #pass语句什么也不做,一般作为占位符或者创建占位程序,pass语句不会执行任何操作
2、discover()
unittest提供了可以共享的defaultTestLoader类,可以使用其子类和方法创建实例,discover()方法就是其中之一。
discover(start_dir, patten=“test* .py”, top_Ievel_dir=None)
找到指定目录下所有测试模块,并可递归査到子目录下的测试模块,只有匹配到文件名才能被加载。如果启动的不是顶层目录,那么顶层目录必须单独指定。
●start_dir :要测试的模块名或测试用例目录。
●patten=“test* .py”:表示用例文件名的匹配原则。此处匹配文件名以“test”开头 的“.py”类型的文件,星号“*”表示任意多个字符。
●top_level_dir=None:测试模块的顶层目录,如果没有顶层目录,默认为None。
3、关于unittest需要知道的
3.1用例执行的顺序
unittest框架默认根据ASCII码的顺序加载测试用例,数字与字母的顺序为:0-9, A〜Z, a〜z。
discover()的加载测试用例的规则与 main()方法相同。所以,我们只能通过测试用例的命名来提高被执行的优先级。
不过可以通过TestSuite类的addTest()方法按照一定的顺序来加载。
3.2执行多级目录的用例
如果将discover()方法中的start_dir参数定义为当前目录,那么只能加载当前目录下的测试用例。怎样让unittest框架査找到当前目录的子目录中的测试文件呢?方法很简单,在每个子目录下放一个__init__.py文件。
3.3 跳过测试和预期失败
在运行测试时,有时需要直接跳过某些测试用例,或者当用例符合某个条件时跳过测试,又或者直接将测试用例设置为失败。unittest提供了实现这些需求的装饰器。
●unittest.skip(reason)
无条件地跳过装饰的测试,说明跳过测试的原因。
●unittest.skipIf(condition, reason)
如果条件为真时,跳过装饰的测试。
●unittest.skipUnless(condition, reason)
如果条件为假,跳过装饰的测试。
●unittest.expectedFailure()
测试标记为失败。不管执行结果是否失败,统一标记为失败。
4.fixtures
unittest还提供了更大范围的fixtures,例如对于测试类和模块的fixtures。
setUpModule/tearDownModule: 在整个模块的开始与结束时被执行
setUpClass/tearDownClass : 在测试类的开始与结束时被执行。
setUp/tearDown : 在测试用例的开始与结束时被执行。
需要注意的是,setUpClass/tearDownClass的写法稍微有些不同,首先,需要通过@classmethod进行装饰,其次方法的参数为cls。其实,cls与self并没有什么特别之处,都只表示类方法的第一个参数,只是大家约定俗成,习惯于这样来命名,当然也可以用abc来代替。
备注:
学习了Python自带的单元测试框架unittest,当然,学习的目的并不是为了编写单元测试,而是为了用它来编写Web自动化测试用例。
利用其组织测试用例、断言、预期结果以及批量执行测试用例等功能,可以很好地进行Web自动化测试的开发。