1.2 更多自动化测试战术体验
1.2.1 利用unittest组织测试脚本
由于Selenium本身并没有提供一套完整的测试框架,因此我们选用unittest测试框架来进一步完善自动化测试脚本。下面是实现的程序。
import unittest from selenium import webdriver class BookFlight(unittest.TestCase): def setUp(self): #实例化 self.driver = webdriver.Firefox() def test_login(self): driver = self.driver driver.get("Mercury Tours登录页面") assert "Mercury Tours" in driver.title username_edit = driver.find_element_by_name("userName") password_edit = driver.find_element_by_name("password") login_button = driver.find_element_by_name("login") username_edit.send_keys("mercury") password_edit.send_keys("mercury") login_button.click() assert "Find a Flight" in driver.title def tearDown(self): self.driver.close() if __name__ == "__main__": unittest.main() |
运行这个自动化测试脚本,脚本运行结果如图1.16所示。可以看出脚本正常运行。
图1.16 脚本运行结果
与之前的例子有所不同的是,我们创建了一个新的BookFlight类并且继承了unittest. TestCase。一旦任何一个类继承了TestCase类,这个类就自动变为unittest单元测试类。类似于Java的JUnit,BookFlight类同样包括了setUp方法及tearDown方法。setUp方法通常代表一个类的初始化,它会在每一个test方法之前运行,所以我们会看到在此方法中加入了self.driver = webdriver.Firefox()。具体代码如下。
def setUp(self): self.driver = webdriver.Firefox() |
这样做就无须在每一个test方法之前都创建一个driver对象了。此处将webdriver. Firefox()实例返回给self.driver。这样操作以后,此类中的其他方法都可以使用此driver实例了。
至于tearDown方法,它和setUp方法的作用几乎一样,它会在每一个test方法之后运行,因此通常会把一些诸如退出、关闭之类的操作放入其中。此处我们加入了 self.driver.close(),这样我们就无须在每一个 test 方法之后加入关闭操作了。具体代码如下。
def tearDown(self): self.driver.close() |
在Python编程中,通常我们使用全小写的方式对方法、变量等进行命名,如my_first_python_script,但是有两个特殊情况。其一,类的命名不遵循这个规则;其二,setUp是一个特殊方法,改为全部小写之后就不起作用了,这个方法相当于JUnit中的setUp,在每个test方法之前都会运行,是关键字,区分大小写。
最后,查看这个脚本的核心部分,即test_login这个测试方法。具体代码如下。
def test_login(self): driver = self.driver driver.get("Mercury Tours登录页面") assert "Mercury Tours" in driver.title username_edit = driver.find_element_by_name("userName") password_edit = driver.find_element_by_name("password") login_button = driver.find_element_by_name("login") username_edit.send_keys("mercury") password_edit.send_keys("mercury") login_button.click() assert "Find a Flight" in driver.title |
“driver = self.driver”这一行代码表示将driver对象指向之前setUp创建的实例对象,这样可使用driver完成后续的一些操作。
所有方法名必须要以test开头,否则PyUnit不会执行这个方法,因为PyUnit会把这个方法作为普通方法而不是测试方法。
当然,在Python中,除了unittest之外,还有另外两个非常棒的单元测试框架—Nose和PyTest,有兴趣的读者可以尝试一下。一旦学会了unittest,你很快就能上手其他框架。
1.2.2 测试用例的数据驱动
代码片段如下。
import unittest from selenium import webdriver class BookFlight(unittest.TestCase): def __init__(self,username,password): unittest.TestCase.__init__(self, methodName='test_login') self.username = username self.password = password def setUp(self): self.driver = webdriver.Firefox() def test_login(self): driver = self.driver driver.get("Mercury Tours登录页面") assert "Mercury Tours" in driver.title username_edit = driver.find_element_by_name("userName") password_edit = driver.find_element_by_name("password") login_button = driver.find_element_by_name("login") username_edit.send_keys(self.username) password_edit.send_keys(self.password) login_button.click() assert "Find a Flight" in driver.title, \ "\n==> username:{0}\n==> password:{1}". \ format(self.username,self.password) def tearDown(self): self.driver.close() if __name__ == "__main__": def data_driven_suite(): data_repositories = [ {'usr':'mercury','pwd':'mercury'}, {'usr':'mercury1','pwd':'mercury'}, {'usr':'mercury2','pwd':'mercury'}, ] tests = [] for data in data_repositories: tests.append(BookFlight(data['usr'],data['pwd'])) return unittest.TestSuite(tests) runner = unittest.TextTestRunner() runner.run(data_driven_suite()) |
首先,看BookFlight类,它增加了_ _init_ _方法。此方法就是一个类的构造器,当类被初始化的时候被执行。一个实例化只会执行一次,无论类中存在多少个test方法,而setUp方法会在每一个test方法前执行。继续看代码。
def __init__(self,username,password): unittest.TestCase.__init__(self, methodName='test_login') self.username = username self.password = password |
在上面的代码中,首先指定需要执行的测试方法名test_login,并增加了两个参数,即username和password,这样做是为了从外部“实例化”测试类的时候,把用户名和密码传入此类中以达到参数化的目的。然后,如下面的代码片段所示,将脚本中的用户名和密码一并替换成了self.username和self.password。
username_edit.send_keys(self.username) password_edit.send_keys(self.password) |
接着,再来看data_driven_suite这个方法,它是用于数据驱动的核心方法。
def data_driven_suite(): data_repositories = [ {'usr':'mercury','pwd':'mercury'}, {'usr':'mercury1','pwd':'mercury'}, {'usr':'mercury2','pwd':'mercury'}, ] tests = [] for data in data_repositories: tests.append(BookFlight(data['usr'],data['pwd'])) return unittest.TestSuite(tests) |
在以上代码中,首先创建了一个字典数组data_repositories,其中包含了3组用户名和密码。然后,创建了一个空的tests数组,如果不理解,可以把这个空的tests数组看作一个用于存放所有test库的数组,把需要执行的test全部放在里面。接着,用一个for循环遍历整个data_repositories中的3组用户名和密码,生成3组不同用户名和密码的test,并将其添加到tests库中。最后,将整个tests库合并成一个测试集(testsuite)并返回。
runner = unittest.TextTestRunner() runner.run(data_driven_suite()) |
上面的程序表示把整个suite传给runner.run函数,之后即可运行。通过以上方式即可完成3组数据驱动测试,测试结果如图1.17所示。
图1.17 利用unittest框架执行的测试结果
一共有3组测试,失败了两组,因为第二组和第三组的用户名与密码都是不正确的。同时,测试结果中输出了出错时所使用的数据,这是因为我们已经在脚本的“assert”中加入了日志输出功能。具体代码如下。
assert "Find a Flight" in driver.title, \ "\n==> username:{0}\n==> password:{1}". \ format(self.username,self.password) |
在代码片段中,每行句末的符号“\”是Python中的续行符号。
版权声明:51Testing软件测试网获得人民邮电出版社和作者授权连载本书部分章节。
任何个人或单位未获得明确的书面许可,不得对本文内容复制、转载或进行镜像,否则将追究法律责任。