Selenium WebDriver基于Python - 使用unittest编写单元测试(二)

Selenium WebDriver是一个浏览器自动化测试的API集合。它提供了很多与浏览器自动化交互的特性,并且这些API主要是用于测试Web程序。如果仅仅使用Selenium Driver,我们无法实现执行测试前置条件、测试后置条件,对比预期结果和实际结果,检查程序的状态,生成测试报告,创建数据驱动的测试等功能。在此,我们总结一下如何使用unittest来创建基于Python的Selenium WebDriver测试脚本。

unittest单元测试框架

unittest(一般称为PyUnit)是从Java程序开发中广泛应用的JUnit启发而来的。我们可以使用unittest为任何项目创建全面的测试套件。unittest也是Python中用来测试各种标准类库模块的,甚至包括unittest自己。可以在以下网址查看unittest的文档:https://docs.python.org/2/library/unittest.html,unittest使我们具备创建测试用例、测试套件、测试夹具的能力。

Test Fixture(测试夹具):通过使用测试夹具,可以定义在单个或多个测试执行之前的准备工作和测试执行之后的清理工作。

Test Case(测试用例):一个测试用例实在unittest提供的assert方法来验证一组特定的操作和输入以后得到的具体响应。unittest提供了一个名称为TestCase的基础类,可以用来创建测试用例。

TestSuite(测试套件):一个测试套件是多个测试或测试用例的集合,是针对被测程序的对应的功能和模块创建的一组测试,一个测试套件内的测试用例将一起执行。

Test Runner(测试执行器):测试执行器负责测试执行调度并且生成测试结果给用户。测试执行器可以使用图形界面、文本界面或者特定的返回值来展示测试执行结果。

Test Report(测试报告):测试报告用来展示所有执行用例的成功或者失败状态的汇总,执行失败的测试步骤的预期结果与实际结果,还有整体运行状况和运行时间的汇总。


通过与unittest类似的xUnite测试框架创建的测试杯拆分为3部分,即3A's,具体如下:

Arrange:是用来初始化测试的前置条件,包含初始化被测试的对象、相关配置和依赖。

Act:用来执行功能操作。

Assert:用来检验实际与预期结果是否一致。


TestCase类

我们可以通过继承TestCase类并且在测试类中为每一个测试添加测试方法来创建单个测试或者一组测试。

为了创建测试,我们需要使用TestCase类中的assert或者其中的一种assert方法。

每个测试最重要的任务是调用assertEqual()来检验预期结果,调用assertTrue()来验证条件,或者调用assertRaises()来验证预期的异常。

除了添加测试,我们可以添加测试夹具 - - setUp()方法和tearDown()方法,创建或处置测试用例所需要的任何对象和条件。

setUp():一个测试用例是从setUp()方法开始执行的,我们可以用这个方法在每个测试开始前去执行一些初始化的任务。可以是这样的初始化准备:比如创建浏览器实例,访问URL,加载测试数据和打开日志文件等。

此方法没有参数,而且不返回任何值。当定义了一个setUp()方法,测试执行器在每次执行测试方法之前优先执行该方法。


测试方法命名是以test开头的。这种命名约定通知test runner 哪个方法代表测试方法。

对于test runner能找到的每个测试方法,都会在执行测试方法之前先执行setUp()方法。这样做有助于确保每个测试方法都能够依赖相同的环境,无论类中有多少测试方法。我们将使用简单的assertEqual()方法来验证用程序搜索该术语返回的结果是否和预期结果相匹配。


tearDown()方法:代码清理。类似于setUp()方法在每个测试方法之前被调用,TestCase类也会在测试执行完成之后调用tearDown()方法来清理所有的初始化值。

一旦测试被执行,在setUp()方法中定义的值将不再需要,所以最好的做法实在测试执行完成时清理掉由setUp()方法初始化的数值。

-*- coding:utf-8 -*-

import unittest

from selenium import webdriver


class TestUnittest(unittest.TestCase):
    # 一个测试用例从setUp()方法开始执行的,此方法没有参数,而且不返回任何值。
    def setUp(self):
        # create a new firefox session
        self.driver = webdriver.Firefox()
        self.driver.implicitly_wait(30)
        self.driver.maximize_window()

        # navigate to the application home page
        self.driver.get("http://www.baidu.com/")

    def test_search_by_category(self):
        # get the search textbox
        self.search_field = self.driver.find_element_by_id('kw')
        self.search_field.clear()

        # enter search keyword and submit
        self.search_field.send_keys('路飞')
        self.search_field.submit()

        # get all the anchor elements which have product names
        # displayed currently on result page using
        # find_elements_by_xpath method
        products = self.driver.find_elements_by_xpath('//h3[@class="t"]/a')
        for product in products:
            print(product.text)
        print("products.len = "+str(products.__len__()))

        self.assertEqual(9, len(products))

    # 代码清理
    def tearDown(self):
        # close the browser window
        self.driver.quit()

unittest.main()  # unittest.main(verbosity=2)

执行结果为:

----------------------------------------------------------------------
Ran 1 test in 4.875s

OK

Process finished with exit code 0

若执行失败,失败自身会以AssertionError形式显示:

----------------------------------------------------------------------
Traceback (most recent call last):
  File "XXX", line XX, in XXXXX
    XXXXXX
AssertionError: XXXXXX

----------------------------------------------------------------------
Ran 1 test in 4.897s

FAILED (failures=1)

Process finished with exit code 1


断言

unittest的TestCase类提供了很多实用的方法来校验预期结果和程序返回的实际结果是否一致。这些方法要求必须满足某些条件才能继续执行接下来的测试。大致有3种这样的方法,各覆盖一个特定类型的条件,例如等价检验、逻辑校验和异常校验。如果给定的断言通过了,接下来的测试代码将会执行;相反,将会导致测试立即停止并且给出异常信息。

unittest提供了所有的标准xUnit断言方法。下表列出了一些重要的方法。



测试套件

应用unittest的TestSuites特性,可以将不同的测试组成一个逻辑组,然后设置统一的测试套件,并通过一个命令来执行测试。这都是通过TestSuites、TestLoader和TestRunner类来实现的。

TestUnittest.py

# -*- coding:utf-8 -*-

import unittest
import time
from selenium import webdriver


class TestSearch(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        # create a new firefox session
        cls.driver = webdriver.Firefox()
        cls.driver.implicitly_wait(30)
        cls.driver.maximize_window()

        # navigate to the application home page
        cls.driver.get("http://www.baidu.com/")

    def test_search_by_category(self):
        # get the search textbox
        self.search_field = self.driver.find_element_by_id('kw')
        self.search_field.clear()

        # enter search keyword and submit
        self.search_field.send_keys('python')
        self.search_field.submit()

        # get all the anchor elements which have product names
        # displayed currently on result page using
        # find_elements_by_xpath method
        products = self.driver.find_elements_by_xpath('//h3[@class="t"]/a')
        # for product in products:
            #print(product.text)
        # print("products.len = "+str(products.__len__()))
        self.assertEqual(9, len(products))

    def test_search_by_name(self):
        # get the search textbox
        self.search_field = self.driver.find_element_by_id("kw")
        self.submit_field = self.driver.find_element_by_id("su")
        self.search_field.clear()

        # enter search keyword and submit
        self.search_field.send_keys('路飞')
        self.search_field.submit()
        # time.sleep(2)
        self.submit_field.click()
        time.sleep(1)

        # get all the anchor elements which have product names displayed
        # currently on result page using find_elements_by_xpath method
        products = self.driver.find_elements_by_xpath('//h3[@class="t"]/a')
        # for product in products:
            #print(product.text)
        # print("products.len = "+str(products.__len__()))
        self.assertEqual(6, len(products))

    # 代码清理
    @classmethod
    def tearDownClass(cls):
        # close the browser window
        cls.driver.quit()


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

Test20180620.py

# -*- coding:utf-8 -*-

import unittest
from selenium import webdriver
from selenium.common.exceptionsimport NoSuchElementException
from selenium.webdriver.common.by import By


class HomePageTest(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        # create a new Firefox session """
        cls.driver = webdriver.Firefox()
        cls.driver.implicitly_wait(30)
        cls.driver.maximize_window()

        # navigate to the application home page """
        cls.driver.get("http://www.baidu.com/")

    def test_search_field(self):
        # check search field exists on Home page
        self.assertTrue(self.is_element_present(By.NAME, 'wd'))

    def test_language_option(self):
        # check language options dropdown on Home page
        self.assertTrue(self.is_element_present(By.ID, 'kw'))

    def test_search_by_category(self):
        # get the search textbox
        self.search_field = self.driver.find_element_by_id('kw')
        self.search_field.clear()

        # enter search keyword and submit
        self.search_field.send_keys('python')
        self.search_field.submit()

        # get all the anchor elements which have product names
        # displayed currently on result page using
        # find_elements_by_xpath method
        products = self.driver.find_elements_by_xpath('//h3[@class="t"]/a')
        # for product in products:
            #print(product.text)
        # print("products.len = "+str(products.__len__()))
        self.assertEqual(9, len(products))

    @classmethod
    def tearDownClass(cls):
        # close the browser window
        cls.driver.quit()

    def is_element_present(self, how, what):
        """
        Utility method to check presence of an element on page
        :param how:  By locator type
        :param what:  locator value
        :return:
        """
        try: self.driver.find_element(by=how, value=what)
        except NoSuchElementException: return False
        return True


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

TestSuites20180620.py

# -*- coding:utf-8 -*-

import unittest

from TestUnittest import TestSearch

from Test20180620 import HomePageTest


# get all tests from TestUnittest and HomPageTest
unittest_tests = unittest.TestLoader().loadTestsFromTestCase(TestSearch)
home_page_tests = unittest.TestLoader().loadTestsFromTestCase(HomePageTest)

# create a test suite combining unittest_test and home_page_test
smoke_tests = unittest.TestSuite([unittest_tests, home_page_tests])

# run the suite
unittest.TextTestRunner(verbosity=2).run(smoke_tests)

最终执行结果为:

/usr/local/bin/python3.6 /xxxx/Selenium_Test/TestSuite20180620.py
test_search_by_category (TestUnittest.TestSearch) ... ok
test_search_by_name (TestUnittest.TestSearch) ... ok
test_language_option (Test20180620.HomePageTest) ... ok
test_search_by_category (Test20180620.HomePageTest) ... ok
test_search_field (Test20180620.HomePageTest) ... ok

----------------------------------------------------------------------
Ran 5 tests in 10.364s

OK

Process finished with exit code 0

注意:

在套件时,每个单元测试.py文件必须:

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

生成HTML格式的测试报告

unittest在命令行输出测试结果。你可能需要生成一个所有测试的执行结果作为报告或者把测试结果发给相关人员。给相关人员发送命令行日志不是一个明智的选择。他们需要格式更加友好的测试报告,既能够查看测试结果的概况,也能够深入查看报告细节。

unittest没有相应的内置模块可以生成友好的报告,我们可以应用Wai Yip Tung编写的unittest的扩展HTMLTestRunner来实现。该网址及文档:https://pypi.org/project/HTMLTestRunner/ 但是这个还是有问题,在网上找到修改版,下载后放在project下即可。

我们将在测试中使用HTMLTestRunner来生成漂亮的测试报告。在测试套件文件中来添加HTMLTestRunner支持,需要创建一个包含实际测试报告的输出文件,需要配置HTMLTestRunner选项和运行测试,具体如下。

# -*- coding:utf-8 -*-

import unittest
import HTMLTestRunner
import os
from TestUnittest import TestSearch
from Test20180620 import HomePageTest


# get the directory path to output report file
dir = os.getcwd()

# get all tests from TestUnittest and HomPageTest
unittest_tests = unittest.TestLoader().loadTestsFromTestCase(TestSearch)
home_page_tests = unittest.TestLoader().loadTestsFromTestCase(HomePageTest)

# create a test suite combining unittest_test and home_page_test
smoke_tests = unittest.TestSuite([unittest_tests, home_page_tests])

# open the report file
# 使用 with open 完成后会帮你正确的关闭文件。
with open(dir + "/SmokeTestReport20180620.html", "wb") as outfile:
    # configure HTMLTestRunner options
    runner = HTMLTestRunner.HTMLTestRunner(stream=outfile,
                                           title='20180620 Test Report',
                                           description='Smoke Tests 20180620')
    # run the suite using HTMLTestRunner
    runner.run(smoke_tests)

在同目录下生成的测试报告文件 - SmokeTestReport20180620.html:







猜你喜欢

转载自blog.csdn.net/duanmulirui/article/details/80844315