一、概述

  对网页计算器,进行加减乘除的测试操作。通过读取数据文件中的数据来执行测试用例。

  数据驱动

  网址:https://cal.supfree.net/cal.html

  实现步骤:

  1.采用po模式的分层思想对页面进行封装(page、object)

  2.编写测试脚本

  3.使用参数化传入测试数据(parameterized库)

二、项目框架

  1.base包(主要封装对驱动浏览器层面的操作方法——selenium库相关的方法)

    ①base.py:封装元素的基本操作,内有Base类,包含的方法:页面初始化、查找元素方法、点击元素方法、获取value属性方法、截图方法

    ②get_driver.py:内有GetDriver类,用来实现driver的封装和关闭

  2.page包(主要封装计算器的业务方法——加减乘除以及计算器的相关信息)

    ①__init__.py (创建包时自动生成)定位数据,可以看做是传入函数的一些参数,方便维护修改

        如写入服务器域名地址、计算器配置数据、测试用例文件数据

    ②page_calc.py :内有PageCalc(Base)类并继承Base类,调用Base类中封装的方法对具体元素进行操作

        (a)点击数字方法:page_click_num(self,num)遍历字符串时,负数和整数的点击顺序不同所以判断是否为负数

        (b)点击加号:page_click_add(self)、点击减号page_click_sub(self)、点击乘号page_click_mul(self)、点击除号page_click_div(self)、点击等号page_click_eq(self)、获取结果page_get_result(self)、点击清屏page_click_clear(self)

        (c)组装运算业务方法:完成运算的步骤:a+-*/b=(注意这是不用获取结果)

  3.script文件夹(执行文件、测试用例文件)

    ①index.py:执行用例并生成报告,测试套件与HTMLTestRunner搭配使用

    ②test_calc.py:内有TestCalc(unittest.TestCase)类并继承unittest.TestCase类

      实例化driver对象和page_calc对象

      初始化方法:setUpClass、结束方法tearDownClass

      测试运算方法:

        步骤:调用运算业务方法(组装过的)、断言、截图、抛异常

        方法:

          测试加法:@parameterized.expand(get_data(page.add_file))、test_add_calc(self,a,b,expcet)、

          用parameterized对数据进行动态获取并引用、expcet为运算结果,如果输入数据为数据型,断言时将其转变为字符型

          测试减法、测试乘法、测试除法同加法

  4.tools包(实现对数据的读取)

    ①read_json.py:读取json文件中的数据,调用json.load方法

    ②get_data.py:由于测试用例是json文件,读取出来的数据是字典,而parameterized参数化时数据类型是元组,所以需要将字典转化成元组

    ③HTMLTestRunner.py:用来生成测试报告,网上可下载此文件

  5.data文件夹

    json文件,存放测试用例

    格式

  6.image文件夹:存放截图,当断言错误时,截图保存

  7.report文件夹:存放测试报告,html格式

    

三、

0.安装两个库

  pip install selenium

  pip install parameterized

1.page包——__init__.py

from selenium.webdriver.common.by import By
"""
存放计算器页面的实际信息,若后续这些信息发生变化或者换个网页的计算器,只需要修改这里的信息即可
"""

"""以下为服务器域名配置地址"""
url = 'https://cal.supfree.net/cal.html'

"""以下是计算器配置数据"""
# 由于数字键是有一定的规律,所以暂时先不用定位,用到的时候再考虑怎么解决
# cala_num = By.CSS_SELECTOR, '#simple{}'
# 定位加号+
calc_add = By.CSS_SELECTOR, '#simpleAdd'
# 定位减号-
calc_sub = By.CSS_SELECTOR, '#simpleSubtr'
# 定位乘号
calc_mul = By.CSS_SELECTOR, '#simpleMulti'
# 定位除号
calc_div = By.CSS_SELECTOR, '#simpleDivi'
# 定位等号
calc_eq = By.CSS_SELECTOR, '#simpleEqual'
# 定位小数点
calc_dot = By.CSS_SELECTOR, "#simpleDot"
# 定位负数符号
calc_neg = By.CSS_SELECTOR, "[value = '+/-']"
# 获取结果
calc_resual = By.CSS_SELECTOR, '#resultIpt'
# 定位清屏
calc_clear = By.CSS_SELECTOR, '#simpleClearAllBtn'

"""以下为测试用例文件"""
add_file = "addCalc.json"
sub_file = "subCalc.json"
mul_file = "mulCalc.json"
div_file = "divCalc.json"

2.base包——get_driver.py

from selenium import webdriver
import page

class GetDriver:
    # 初始化类属性
    driver = None

    # 获取driver
    # @classmethod修饰符对应的函数不需要实例化
    @classmethod
    def get_driver(cls):
        # 保证只有一个driver对象
        if cls.driver is None:
            # 实例化对象
            cls.driver = webdriver.Chrome()
            # 打开浏览器、计算器页面
            cls.driver.get(page.url)
            # 最大化
            cls.driver.maximize_window()
        return cls.driver

    # 退出driver
    @classmethod
    def quit_driver(cls):
        if cls.driver:
            cls.driver.quit()
            cls.driver = None


if __name__ == '__main__':
    # 第一次获取浏览器对象
    print(GetDriver().get_driver())     # 不需要实例化即可直接调用get_driver()
    # 第二次获取浏览器对象
    print(GetDriver.get_driver())       # 虽然第一次的浏览器对象没有关闭,但仍然只有一个浏览器是打开的

3.base包——base.py

import time
from selenium.webdriver.support.wait import WebDriverWait

class Base:
    # 初始化——处理对象仍是针对浏览器驱动对象
    def __init__(self, driver):
        self.driver = driver

    # 查找元素方法,封装
    def base_find_element(self, loc):
        """
        :param loc: 元素的定位信息,格式为元组
        :return: 返回查找到的元素
        """
        # 查找过程中使用显示等待
        # *loc:不定长参数,一个星号*可传入元组、列表;两个星号**可传入字典
        return WebDriverWait(self.driver, 10).until(lambda x: x.find_element(*loc))

    # 点击元素方法,封装
    def base_click(self, loc):
        self.base_find_element(loc).click()

    # 获取value属性
    def base_get_value(self, loc):
        # 使用get_attribute()方法获取指定的元素属性值
        # 注意:返回
        return self.base_find_element(loc).get_attribute("value")

    # 截图 方法封装
    def base_get_img(self):
        self.driver.get_screenshot_as_file("../image/{}.png".format(time.strftime("%Y-%m-%d %H-%M-%S")))

4.page包——page_calc.py

from selenium.webdriver.common.by import By
import page
from base.base import Base

class PageCalc(Base):
    # 点击数字方法
    def page_click_num(self, num):
        # num为负数
        if num < 0:
            flag = True
            for n in str(num):
                if flag:
                    # 先跳过负号(即num索引[0])
                    flag = False
                    continue
                elif n == '.':
                    self.base_click(page.calc_dot)
                else:
                    # 拆开单个按钮的定位方式
                    loc = By.CSS_SELECTOR, '#simple{}'.format(n)
                    self.base_click(loc)
            # 最后点击负号
            # 网页上的计算器在输入负数的时候,就是在最后输入负号才有效的
            self.base_click(page.calc_neg)
        # num为正数
        else:
            for n in str(num):
                if n == '.':
                    self.base_click(page.calc_dot)
                else:
                    loc = By.CSS_SELECTOR, "#simple{}".format(n)
                    self.base_click(loc)

    # 点击加号
    def page_click_add(self):
        self.base_click(page.calc_add)

    # 点击减号
    def page_click_sub(self):
        self.base_click(page.calc_sub)

    # 点击乘号
    def page_click_mul(self):
        self.base_click(page.calc_mul)

    # 点击除号
    def page_click_div(self):
        self.base_click(page.calc_div)

    # 点击等号
    def page_click_eq(self):
        self.base_click(page.calc_eq)

    # 获取结果
    def page_get_result(self):
        return self.base_get_value(page.calc_resual)

    # 清屏
    def page_click_clear(self):
        self.base_click(page.calc_clear)

    # 截图
    def page_get_image(self):
        self.base_get_img()

    # 组装加法业务
    def page_add_calc(self, a, b):
        self.page_click_num(a)
        self.page_click_add()
        self.page_click_num(b)
        self.page_click_eq()

    # 组装减法业务
    def page_sub_calc(self, a, b):
        self.page_click_num(a)
        self.page_click_sub()
        self.page_click_num(b)
        self.page_click_eq()

    # 组装乘法业务
    def page_mul_calc(self, a, b):
        self.page_click_num(a)
        self.page_click_mul()
        self.page_click_num(b)
        self.page_click_eq()

    # 组装除法业务
    def page_div_calc(self, a, b):
        self.page_click_num(a)
        self.page_click_div()
        self.page_click_num(b)
        self.page_click_eq()


"""
————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
以下内容是对该类的测试,正常情况下,导入行为应该放置到文件开始的位置
"""
from selenium import webdriver
import time
if __name__ == '__main__':
    driver = webdriver.Chrome()
    driver.get(page.url)
    pc = PageCalc(driver)       # 实例化

    # 测试查找数字(√)
    pc.page_click_num(-1234)      # 能够成功点击
    pc.page_click_num(12)         # 找出错误:查找正数元素时simple{}前面少个#
    pc.page_click_num(12.33)
    pc.page_click_clear()       # 清屏(√)

    # 测试组装的加法(√)
    pc.page_add_calc(1, 3)
    addtest = pc.page_get_result()      # 获取结果(√)
    print(addtest)          # 4
    pc.page_get_image()     # 截图能够成功保存到image目录(√)
    pc.page_click_clear()

"""
疑问:
1.为什么会发生漏截图的情况?
"""

5.tools包——read_json.py

import json

def read_json(filename):
    filepath = '../data/'+filename
    # 调用Load方法
    with open(filepath, 'r', encoding='utf-8') as f:
        return json.load(f)


if __name__ == '__main__':
    print(read_json('addCalc.json'))        # 与写入的内容一致:字典{键:{值.字典}, 键,{}}   (√)

6.tools包——get_data.py

from tools.read_json import read_json

def get_data(filename):
    datas = read_json(filename)
    # 新建空列表
    arrs = []
    for data in datas.values():
        arrs.append((data['a'], data['b'], data['expect']))
    return arrs


if __name__ == '__main__':
    arrs = get_data('addCalc.json')
    print(arrs)

    # 发现错误:append中少些一对括号,没有是数据变成元组形式

7.script文件夹——test_calc.py

import unittest
from parameterized import parameterized
import page
from base.get_driver import GetDriver
from page.page_calc import PageCalc
from tools.get_data import get_data


class TestCalc(unittest.TestCase):
    driver = None

    # setUp
    @classmethod
    def setUpClass(cls):
        # 获取driver
        cls.driver = GetDriver.get_driver()
        # 初始化 计算器页面对象
        cls.calc = PageCalc(cls.driver)

    # tearDown
    @classmethod
    def tearDownClass(cls):
        # 关闭driver
        GetDriver.quit_driver()

    # 测试加法方法
    @parameterized.expand(get_data(page.add_file))
    def test_add_calc(self, a, b, expcet):
        # 调用计算器业务加法
        self.calc.page_add_calc(a, b)
        try:
            # 断言
            self.assertEqual(self.calc.page_get_result(), str(expcet))      # 注意,获取到的结果是字符串,二expcet是数值,因此要转换类型
        except:
            # 截图
            self.calc.page_get_image()
            raise

    # 测试减法方法
    @parameterized.expand(get_data(page.sub_file))
    def test_sub_calc(self, a, b, expcet):
        # 调用计算器业务减法
        self.calc.page_sub_calc(a, b)
        try:
            # 断言
            self.assertEqual(self.calc.page_get_result(), str(expcet))
        except:
            # 截图
            self.calc.page_get_image()
            raise

    # 测试乘法
    @parameterized.expand(get_data(page.mul_file))
    def test_mul_calc(self, a, b, expcet):
        # 调用计算器业务乘法
        self.calc.page_mul_calc(a, b)
        try:
            # 断言
            self.assertEqual(self.calc.page_get_result(), str(expcet))
        except:
            # 截图
            self.calc.page_get_image()
            raise

    # 测试除法
    @parameterized.expand(get_data(page.div_file))
    def test_div_calc(self, a, b, expcet):
        # 调用计算器业务除法
        self.calc.page_div_calc(a, b)
        try:
            # 断言
            self.assertEqual(self.calc.page_get_result(), str(expcet))
        except:
            # 截图
            self.calc.page_get_image()
            raise

8.script文件夹——index.py

import time
import unittest
from tools.HTMLTestRunner import HTMLTestRunner


# 定义 测试套件
suite = unittest.defaultTestLoader.discover('../script', pattern='test*.py')
# 定义报告存放位置及文件名称
report_dir = '../report/{}.html'.format(time.strftime("%Y-%m-%d %H-%M-%S"))
# 执行
with open(report_dir, 'wb') as f:
    HTMLTestRunner(stream=f, verbosity=2, title='计算器加减乘除测试报告').run(suite)

9.data文件夹——设计测试用例

addCalc.json 有一条结果是错误的,测试截图功能
{
   "calc 001":{"a": 2, "b": 3, "expect":5},
  "calc_002":{"a": 2, "b": 0.4, "expect" :2.4},
  "calc_003":{"a": -4, "b": -9, "expect" :-13},
  "calc_004":{"a": 12, "b": 0.34, "expect" :12.34},
  "calc_005":{"a": 0.56, "b": 78, "expect" :78.56},
  "calc_006":{"a": -90, "b": 10, "expect" :-80},
  "calc_007":{"a": 123456, "b": -123456, "expect" :0},
  "calc_008":{"a": 12, "b": -12, "expect" :1}
}

  减乘除的数据同上,把结果换一下即可

10.运行index.py

  结果:生成的报告显示只有一个问题,且并不是因为反例造成的(有四个有问题的测试用例),浏览器并没有自动打开

 

 

 

四、验证每份代码文件是否有问题

  在排查过程中发现很多细节上的问题

    如:在定位并点击数字元素时,少写一个#,导致num>=0的数没有办法定位到,一直报查找超时的错误

      在断言中,判断获取的结果和数据文件中的结果时,expcet没有转换成str()类型,导致断言错误

      append中少些一对括号,导致数据不是元组形式的,导致parameterized传参时一直报错提示有“,”

  遇到问题:

    page_calc.py:在连续测试加减乘除的方法时,每得到一个结果就截一张图,但是永远少一张,但没有报错。

           为什么会漏截?猜测运行速度过快?——尝试每得到一个结果截图后,sleep1秒——能够正常获取四张截图

           为什么出现这样的情况仍不报错?

五、最终结果

  在把错误都排除完后,代码能够执行下去,且结果与预期一致

自动化测试窗口切换java 自动化测试页面_json

         

自动化测试窗口切换java 自动化测试页面_自动化测试窗口切换java_02

 

得到的测试报告

自动化测试窗口切换java 自动化测试页面_json_03