Python+Selenium+unittest自动化测试框架

一. 自动化测试框架

1. 什么是自动化测试框架

简单来说,自动化测试框架就是由一些标准,协议,规则组成,提供脚本运行的环境。自动化测试框架能够提供很多便利给用户高效完成一些事情,例如,结构清晰开发脚本,多种方式、平台执行脚本,良好的报告去跟踪脚本执行结果。

框架具有以下一些优点:
1)代码复用
2)最大覆盖率
3)很低成本维护
4)很少人工干预
5)简单报告输出

2. 框架基本组件

我们来思考下框架组成部分:

1)测试数据分离【需要配置文件管理】
2)业务逻辑代码和测试脚本分离
3)报告和日志文件输出
4)自定义的库的封装
5)管理、执行脚本方式
6)第三方插件引入
7)持续集成

Python+Selenium+unittest自动化测试框架_第1张图片
image.png

3. 概要设计

概要设计包括了四个大的模块:公共库模块(装饰器、测试报告生成包、发送邮件管理)、用例仓库(测试用例)、页面管理(单独对Web页面进行抽象,封装页面元素和操作方法)、执行模块

概要设计类图:


Python+Selenium+unittest自动化测试框架_第2张图片
类图1.jpg

目录图:


Python+Selenium+unittest自动化测试框架_第3张图片
image.png
  1. common目录是公共库,用来存放装饰器、测试报告生成包、发送邮件管理等模块包
  2. config目录中存放的是测试配置相关的文件,文件类型ini,包括测试的网址、浏览器驱动等信息
  3. interface和model存放的是接口和页面的基础类,各页面的接口和元素的封装:一个页面封装为一个类,一个元素封装为一个方法
  4. report用来存放输出的测试报告和截图
  5. salesplatform_unit目录下,testcase用来存放测试用例,testsuites用来存放测试用例集
  6. main.py 执行用例

3.1 详细设计与实现

3.1.1 页面管理

页面模式是页面与测试用例之间的桥梁,它将每个页面抽象成一个单独的页面类,为测试用例提供页面元素的定位和操作。


Python+Selenium+unittest自动化测试框架_第4张图片
image.png

Page作为基类包含一个driver、url成员变量,它用来标记Selenium中的WebDriver,以便在BasePage的派生类中定位页面元素。ModelPage等作为派生类,可以提供相应页面元素的定位和操作方法。比如测试对象的登录页面:

Python+Selenium+unittest自动化测试框架_第5张图片
image.png

从页面可以看出,需要操作的页面元素分别为:Username,Password和Login按钮,它们对应的操作为输入用户名和密码,点击登录按钮,具体代码级别的实现如下:

页面基类Page.py:

class Page:
    """
    页面基础类
    """

    url = None

    def __init__(self, driver: webdriver.Chrome, url: str = None):
        """
        初始化页面
        :param url:
        """
        self.driver = driver
        self.driver.implicitly_wait(3)

        if url is not None or self.url is not None:
            self.driver.get(url if url is not None else self.url)

        try:
            self.driver.maximize_window()
        except exceptions.WebDriverException:
            pass

        self.current_handle = self.driver.current_window_handle
        handlers = self.driver.window_handles
        self.driver.switch_to.window(handlers[-1])

登录页面Login.py:

#!/usr/bin/python3
# -*- coding: utf-8 -*-
# @Time    : 2019-03-22 11:42
# @Author  : Huo
# @Email   : [email protected]
# @File    : Login.py
# @Content : 登录页面封装
from models import Page


class Login(Page):
    """
    登录
    """

    def act_login(self, username, password):
        """
        登录
        :param username:
        :param password:
        :return:
        """
        self.find_element_by_id("username").send_keys(username)
        self.find_element_by_id("password").send_keys(password)
        self.find_element_by_css_selector("button.ant-btn-lg").click()

    def act_logout(self):
        """
        退出登录
        :return:
        """
        self.find_element_by_link_text("登出").click()

3.1.2 公共库模块

公共库模块是为创建测试用例服务的,它主要包括装饰器、测试报告生成包、发送邮件管理等。
公共库模块涉及到的功能一般多而杂,在设计的时候只要遵循高内聚低耦合就可以了。
下图是该项目用到的一些功能模块:


Python+Selenium+unittest自动化测试框架_第6张图片
image.png

3.1.3 用例仓库

用例仓库主要用来组织自动化测试用例。每条测试用例都被抽象成一个独立的类,并且均继承自unittest.TestCase类。 Python中的unittest库提供了丰富的测试框架支持,包括测试用例的setUp和tearDown方法,在实现用例的过程中可以重写。依托页面管理和公共库模块实现的页面方法和公共函数,每一个测试用例脚本的书写都会非常清晰简洁:


Python+Selenium+unittest自动化测试框架_第7张图片
image.png

扩展基础TestCase类:TestCase.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-
# @Time    : 2019-03-22 11:42
# @Author  : Huo
# @Email   : [email protected]
# @File    : TestCase.py
# @Content : 扩展基础TestCase类

import unittest

from selenium import webdriver

from common import utils
from common.utils.Config import Config


class TestCase(unittest.TestCase):
    """
    扩展基础TestCase类.
    """

    config = {}

    driver = None

    close_browser_current_tab_on_tear_down = True

    @classmethod
    def setUpClass(cls):
        """
        测试开始前执行的动作.

        :return:
        """
        # 处理弹窗
        options = webdriver.ChromeOptions()
        prefs = {
            'profile.default_content_setting_values':
                {
                    'notifications': 1
                }
        }
        options.add_experimental_option('prefs', prefs)

        cls.driver = webdriver.Chrome(chrome_options=options)

    def tearDown(self):
        """
        每个测试结束时执行的动作.

        :return:
        """
        utils.ScreenShot.save_screen_shot(self.driver, '用例完成自动截图', sub_directory_name='completed')

        if self.close_browser_current_tab_on_tear_down is True:
            self.driver.close()

    @classmethod
    def tearDownClass(cls):
        """
        测试全部结束时执行的动作.

        :return:
        """
        cls.driver.quit()

登录界面测试用例:LoginTestCase.py

from . import TestCase
import models
from common.utils.Config import Config
from common import decorators


class LoginTestCase(TestCase):
    """
    登录页测试用例
    """

    @classmethod
    def setUpClass(cls):
        TestCase.setUpClass()
        cls.login = models.Login(url=Config.config().cf.get("URL", "login_url"))

    def tearDown(self):
        """将关闭浏览器标签的标志设置为False"""
        self.close_browser_current_tab_on_tear_down = False

    def test_login(self):
        """测试登录"""
        self.login.act_login(Config.config().cf.get("User", "username"), Config.config().cf.get("User", "password"))
        current_url = self.login.get_current_page_url()
        self.assertEqual(current_url, "https://testsalesp.sunlands.wang/html/index.html#/home", "登录失败,url不正确")

3.1.4 用例执行模块(控制器)

执行模块主要用来控制测试用例脚本的批量执行,形成一个测试集。

#!/usr/bin/env python3
# _._ coding:utf-8 _._
#
"""
运行测试用例,形成测试报告

:author: ronghui.huo 
"""

import os
import time
import unittest
import argparse
from salesplatform_unit import testcases
from salesplatform_unit import testsuites
from common.unittest_.runner import HTMLTestRunner

run_path = os.path.split(os.path.realpath(__file__))[0]

"""
举例说明:
python3 main.py -e production -r   正式环境,生成测试报告,所有套件
python3 main.py -ss Course     dev环境,不生成测试报告,CourseTestSuite套件
"""
parser = argparse.ArgumentParser()
parser.add_argument('-e', '--environment', default='development',
                    help='运行的测试环境,默认为:development。可选值:development pre-production production')
parser.add_argument('-r', '--report', action="store_true", help='生成HTML测试报告')
parser.add_argument('-ss', '--suites', default=[], nargs='*', help='设置运行的测试套件,若不设置则执行所有套件')

opts = parser.parse_args()

all_test_suites = {_suite[:-9].lower(): _suite for _suite in testsuites.__dict__ if 'TestSuite' in _suite}

if __name__ == '__main__':

    suite = unittest.TestSuite()

    for suite_name in opts.suites or all_test_suites.keys():
        if suite_name.lower() not in all_test_suites.keys():
            raise NameError("无法找到对应的测试套件:{0}".format(suite_name))

        suite.addTests(testsuites.__dict__[all_test_suites[suite_name.lower()]])

    if opts.report is False:
        runner = unittest.TextTestRunner()
    else:
        report_path = os.path.join(run_path, 'report')
        now = time.strftime('%Y-%m-%d %H-%M-%S')

        filename = os.path.join(report_path, now + 'report.html')
        fp = open(filename, 'wb')

        runner = HTMLTestRunner(stream=fp, title='课程库测试结果', description='测试报告.')

    runner.run(suite)

测试报告:


image.png

4.优化与改进

对于该测试框架,基本满足web对象的自动化需求,但还是有些可以改进提高的地方,比如:

  1. 参数化用例
  2. log记录
  3. 结合业界优秀的自动化框架和实践持续改进

5. 其他

测试数据的存放

  1. 对于账号密码这种全局参数,可以写在ini配置文件中,然后用cf.get随时获取数据
  2. 对于一次性消耗的数据,比如每次生成新的用户openId,可以用随机函数的方式获取
  3. 对于一个接口中有多组参数的数据,可以参数化,放到json、excel、yaml、text中
  4. 对于可以反复使用的数据,比如订单的各种状态需要造数据的情况,可以放到 数据库,每次数据初始化,用完后再清理
  5. 对于少量的静态数据,比如一个接口的测试数据,也就 2-3 组,可以写到 py 脚本的开头,十年八年都不会变更的

你可能感兴趣的:(Python+Selenium+unittest自动化测试框架)