精华内容
下载资源
问答
  • ui自动化测试项目实战
    2021-06-05 21:07:36

    两个问答

    自动化测试有什么用?

    这个问题,没啥细说的。

    节省人工、减少回归、保证软件的稳定之类的理论文章不计其数。

    你打算做自动化测试,恐怕更关心的是——掌握它,更有竞争力,面试能拿到更高的薪水。

    什么样的项目,能做自动化测试?

    这个问题,也没啥细说的。

    我有一个观点——任何项目都可以做自动化测试

    只不过,项目稳定、需求变更少,研发和维护周期长,有足够的的资源,更好做一点。

    两个误区

    真的为了钱吗?

    相比功能测试,自动化测试、测试开发岗位的薪水确实可观。

    但,你别忘了一个道理,是做自动化测试,能赚更多的钱;而不是,为了赚更多的钱,去做自动化测试。

    不要本末倒置。

    会写代码,就会自动化测试?

    不会写代码,也可以做自动化测试

    好的自动化测试框架,做自动化测试就像拼积木。再比如行为驱动框架,非研发人员,也可以做自动化测试。

    会写代码,不一定会做自动化测试

    这就好比,开发不是测试,测试不是开发,测试开发不是开发测试。

    讲真,你需要理解“不能为了自动化而自动化”这句话的真谛。

    自动化测试,本质是一个项目,往大了说,要为公司服务,往小了说,要为你的测试工作服务。

    不是你觉得或者你的领导觉得,大家都在做自动化测试,所以我也要做,你需要量化你的自动化测试效果。举个简单的例子,做完自动化测试,一周能发现一个问题不?一个月能发现一个问题不?节省了多少工时?提升了多少效率?

    如果做了之后,没有发现问题,没有实打实的提升效率,那你做的自动化测试项目,除了在系统稳定性层面有保障外,带来的价值并不高。至少说,你在向上或者部门汇报时,没啥亮点,你的自动化项目很容易被砍。

    实战说明

    起初,我想尽可能的多写、尽可能的深入,讲讲不同的框架、不同的模式。但后来,我做了一定范围内的调查,发现测试奇谭的大多数读者没做过自动化测试,或者自动化测试的经验不多。

    于是乎,难点以后再聊,我不打算在这部分深入,本次实战教程的受众群体是:自动化测试经验不足的新手

    这是一套快速入门的教程。我将尽可能的用最简单的语言带大家快速入门。

    最后,讲讲新手入门的有三个关键点:

    1、编码,优先选Python(语法简单易学)

    2、框架,优先选unittest(简单,容易理解)

    3、业务,优先选接口自动化(相比UI、APP自动化简单)

    故,本次实战,是基于Python的unittest库做接口自动化测试

    最后为方便大家学习测试,特意给大家准备了一份13G的超实用干货学习资源,涉及的内容非常全面。
    在这里插入图片描述
    包括,软件学习路线图,50多天的上课视频、16个突击实战项目,80余个软件测试用软件,37份测试文档,70个软件测试相关问题,40篇测试经验级文章,上千份测试真题分享,还有2021软件测试面试宝典,还有软件测试求职的各类精选简历,希望对大家有所帮助……

    关注我公众号:【程序员二黑】即可获取这份资料了!

    更多相关内容
  • Web-UI自动化测试实战

    2021-09-12 09:44:38
    自动化测试实战前言一、环境搭建1.安装Apache2.安装Mysql3.下载jpress4.集成项目二、编写自动化脚本1.需求分析2.用例设计3.编写代码三、执行自动化脚本四、生成自动化脚本报告 前言 借助开源的项目驱动Web自动化...

    前言

    • 借助开源的项目驱动Web自动化测试
    • 有参考极客间里面郭老师的内容
    • 欢迎加入python知识星球(附python web自动化教程)

    一、环境搭建

    • 项目架构
      • Windows+Apache+Mysql+jpress

    1.安装Apache

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述在这里插入图片描述
    在这里插入图片描述检验是否安装成功
    浏览器输入127.0.0.1:8080 出现下面界面安装成功

    在这里插入图片描述

    2.安装Mysql

    3.下载jpress

    4.集成项目

    1. 将jpress文件源码放置Apache Tomcat webapps文件下
      在这里插入图片描述2.确保Mysql服务在运行并启动Apache服务器,浏览器进入http://127.0.0.1:8080/jpress/install
      在这里插入图片描述
      在这里插入图片描述在这里插入图片描述
      前台地址:http://127.0.0.1:8080/jpress
      用户登录地址:http://127.0.0.1:8080/jpress/user/login
      管理员登录地址:http://127.0.0.1:8080/jpress/admin/login

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    二、编写自动化脚本

    1.需求分析

    • 本项目测试的是一个开源的Java项目,测试的功能有
      • 用户注册: http://127.0.0.1:8080/jpress/user/register
      • 用户登录 : http://127.0.0.1:8080/jpress/user/login
      • 后台管理员登录: http://127.0.0.1:8080/jpress/admin/login
      • 文章分类:http://127.0.0.1:8080/jpress/admin/article/category
      • 添加文章:http://127.0.0.1:8080/jpress/admin/article/write
    • 本项目通过自动化测试的方法来验证,用户注册、用户登录、管理员登录、添加文章分类、添加文章、信息完整性、正确性。

    2.用例设计

    描述步骤预期结果
    验证有效的账号、邮箱、密码、确认密码、验证码进行注册登录1.输入有效账号(wang)
    2.输入有效邮箱(380590045@qq.com)
    3.输入密码(w1234567w)
    4.输入确认密码(w1234567w)
    5.输入与之匹配的验证码
    6.点击注册
    注册成功,点击确认登录
    验证无效的账号,有效的邮箱、密码、确认密码、验证码进行注册登录1.用户为空
    2.输入有效邮箱(380590045@qq.com)
    3.输入密码(w1234567w)
    4.输入确认密码(w1234567w)
    5.输入与之匹配的验证码
    6.点击注册
    用户名不能为空
    验证无效的账号,有效的邮箱、密码、确认密码、验证码进行注册登录1.用户名重复
    2.输入有效邮箱(380590045@qq.com)
    3.输入密码(w1234567w)
    4.输入确认密码(w1234567w)
    5.输入与之匹配的验证码
    6.点击注册
    该用户名已经存在
    验证有效的账号,无效的邮箱,有效的密码、确认密码、验证码进行注册登录1.输入有效账号(zhang)
    2.输入无效邮箱(123@qq.com)
    3.输入密码(w1234567w)
    4.输入确认密码(w1234567w)
    5.输入与之匹配的验证码
    6.点击注册
    邮箱不正确
    验证有效的账号,无效的邮箱,有效的密码、确认密码、验证码进行注册登录1.输入有效账号(zhang)
    2.输入无效邮箱重复(380590045@qq.com)
    3.输入密码(w1234567w)
    4.输入确认密码(w1234567w)
    5.输入与之匹配的验证码
    6.点击注册
    该邮箱已经注册
    验证有效的账号、邮箱,无效的密码,有效的确认密码、验证码进行注册登录1.输入有效账号(zhang)
    2.输入有效邮箱(3805900045@qq.com)
    3.输入密码(1)
    4.输入确认密码(1)
    5.输入与之匹配的验证码
    6.点击注册
    请输入6-16位字母、数字组成的密码
    验证有效的账号、邮箱、密码,无效的确认密码,有效的验证码进行注册登录1.输入有效账号(zhang)
    2.输入有效邮箱(3805900045@qq.com)
    3.输入密码(w123456w)
    4.输入确认密码(1)
    5.输入与之匹配的验证码
    6.点击注册
    确认密码与密码不一致,请重新输入
    验证有效的账号、邮箱、密码、确认密码,无效的验证码进行注册登录1.输入有效账号(zhang)
    2.输入有效邮箱(3805900045@qq.com)
    3.输入密码(w123456w)
    4.输入确认密码(w123456w)
    5.输入验证码1234
    6.点击注册
    验证码不正确
    验证在展示页评论有效内容填写和匹配验证码进行提交1.点击文章(test)
    2.评论栏输入123456
    3.输入验证码
    4.点击提交
    评论直接显示下方
    验证在展示页评论无效内容填写和匹配验证码进行提交1.点击文章(test)
    2.评论栏为空
    3.输入验证码
    4.点击提交
    评论失败:评论内容不能为空
    验证在展示页评论有效内容填写,无效验证码进行提交1.点击文章(test)
    2.评论栏输入123456
    3.输入验证码1234
    4.点击提交
    评论失败:验证码错误
    验证文章中投稿输入有效标题和内容进行保存投稿1.输入标题testdemo
    2.输入内容 this is testdemo
    3.点击保存并投稿
    文章保存成功
    验证文章中投稿输入无效标题,有效内容进行保存投稿1.标题为空
    2.输入内容 this is testdemo
    3.点击保存并投稿
    操作失败,标题不能为空
    验证文章中投稿输入有效标题,无效内容进行保存投稿1.标题为testdemo
    2.输入内容为空
    3.点击保存并投稿
    操作失败,文章内容不能为空
    验证有效旧密码、输入有效密码、确认密码进行密码修改1.输入有效旧密码admin
    2.输入新密码admin
    3.输入确认密码admin
    修改成功
    验证无效旧密码、输入有效密码、确认密码进行密码修改1.输入有效旧密码admin1
    2.输入新密码admin1
    3.输入确认密码admin
    密码错误
    验证有效旧密码、输入有效密码、无效确认密码进行密码修改1.输入有效旧密码admin
    2.输入新密码admin1
    3.输入确认密码admin
    两次出入密码不一致
    验证删除单个文章1.勾选文章框
    2.点击批量删除
    3.点击确定
    文章数量少1
    验证删除多个文章1.勾选文章框
    2.点击批量删除
    3.点击确定
    文章数为0

    3.编写代码

    • 项目架构设计
      • Lib 第三方库
        • chaojiying.py(超级鹰图片处理平台)
      • Reports 测试报告
      • Screenshots 截屏
      • Testcases 测试用例
        • both_link(存放关联登录后的操作测试用例)
          在这里插入图片描述
        • no_link_login(不需要关联登录的操作的测试用例)
          在这里插入图片描述
      • main.py
    #chaojiying.py
    
    
    #!/usr/bin/env python
    # coding:utf-8
    
    import requests
    from hashlib import md5
    
    class Chaojiying_Client(object):
    
        def __init__(self, username, password, soft_id):
                self.username = username
                password =  password.encode('utf8')
                self.password = md5(password).hexdigest()
                self.soft_id = soft_id
                self.base_params = {
                    'user': self.username,
                    'pass2': self.password,
                    'softid': self.soft_id,
                }
                self.headers = {
                    'Connection': 'Keep-Alive',
                    'User-Agent': 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)',
                }
    
        def PostPic(self, im, codetype):
                """
                im: 图片字节
                codetype: 题目类型 参考 http://www.chaojiying.com/price.html
                """
                params = {
                    'codetype': codetype,
                }
                params.update(self.base_params)
                files = {'userfile': ('ccc.jpg', im)}
                r = requests.post('http://upload.chaojiying.net/Upload/Processing.php', data=params, files=files, headers=self.headers)
                return r.json()
    
        def ReportError(self, im_id):
                """
                im_id:报错题目的图片ID
                """
                params = {
                    'id': im_id,
                }
                params.update(self.base_params)
                r = requests.post('http://upload.chaojiying.net/Upload/ReportError.php', data=params, headers=self.headers)
                return r.json()
    
    
    if __name__ == '__main__':
        chaojiying = Chaojiying_Client('超级鹰用户名', '超级鹰用户名的密码', '96001')  # 用户中心>>软件ID 生成一个替换 96001
        im = open('a.jpg', 'rb').read()  # 本地图片文件路径 来替换 a.jpg 有时WIN系统须要//
        print(chaojiying.PostPic(im, 1902))
    #1902 验证码类型  官方网站>>价格体系 3.4+版 print 后要加()
    
    
    #both_link   
    #test_commitcomment.py
    import unittest,time
    from testcases.no_link_login import test_login_user
    from testcases.link_login import  test_commitpl
    
    
    class Test_commitcomment(unittest.TestCase):
    
        def test_commit_success(self):
            userLogincase = test_login_user.Test_login('test_success')
            changpwd = test_commitpl.Test_commit('test_commitsuccess', userLogincase)
    
            suite = unittest.TestSuite()
            suite.addTest(userLogincase)
            suite.addTest(changpwd)
            runner = unittest.TextTestRunner().run(suite)
    
        def test_commit_fail1(self):
            userLogincase = test_login_user.Test_login('test_success')
            changpwd = test_commitpl.Test_commit('test_commitfail1', userLogincase)
    
            suite = unittest.TestSuite()
            suite.addTest(userLogincase)
            suite.addTest(changpwd)
            runner = unittest.TextTestRunner().run(suite)
    
        def test_commit_fail2(self):
            userLogincase = test_login_user.Test_login('test_success')
            changpwd = test_commitpl.Test_commit('test_commitfail2', userLogincase)
    
            suite = unittest.TestSuite()
            suite.addTest(userLogincase)
            suite.addTest(changpwd)
            runner = unittest.TextTestRunner().run(suite)
    
    if __name__ == '__main__':
        unittest.main()
    
    #no_link_login
    #test_login_user.py
    
    from  selenium import  webdriver
    import  unittest
    from lib.make_UserPwd import Create_username
    from  time import  sleep
    
    class Test_login(unittest.TestCase):
    
        @classmethod
        def setUpClass(cls) -> None:
            #浏览器初始化
            cls.driver=webdriver.Chrome()
            #url
            cls.driver.get('http://127.0.0.1:8080/jpress/user/login')
            #隐式等待
            cls.driver.implicitly_wait(2)
            #全屏
            cls.driver.maximize_window()
    
        #登录成功
        def test_success(self):
            #记录清除
            self.driver.find_element_by_name('user').clear()
            #账号定位
            name='admin'
            self.driver.find_element_by_name('user').send_keys(name)
            #密码框定位
            self.driver.find_element_by_name('pwd').clear()
            self.driver.find_element_by_name('pwd').send_keys('admin')
            #点击登录
            self.driver.find_element_by_css_selector('body > div > div > div > form > div.row > div > button').click()
    
            #断言
            try:
                excepted="您好 {} , 欢迎回来".format(name)
                result=self.driver.find_element_by_css_selector('body > div > header > nav > div > ul > li.dropdown.user.user-menu > a > span')
                self.assertEqual(result.text,result)
            except BaseException as e:
                print(e)
    
    
    
        #登录失败,用户名不正确
        def test_fail(self):
            #随机生成用户名
            username=Create_username()
            #定位账号输入
            self.driver.find_element_by_name('user').clear()
            self.driver.find_element_by_name('user').send_keys(username)
    
            #密码框定位
            self.driver.find_element_by_name('pwd').clear()
            self.driver.find_element_by_name('pwd').send_keys('admin')
            #点击登录
            self.driver.find_element_by_css_selector('body > div > div > div > form > div.row > div > button').click()
            #强制休息2s
            sleep(2)
            #断言
            try:
                excepted="用户名不正确。"
                result=self.driver.switch_to.alert
                self.assertEqual(result.text,excepted)
            except BaseException as e:
                print(e)
    
        #登录失败,用户名或密码不正确
        def test_fail2(self):
    
    
            username = "admin"
            # 定位账号输入
            self.driver.find_element_by_name('user').clear()
            self.driver.find_element_by_name('user').send_keys(username)
            #随机生成密码
            pwd=Create_username()
            # 密码框定位
            self.driver.find_element_by_name('pwd').clear()
            self.driver.find_element_by_name('pwd').send_keys(pwd)
            # 点击登录
            self.driver.find_element_by_css_selector('body > div > div > div > form > div.row > div > button').click()
            # 强制休息2s
            sleep(2)
            # 断言
            try:
                excepted ="用户名或密码不正确"
                result = self.driver.switch_to.alert
                self.assertEqual(result.text, excepted)
            except BaseException as e:
                print(e)
    
    
        #登录失败,账号不能为空
        def test_fail3(self):
    
            username = ""
            # 定位账号输入
            self.driver.find_element_by_name('user').clear()
            self.driver.find_element_by_name('user').send_keys(username)
            # 随机生成密码
            pwd = Create_username()
            # 密码框定位
            self.driver.find_element_by_name('pwd').clear()
            self.driver.find_element_by_name('pwd').send_keys(pwd)
            # 点击登录
            self.driver.find_element_by_css_selector('body > div > div > div > form > div.row > div > button').click()
            # 强制休息2s
            sleep(2)
            # 断言
            try:
                excepted = "账号不能为空"
                result = self.driver.switch_to.alert
                self.assertEqual(result.text, excepted)
            except BaseException as e:
                print(e)
    
    
    
    
    
    
        #登录失败,密码不能为空
        def test_fail4(self):
            username = "admin"
            # 定位账号输入
            self.driver.find_element_by_name('user').clear()
            self.driver.find_element_by_name('user').send_keys(username)
            # 随机生成密码
            pwd = Create_username()
            # 密码框定位
            self.driver.find_element_by_name('pwd').clear()
            self.driver.find_element_by_name('pwd').send_keys(pwd)
            # 点击登录
            self.driver.find_element_by_css_selector('body > div > div > div > form > div.row > div > button').click()
            # 强制休息2s
            sleep(2)
            # 断言
            try:
                excepted = "密码不能为空"
                result = self.driver.switch_to.alert
                self.assertEqual(result.text, excepted)
            except BaseException as e:
                print(e)
    
    
    if __name__ == '__main__':
       unittest.main()
    

    -这里就贴部分代码,所有的代码将会在Python知识星球分享(一个Python自学知识分享的平台,您的加入更多的是分享与互动)
    请添加图片描述

    • 代码的需要优化处
      • 关联登录的操作出现重复代码
      • 没有写log日志
      • 没有做数据驱动
      • 生成测试报告会出现两处地方
      • 部分代码没有做备注

    三、执行自动化脚本

    #main.py
    import unittest,time
    from unittestreport  import  TestRunner
    
    
    
    if __name__ == '__main__':
        #调用unittest中defaultTestLoader中discover方法
        # dis=unittest.defaultTestLoader.discover('./testcases/no_login',pattern='test_*.py')
        dis=unittest.defaultTestLoader.discover('./testcases/both_link/',pattern='test*.py')
    
        #时间戳
        nowtime = time.strftime("%Y_%m_%d %H_%M_%S")
        file_name =  nowtime + 'report.html'
    
        with open(file_name, 'wb') as f:
            # 实例化
            TestRunner(dis, filename=file_name, title="测试用例报告", tester="lw").run(3)
    
    

    四、生成自动化脚本报告

    在这里插入图片描述在这里插入图片描述

    展开全文
  • 项目实施UI自动化测试的时机:一般情况下在手工测试完成之后,版本或项目功能趋于稳定 项目需要回归测试(甲⽅⾃营项⽬、⾦融、电商) 项目需要实现自动化的功能模块需求变更不频繁:稳定的模块 项目周期要长:(甲...

    目标

    1.掌握使用Selenium实现批量企业级执行测试用例
    2.熟悉自动化测试的流程
    3.能够对Web项目和APP项目实现自动化测试
    4、掌握如何在Jenkins中配置并运行测试脚本 - 持续集成 CI (Continuous integration)  / CD

    项目实施

    要实现的业务:1、app下单流程   2、web发货业务

    实现技术:python+appium+selenium + unittest

    结构实现:po模式 +日志+数据驱动+报告

    执行方式:持续集成 

    自动化测试流程 - 熟悉

    1、需求分析

    2、挑选适合做自动化测试的功能

    3、设计测试用例

    4、搭建测试环境 【可选】

    5、设计自动化测试项目的架构 【可选】

    6、编写代码

    7、执行测试用例

    8、生成测试报告并分析结果

    编写自动化测试用例的原则:

    1. 自动化测试用例一般只实现核心业务流程或者重复执行率较高的功能
    2. 自动化测试用例的选择一般以“正向”逻辑的验证为主
    3. 不是所有手工用例都可以使用自动化测试用例来执行
    4. 尽量减少多个脚本之间的依赖
    5. 自动化测试用例执行完毕之后,一般需要回归原点

    项目实施UI自动化测试的时机: 一般情况下在手工测试完成之后     版本或项目功能趋于稳定

    项目需要实施UI自动化测试需要满足以下前置条件:

    1. 项目需要回归测试 (甲⽅⾃营项⽬、⾦融、电商)
    2. 项目需要实现自动化的功能模块需求变更不频繁 :稳定的模块
    3. 项目周期要长:(甲⽅⾃营项⽬、6个⽉以上的外包)

    项目依赖包

    1、安装Selenium 包

    2、安装 Appium-Python-Client 包

    3、移动端自动化环境搭建

    UI自动化测试--- 移动端自动化环境搭建 - (6)

    4、安装 parameterized  参数化

    5、安装  HTMLTestReport  生成测试报告

    结构搭建

    1、新建项目   

    项目名称:uiAutoTestTpshop

    2、创建目录结构

                         

    base:所有page页面公共方法

    data:测试数据存储文件

    img:存放截图图片文件

    log:日志

    page:页面对象

    report:测试报告

    script:测试脚本

    config.py:配置文件

    run_suite.py:运行套件

    utils.py:工具类

    3、实现config.py 配置文件

    import os
    # web后台url
    HOST = 'http://hmshop-test.itheima.net/index.php/Admin/Admin/login'
    # 项目路径
    DIR_PATH = os.path.dirname(__file__)
    print(DIR_PATH)

    4、实现base.py文件

    import os
    import time
    from selenium.webdriver.support.wait import WebDriverWait
    
    from config import DIR_PATH
    
    
    class Base:
        # 初始化方法
        def __init__(self, driver):
            self.driver = driver
    
        # 查找元素方法
        def base_find(self, loc, timeout=10, poll_frequency=0.5):
            return WebDriverWait(self.driver, timeout, poll_frequency).until(lambda x: x.find_element(*loc))
    
        # 点击方法
        def base_click(self, loc):
            self.base_find(loc).click()
    
        # 输入方法
        def base_input(self, loc, value):
            # 获取元素
            el = self.base_find(loc)
            # 清空
            el.clear()
            # 输入
            el.send_keys(value)
    
        # 获取文本方法
        def base_get_text(self, loc):
            return self.base_find(loc).text
    
        # 截图方法
        def base_screenshot_img(self):
            img_path = DIR_PATH + os.sep + 'img' + os.sep + '{}.png'.format(time.strftime('%Y%m%d %H%M%S'))
            return self.driver.get_screen_as_file(img_path)
    

    5、登录业务结构搭建

    app 登录page结构搭建

    """
       将操作步骤封装 + 组合业务方法
    """
    
    
    class PageAppLogin:
    
        # 1、点击 我的
        def page_app_click_me(self):
            pass
    
        # 2、点击 登录头像
        def page_app_click_login_link(self):
            pass
    
        # 3、输入用户名
        def page_app_input_username(self):
            pass
    
        # 4、输入密码
        def page_app_input_password(self):
            pass
    
        # 5、点击勾选协议
        def page_app_click_protocol(self):
            pass
    
        # 6、点击登录按钮
        def page_app_click_login_btn(self):
            pass
    
        # 7、获取登录昵称
        def page_app_get_login_nickname(self):
            pass
    
        # 组合业务
        def page_app_login(self):
            pass
    

    2、登录配置信息整理    page--->    __init__文件

    from selenium.webdriver.common.by import By
    
    """
       一、以下为app登录模块配置信息
    """
    # 我的
    app_login_me = By.XPATH, "//*[@text='我的']"
    # 登录头像
    app_login_icon = By.XPATH, "//*[@resource-id='com.tpshop.malls:id/head_img']"
    # 用户名
    app_username = By.XPATH, "//*[@resource-id='com.tpshop.malls:id/mobile_et']"
    # 密码
    app_pwd = By.XPATH, "//*[@resource-id='com.tpshop.malls:id/pwd_et']"
    # 协议
    app_pro = By.XPATH, "//*[@resource-id='com.tpshop.malls:id/agree_btn']"
    # 登录按钮
    app_login_btn = By.XPATH, "//*[@resource-id='com.tpshop.malls:id/login_tv']"
    # 登录昵称
    app_nickname = By.XPATH, "//*[@resource-id='com.tpshop.malls:id/nick_name_tv']"
    

    3、登录page页面实现

    page_app_login.py 文件实现

    """
       将操作步骤封装 + 组合业务方法
    """
    from time import sleep
    import page
    from base.base import Base
    
    
    class PageAppLogin(Base):
    
        # 1、点击 我的
        def page_app_click_me(self):
            self.base_click(page.app_login_me)
    
        # 2、点击 登录头像
        def page_app_click_login_link(self):
            self.base_click(page.app_login_icon)
    
        # 3、输入用户名
        def page_app_input_username(self, username):
            self.base_input(page.app_username, username)
    
        # 4、输入密码
        def page_app_input_password(self, pwd):
            self.base_input(page.app_pwd, pwd)
    
        # 5、点击勾选协议
        def page_app_click_protocol(self):
            self.base_click(page.app_pro)
    
        # 6、点击登录按钮
        def page_app_click_login_btn(self):
            self.base_click(page.app_login_btn)
    
        # 7、获取登录昵称
        def page_app_get_login_nickname(self):
            sleep(2)
            return self.base_get_text(page.app_nickname)
    
        # 组合业务
        def page_app_login(self, username=15826331234, pwd=123456):
            self.page_app_click_me()
            self.page_app_click_login_link()
            self.page_app_input_username(username)
            self.page_app_input_password(pwd)
            self.page_app_click_protocol()
            self.page_app_click_login_btn()
    

    # 提示:获取昵称需要加个强制等待时间   sleep(2)

    原因:查找到元素之后,页面内容还没刷新,没有显示出来,可能会出现  昵称为 “优惠券”的bug

    4、登录测试脚本实现     

    import unittest
    from utils import GetDriver
    from page.page_app_login import PageAppLogin
    
    
    class TestAppLoginOrder(unittest.TestCase):
        def setUp(self):
            self.driver = GetDriver.get_app_driver()
            # 获取PageAppLogin实例
            self.app = PageAppLogin(self.driver)
    
        def tearDown(self):
            self.driver.quit()
    
        def test01_login(self):
            """登录测试用例"""
            # 调用登录
            self.app.page_app_login()
            # 获取昵称  --> 断言
            nickname = self.app.page_app_get_login_nickname()
            print('登录账户昵称为:', nickname)
    
    

    6、订单业务

    1、订单page业务结构搭建  

    from base.base import Base
    
    
    class PageAppOrder(Base):
        # 说明:app项目环境的问题,本来应该通过点击首页搜索,但是测试环境中  首页 没有元素,故切换到分类选项卡中搜索,原理是一样的
        # 1、点击分类
        def page_app_click_cate(self):
            pass
    
        # 2、点击搜索框
        def page_app_click_search_text(self):
            pass
    
        # 3、输入搜索内容  小米
        def page_app_input_search_text(self):
            pass
    
        # 4、点击搜索按钮
        def page_app_click_search_btn(self):
            pass
    
        # 5、选择商品
        def page_app_choose_goods(self):
            pass
    
        # 6、加入购物车
        def page_app_add_cart(self):
            pass
    
        # 7、点击确定
        def page_app_click_sure(self):
            pass
    
        # 8、点击购物车
        def page_app_click_goodscart(self):
            pass
    
        # 9、点击立即购买
        def page_app_now_buy(self):
            pass
    
        # 10、点击提交订单
        def page_app_click_submit_order(self):
            pass
    
        # 11、点击立即支付
        def page_app_now_click_now_pay_money(self):
            pass
    
        # 12、输入支付密码   123456
        def page_app_input_pay_pwd(self):
            pass
    
        # 13、点击确定
        def page_app_pay_sure(self):
            pass
    
        # 14、获取订单编号
        def page_app_get_ordernum(self):
            pass
    
        # 15、下单业务方法
        def page_app_order(self):
            pass
    

    2、app订单配置信息整理

    """
       二、以下为app订单模块配置信息
    """
    
    # 1、点击首页
    app_order_index = By.XPATH, "//*[@text='分类']"
    # 2、点击搜索框1
    app_order_search_text1 = By.XPATH, "//*[@class='android.widget.EditText']"
    # 点击搜索框2
    app_order_search_text2 = By.XPATH, "//*[@class='android.widget.EditText']"
    # 3、输入搜索内容
    # app_order_sarch_content =
    # 4、点击搜索按钮
    app_order_search_btn = By.XPATH, "//*[@resource-id='com.tpshop.malls:id/search_btn']"
    # 5、点击选择第一张图片的商品
    app_choose_img = By.XPATH, "//*[@resource-id='com.tpshop.malls:id/product_pic_img']"
    # 6、点击加入购物车
    app_order_add_cart = By.XPATH, "//*[@text='加入购物车']"
    # 7、点击确定
    app_order_cart_ok = By.XPATH, "//*[@text='确定']"
    # 8、点击购物车
    app_order_cart = By.XPATH, "//*[@text='购物车']"
    # 9、点击 立即购买
    app_order_cart_now_purchase = By.XPATH, "//*[@text='立即购买']"
    # 10、点击 提交订单
    app_order_submit = By.XPATH, "//*[@text='提交订单']"
    # 11、点击 立即支付
    app_order_now_pay = By.XPATH, "//*[@text='立即支付']"
    # 12、输入支付密码
    app_order_pay_pwd = By.XPATH, "//*[@resource-id='com.tpshop.malls:id/pwd_et']"
    # 13、点击 确定
    app_order_pay_ok = By.XPATH, "//*[@text='确定']"
    # 14、获取订单编号
    app_order_no = By.XPATH, "//*[@resource-id='com.tpshop.malls:id/pay_trade_no_tv']"
    

    注意:在base.py文件加上: print('-->正在查找:{}'.format(loc)) 代码,方便调试输出到控制台,运行时最右边可以与模拟器两相对照

      # 查找元素方法
        def base_find(self, loc, timeout=10, poll_frequency=0.5):
            print('-->正在查找:{}'.format(loc))
            return WebDriverWait(self.driver, timeout, poll_frequency).until(lambda x: x.find_element(*loc))
    

    3、订单page页面实现

    from time import sleep
    
    from base.base import Base
    import page
    
    
    class PageAppOrder(Base):
        # 1、点击分类
        def page_app_click_cate(self):
            sleep(2)
            self.base_click(page.app_order_index)
    
        # 2、点击搜索框
        def page_app_click_search_text(self):
            sleep(1)
            self.base_click(page.app_order_search_text1)
    
        # 3、输入搜索内容  小米
        def page_app_input_search_text(self, value='小米'):
            sleep(1)
            self.base_input(page.app_order_search_text1, value)
    
        # 4、点击搜索按钮
        def page_app_click_search_btn(self):
            sleep(1)
            self.base_click(page.app_order_search_btn)
    
        # 5、选择商品
        def page_app_choose_goods(self):
            sleep(1)
            self.base_click(page.app_choose_img)
    
        # 6、加入购物车
        def page_app_add_cart(self):
            sleep(1)
            self.base_click(page.app_order_add_cart)
    
        # 7、点击确定
        def page_app_click_sure(self):
            sleep(1)
            self.base_click(page.app_order_cart_ok)
    
        # 8、点击购物车
        def page_app_click_goodscart(self):
            sleep(1)
            self.base_click(page.app_order_cart)
    
        # 9、点击立即购买
        def page_app_now_purchase(self):
            sleep(1)
            self.base_click(page.app_order_cart_now_purchase)
    
        # 10、点击提交订单
        def page_app_click_submit_order(self):
            sleep(1)
            self.base_click(page.app_order_submit)
    
        # 11、点击立即支付
        def page_app_now_click_now_pay_money(self):
            sleep(1)
            self.base_click(page.app_order_now_pay)
    
        # 12、输入支付密码   123456
        def page_app_input_pay_pwd(self, pwd='123456'):
            sleep(1)
            self.base_input(page.app_order_pay_pwd, pwd)
    
        # 13、点击确定
        def page_app_pay_sure(self):
            sleep(1)
            self.base_click(page.app_order_pay_ok)
    
        # 14、获取订单编号
        def page_app_get_order_no(self):
            sleep(2)
            return self.base_get_text(page.app_order_no)
    
        # 15、下单业务方法
        def page_app_order(self, value='小米', pwd='1233456'):
            self.page_app_click_cate()
            self.page_app_click_search_text()
            self.page_app_input_search_text(value)
            self.page_app_click_search_btn()
            self.page_app_choose_goods()
            self.page_app_add_cart()
            self.page_app_click_sure()
            self.page_app_click_goodscart()
            self.page_app_now_purchase()
            self.page_app_click_submit_order()
            self.page_app_now_click_now_pay_money()
            self.page_app_input_pay_pwd(pwd)
            self.page_app_pay_sure()
    

    4、订单script 测试脚本实现

    import unittest
    
    from page.page_app_order import PageAppOrder
    from utils import GetDriver
    from page.page_app_login import PageAppLogin
    
    
    class TestAppLoginOrder(unittest.TestCase):
        def setUp(self):
            self.driver = GetDriver.get_app_driver()
            # 获取PageAppLogin实例
            self.app = PageAppLogin(self.driver)
            # 获取PageAppOrder实例
            self.order = PageAppOrder(self.driver)
    
        def tearDown(self):
            self.driver.quit()
    
        def test01_login_order(self, search_value='小米', pwd='123456'):
            """登录测试用例"""
            # 1、调用登录
            self.app.page_app_login()
            # 获取昵称  --> 断言
            nickname = self.app.page_app_get_login_nickname()
            print('登录账户昵称为:', nickname)
            # 2、下订单
            self.order.page_app_order(search_value, pwd)
            # 获取订单编号
            order_no = self.order.page_app_get_order_no()
            print('获取的订单编号为:', order_no)
    

    不完善之处记录:

     思考:第一次运行时,为什么会出现“请先安装QQ”的错误提示?

    解决方案:第一次运行时需要手动加入购物车

    报错:获取的订单编号为:None

    原因:在获取订单编号方法一定要返回 return  ,获取文本值一定要返回,后续做断言用

        # 14、获取订单编号
        def page_app_get_order_no(self):
            sleep(2)
            # 注意:一定要返回
            return self.base_get_text(page.app_order_no)

    断言和参数化

    在data目录下新增 app_order.json文件 文件代码如下:

    {
      "order": [
        {
          "desc": "下单成功",
          "search_value": "小米",
          "pwd": "123456",
          "expect": "13600001111"
        }
      ]
    }

     在utils.py文件中新增方法:

    # 读取json文件方法
    def read_json(filename, key):
        filepath = DIR_PATH + os.sep + "data" + os.sep + filename
        arrs = []
        with open(filepath, encoding='utf-8') as f:
            for data in json.load(f).get(key):
                arrs.append(tuple(data.values())[1:])
    
        return arrs

    参数化:在   test01_app_from_login_to_orde.py  文件中新增以下代码:

    from parameterized import parameterized
    
     @parameterized.expand(read_json('app_order.json', 'order'))
        def test01_login_order(self, search_value, pwd, expect):
            try:
                """登录测试用例"""
                # 1、调用登录
                self.app.page_app_login()
                # 获取昵称  --> 断言
                nickname = self.app.page_app_get_login_nickname()
                print('登录账户昵称为:', nickname)
                self.assertEqual(expect, nickname)
                # 2、下订单
                self.order.page_app_order(search_value, pwd)
                # 获取订单编号
                order_no = self.order.page_app_get_order_no()
                print('获取的订单编号为:', order_no)
            except Exception as e:
                log.error(e)
                # 1、截图
                self.order.base_screenshot_img()
                # 2、抛异常
                raise
    

    7、web端登录业务

    1、web登录配置信息整理    page--->  __init__文件:

    """
       三、以下为web登录模块配置信息
    """
    # 用户名
    web_username = By.CSS_SELECTOR, '[name="username"]'
    # 密码
    web_pwd = By.CSS_SELECTOR, '[name="password"]'
    # 验证码
    web_verify = By.CSS_SELECTOR, '[name="vertify"]'
    # 登录按钮
    web_login_btn = By.CSS_SELECTOR, '[name="submit"]'
    # 登录昵称
    web_login_nickname = By.CSS_SELECTOR, '.bgdopa-t'

    2、page_web_login.py文件实现:

    import page
    from base.base import Base
    
    
    class PageWebLogin(Base):
        # 1、输入用户名
        def page_web_input_username(self, uname):
            self.base_input(page.web_username, uname)
    
        # 2、输入密码
        def page_web_input_pwd(self, pwd):
            self.base_input(page.web_pwd, pwd)
    
        # 3、输入验证码
        def page_web_input_verify(self, code):
            self.base_input(page.web_verify, code)
    
        # 3、点击登录按钮
        def page_web_click_login_btn(self):
            self.base_click(page.web_login_btn)
    
        # 4、获取登录昵称
        def page_web_get_login_nickname(self):
            return self.base_get_text(page.web_login_nickname)
    
        # 5、登录业务方法
        def page_web_login(self, uname='admin', pwd='123456', code='8888'):
            self.page_web_input_username(uname)
            self.page_web_input_pwd(pwd)
            self.page_web_input_verify(code)
            self.page_web_click_login_btn()
    

    3、web端测试脚本实现

    script目录下----> 新建 test01_web_from_login_to_order.py 文件

    import unittest
    from time import sleep
    
    from page.page_web_login import PageWebLogin
    from utils import GetDriver
    
    
    class TestWebLoginOrder(unittest.TestCase):
        def setUp(self):
            self.driver = GetDriver.get_web_driver()
            # 获取PageWebLogin实例
            self.web_login = PageWebLogin(self.driver)
    
        def tearDown(self):
            sleep(3)
            self.driver.quit()
    
        def test01_web_login_order(self):
            # 调用登录
            self.web_login.page_web_login()
            # 获取登录昵称
            nickname = self.web_login.page_web_get_login_nickname()
            print('登录后的昵称为:', nickname)
    

    8、web端发货业务

    说明:由于web发货页面需要切换iframe标签,所以在Base类中新增两个方法:

    1、切换frame

    2、回到默认目录

        # 切换frame
        def base_switch_to_frame(self, loc):
            log.info('正在调用切换frame方法,切换对象:{}'.format(loc))
            el = self.base_find(loc)
            self.driver.switch_to.frame(el)
    
        # 恢复frame
        def base_swtich_to_default_content(self):
            self.driver.switch_to.default_content()
    

    1、web登录配置信息整理    page--->  __init__文件:

    """
       四、以下为web后台发货模块配置信息
    """
    # 1、点击 订单 菜单
    web_order = By.XPATH, "//a[text()='订单']"
    # 2、点击左侧菜单 发货单
    web_order_goods_list = By.XPATH, "//a[text()='发货单']"
    # 切换frame
    web_order_frame_workspace = By.CSS_SELECTOR, '#workspace'
    # 3、点击去发货
    # web_order_go_goods = By.XPATH, "//a[text()='去发货']"
    # 指定编号发货      //div[text()=202206222103597720]/../..//td[@class="handle"]//a[1]
    web_order_go_goods = "//div[text()='{}']/../..//td[@class='handle']//a[1]"
    # 4、勾选物流公司  韵达
    web_order_company = By.CSS_SELECTOR, '[value="YD"]'
    # 5、输入配送单号  [name="invoice_no"]
    web_order_goods_no = By.CSS_SELECTOR, '[name="invoice_no"]'
    # 6、点击确认发货
    web_order_ok = By.CSS_SELECTOR, '.ncap-btn-send'
    # 7、打印配货单单
    web_order_print_order = By.XPATH, "//a[text()='打印配货单']"
    # 8、获取订单编号
    web_order_no = By.XPATH, "//div[@id='printDiv']/div[@class='contact-info']/dl[1]/dd[2]"
    
    # 难点:如何根据指定的订单编号,查找对应的去发货元素
    # 思路:先找共同的父级,再逐级查找
    

    2、  utils.py文件   完整文件代码:

    import json
    import os
    import appium.webdriver
    from config import DIR_PATH, HOST
    from selenium import webdriver
    import logging.handlers
    
    
    class GetDriver:
        __app_driver = None
        __web_driver = None
    
        # 获取APP Driver
        @classmethod
        def get_app_driver(cls):
            if cls.__app_driver is None:
                # 定义字典变量
                desired_caps = dict()
    
                # 字典追加启动参数
                desired_caps["platformName"] = "Android"
                # 注意:版本号必须正确,报错时会提示正确的版本号
                desired_caps["platformVersion"] = " "
                # android不检测内容,但是不能为空
                desired_caps["deviceName"] = "192.168.56.101:5555"
                desired_caps["appPackage"] = "com.tpshop.malls"
                desired_caps["appActivity"] = ".SPMainActivity"
                # 设置中⽂
                desired_caps["unicodeKeyboard"] = True
                desired_caps["resetKeyboard"] = True
                # 获取driver
                cls.__app_driver = appium.webdriver.Remote("http://127.0.0.1:4723/wd/hub", desired_caps)
    
            return cls.__app_driver
    
        # 获取Web Driver
        @classmethod
        def get_web_driver(cls):
            if cls.__web_driver is None:
                cls.__web_driver = webdriver.Chrome()
                cls.__web_driver.get(HOST)
                cls.__web_driver.maximize_window()
    
            return cls.__web_driver
    
    
    # 读取json文件方法
    def read_json(filename, key):
        filepath = DIR_PATH + os.sep + "data" + os.sep + filename
        arrs = []
        with open(filepath, encoding='utf-8') as f:
            for data in json.load(f).get(key):
                arrs.append(tuple(data.values())[1:])
    
        return arrs
    
    
    # 写入动态文件,方便生成的订单后 后续持续用
    def write_json(value):
        filepath = DIR_PATH + os.sep + "data" + os.sep + "expect.json"
        with open(filepath, 'w', encoding='utf-8') as f:
            data = {"expect": [{"desc": "", "order_no": value}]}
            json.dump(data, f)
    
    
    # 日志封装
    class GetLog:
        __log = None
    
        @classmethod
        def get_log(cls):
            if cls.__log is None:
                # 获取日志器
                cls.__log = logging.getLogger()
                # 2、设置日志级别
                cls.__log.setLevel(logging.INFO)
                # 3、获取处理器
                filename = DIR_PATH + os.sep + 'log' + os.sep + 'tpshop_auto.log'
                # filename = DIR_PATH + os.sep + 'log' + os.sep + 'tpshop_auto_{}.log'.format('%Y%m%d %H%M%S')
                tf = logging.handlers.TimedRotatingFileHandler(filename=filename, when='midnight', interval=1,
                                                               backupCount=3, encoding='utf-8')
                # 4、获取格式器
                fmt = "%(asctime)s %(levelname)s [%(filename)s(%(funcName)s:%(lineno)d)] - %(message)s"
                fm = logging.Formatter(fmt)
                # 5、将格式器添加到处理器
                tf.setFormatter(fm)
                # 6、将处理器添加到日志器
                cls.__log.addHandler(tf)
            # 7、返回日志器
            return cls.__log
    
    
    if __name__ == '__main__':
        # 预期:[(),()]
        print(read_json('app_order.json', 'order'))
        GetLog.get_log().info('日志测试')
        # write_json('2022062317250001')
        print(read_json('expect.json', 'expect')[0][0])
    

    3、page_web_order.py 文件实现:

    import time
    
    from selenium.webdriver.common.by import By
    
    import page
    from base.base import Base
    from utils import read_json
    
    
    class PageWebOrder(Base):
        # 1、点击 订单 菜单
        def page_web_click_order_menu(self):
            self.base_click(page.web_order)
    
        # 2、点击左侧菜单 发货单
        def page_web_click_order_list(self):
            self.base_click(page.web_order_goods_list)
    
        # 3、工作区域 点击去发货
        def page_web_go_goods(self):
            # 1、切换frame
            self.base_switch_to_frame(page.web_order_frame_workspace)
            # 2、点击去发货
            loc = By.XPATH, page.web_order_go_goods.format(read_json('expect.json', 'expect')[0][0])
            self.base_click(loc)
    
        # 4、勾选物流公司
        def page_web_order_company(self):
            self.base_click(page.web_order_company)
    
        # 5、输入配送单号
        def page_web_input_goods_no(self):
            # 配送单号
            shipping_no = str(time.strftime('%Y%m%d%H%M%S'))
            self.base_input(page.web_order_goods_no, shipping_no)
    
        # 6、点击确认发货
        def page_web_order_goods_ok(self):
            self.base_click(page.web_order_ok)
    
        # 7、打印配货单
        def page_web_order_print(self):
            self.base_click(page.web_order_print_order)
    
        # 8、获取订单编号
        def page_web_get_order_no(self):
            return self.base_get_text(page.web_order_no)
    
        # 9、发货业务方法
        def page_web_order(self):
            self.page_web_click_order_menu()
            self.page_web_click_order_list()
            self.page_web_go_goods()
            self.page_web_order_company()
            self.page_web_input_goods_no()
            self.page_web_order_goods_ok()
            self.page_web_order_print()
    

    4、web端测试脚本实现

    script目录下----> test01_web_from_login_to_order.py 文件更新为:

    import unittest
    from time import sleep
    
    from base import log
    from page.page_web_login import PageWebLogin
    from page.page_web_order import PageWebOrder
    from utils import GetDriver, read_json
    
    
    class TestWebLoginOrder(unittest.TestCase):
        def setUp(self):
            self.driver = GetDriver.get_web_driver()
            # 获取PageWebLogin实例
            self.web_login = PageWebLogin(self.driver)
            # 获取PageWebOrder实例
            self.web_order = PageWebOrder(self.driver)
    
        def tearDown(self):
            sleep(3)
            self.driver.quit()
    
        def test01_web_login_order(self):
            try:
                # 1、调用登录
                self.web_login.page_web_login()
                # 获取登录昵称
                nickname = self.web_login.page_web_get_login_nickname()
                print('登录后的昵称为:', nickname)
                # 2、调用发货业务方法
                self.web_order.page_web_order()
                # 打印订单编号
                order_no = self.web_order.page_web_get_order_no()
                print('发货的订单编号为:', order_no)
                self.assertIn(order_no, read_json('expect.json', 'expect')[0][0])
                log.info('======> web 后台发货成功,发货的订单编号为:{}'.format(read_json('expect.json', 'expect')[0][0]))
            except Exception as e:
                log.error(e)
                # 1、截图
                self.web_order.base_screenshot_img()
                # 2、抛异常
                raise
    

    4、 更改  page_app_from_login_to_order.py  文件方法

    # 默认返回的订单号格式为:   

    订单编号:202206231816135296   需要切割   [5:]

    难点:动态根据订单号去发货  app下单成功的订单号,就是web端进行发货的订单号,两者对应关系

    如果不是动态根据订单号,就可以用下面的配置信息:

    #      page目录 --->  __init__配置文件
    web_order_go_goods = By.XPATH, "//a[text()='去发货']"  
    
    
    
    #       page目录 --->  page_web_order.py文件   --> PageWebOrder类
     # 3、工作区域 点击去发货
        def page_web_go_goods(self):
            # 1、切换frame
            self.base_switch_to_frame(page.web_order_frame_workspace)
            # 2、点击去发货
            self.base_click(page.web_order_go_goods)

    write_json(order_no[5:])
    log.info('======> app发货成功,发货的订单编号为:{}'.format(read_json('expect.json', 'expect')[0][0]))

      @parameterized.expand(read_json('app_order.json', 'order'))
        def test01_login_order(self, search_value, pwd, expect):
            try:
                """登录测试用例"""
                # 1、调用登录
                self.app.page_app_login()
                # 获取昵称  --> 断言
                nickname = self.app.page_app_get_login_nickname()
                print('登录账户昵称为:', nickname)
                self.assertEqual(expect, nickname)
                # 2、下订单
                self.order.page_app_order(search_value, pwd)
                # 获取订单编号
                order_no = self.order.page_app_get_order_no()
                print('获取的订单编号为:', order_no)
                write_json(order_no[5:])
                log.info('======> app发货成功,发货的订单编号为:{}'.format(read_json('expect.json', 'expect')[0][0]))
            except Exception as e:
                log.error(e)
                # 1、截图
                self.order.base_screenshot_img()
                # 2、抛异常
                raise

    5、批量运行测试用例      run_suite.py 文件完整代码:

    import os
    import unittest
    from htmltestreport import HTMLTestReport
    
    from config import DIR_PATH
    
    suite = unittest.defaultTestLoader.discover('./script')
    file_path = DIR_PATH + os.sep + 'report' + os.sep + 'tpshop_auto_report.html'
    HTMLTestReport(file_path, title='tpshop项目UI自动化测试报告(APP、Web)').run(suite)
    

    9、持续集成

    1、把代码分享到Gitee上,步骤如下:

    1)Pycharm软件直接 点击 VCS - -->Import into Version Control--> Share Project on Gitee

    2)在弹出的对话框中去掉 .idle的勾选,其他自己写的目录和py文件都勾选上,点击 Add 按钮

     分享成功提示:Successfully shared project on Gitee

    2、打开jenkins所在目录,点击地址栏输入cmd打开命令提示符黑窗口

    jenkins目录:D:\jenkins

    执行:java -jar jenkins.war

    java -jar jenkins.war

    3、拷贝Gitee项目地址,点击目标项目仓库,点击“克隆/下载” 地址

    4、打开浏览器输入网址:   http://localhost:8080/,输入用户名和密码,登录jenkins工作台

    5、配置jenkins:   

    1)点击新建Item 

    2)输入一个任务名称:xxx-pro,点击 freestyle project / 构建一个自由风格的软件项目,点击确定

    3) 源码管理--->Git--->     Repository URL:第三步拷贝的Gitee仓库地址  Credentials:添加的证书即邮件

    4)构建触发器 ---> Poll SCM 日程表(配置一分钟触发一次):*/1 * * * *    每分钟轮询

    5)构建---> Excute Windows batch command ---> python run_suite.py

    6) 构建后操作  -->  Publish  HTML Report -->  

    7) 构建后操作  --> Editable Email Notification     ---> 邮箱收信人和HTML报告模板

    HTML报告模板:
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>${ENV, var="JOB_NAME"}-第${BUILD_NUMBER}次构建日志</title>
    </head>
    <body leftmargin="8" marginwidth="0" topmargin="8" marginheight="4" offset="0">
    <div>
    <h2>项目信息</h2>
    <ul>
    <li>项目名称:${PROJECT_NAME}</li>
    <li>详细测试报告:<a href="${PROJECT_URL}HTML_20Report/">${PROJECT_URL}HTML_20Report/</a></li>
    <li>触发原因:${CAUSE}</li>
    <li>项目Url:<a href="${PROJECT_URL}">${P OJECT_URL}</a></li>
    </ul>
    <hr/>
    <h2>构建日志</h2>
    <div>${JELLY_SCRIPT,template="html"}</div>
    <hr/>
    </div>
    </body>
    </html>

    报告样式问题: jenkins 倒三角 系统管理 Manage Jenkins  ---》 脚本执行行  Script Console

     

     代码如下:

    System.setProperty("hudson.model.DirectoryBrowserSupport.CSP", "")

    Jenkins构建出错,一般是由于权限或者环境导致的!!!

    展开全文
  • 废话不多说哈,今天再来说说pytest吧,经过几周的时间学习,有收获也有疑惑,总之最后还是搞个小项目出来证明自己的努力不没有白费环境准备序号库/插件/工具安装命令1确保您已经安装了python3.x2配置python3+pycharm...

    前言

    明天就放假了,4天小长假,是不是很开心!也许很多人要回老家帮家里种地,干农活。其实能陪陪家里人,帮忙干点农活还是挺开心的,希望大家有个愉快的假期!废话不多说哈,今天再来说说pytest吧,经过几周的时间学习,有收获也有疑惑,总之最后还是搞个小项目出来证明自己的努力不没有白费

    环境准备

    序号

    库/插件/工具

    安装命令

    1

    确保您已经安装了python3.x

    2

    配置python3+pycharm+selenium2开发环境

    3

    安装pytest库

    pip install pytest

    4

    安装pytest -html 报告插件

    pip install pytest-html

    5

    安装pypiwin32库(用来模拟按键)

    pip install pypiwin32

    6

    安装openpyxl解析excel文件库

    pip install openpyxl

    7

    安装yagmail发送报告库

    pip install yagmail

    8

    确保已配置火狐或谷歌浏览器及对应驱动

    9

    确保已经正确配置好发送邮件的邮箱

    项目简介

    测试地址

    测试范围

    1.126电子邮箱登录功能测试-验证正确帐号密码登录成功-验证错误用户名密码登录失败(有很多情况,用例里面做了充分的校验)

    2.126电子邮箱添加联系人功能测试-验证正确填写必填项数据添加联系人成功-验证缺省必填项数据添加联系人失败-验证必填项字段数据格式错误添加联系人失败

    3.126电子邮箱发送邮件功能测试-验证普通邮件发送成功-验证带附件邮件发送成功

    项目设计

    1.python编程语言设计测试脚本

    2.webdriver驱动浏览器并操作页面元素

    3.二次封装webdriver Api 操作方法

    4.采用PageObject设计模式,设计测试业务流程

    5.通过UI对象库存储页面操作元素

    6.通过数据文件存储数据,读取数据,参数化测试用例并驱动测试执行

    7.通过第三方插件pytest-html生成测试报告

    8.通过yagmail第三方库,编写发送报告接口,测试工作完成后自动发送测试报告

    代码分析

    目录结构

    8f900a89c6347c561fdf2122f13be562.png

    961ddebeb323a10fe0623af514929fc1.png

    1 PytestAutoTestFrameWork2 |—|config3 |——|__init__.py4 |——|conf.py5 |——|config.ini6 |—|data7 |——|__init__.py8 |——|tcData.xlsx9 |—Page10 |——|PageObject.py11 |———|__init__.py12 |———|ContactPage.py13 |———|HomePage.py14 |———|LoginPage.py15 |———|SendMailPage.py16 |——|__init__.py17 |——|BasePage.py18 |—|report19 |—|TestCases20 |——|__init__.py21 |——|conftest.py22 |——|test_confactCase.py23 |——|test_loginCase.py24 |——|test_sendMailCase.py25 |—|util26 |——|__init__.py27 |——|clipboard.py28 |——|keyboard.py29 |——|parseConFile.py30 |——|parseExcelFile.py31 |——|sendMailForReport.py32 |—|conftest.py33 |—|pytest.ini34 |—|RunTestCase.py

    目录结构

    代码实现

    通过126邮箱测试范围分析,我们需要通过设计剪切板,模拟键盘完成附件上传操作,因此我们首先来编写这两个方法

    8f900a89c6347c561fdf2122f13be562.png

    961ddebeb323a10fe0623af514929fc1.png

    1 """

    2 ------------------------------------3 @Time : 2019/4/15 12:044 @Auth : linux超5 @File : clipboard.py6 @IDE : PyCharm7 @Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!8 ------------------------------------9 """

    10 importwin32con11 importwin32clipboard as WC12

    13

    14 classClipBoard(object):15 '''设置剪切板内容和获取剪切板内容'''

    16

    17 @staticmethod18 defgetText():19 '''获取剪切板的内容'''

    20 WC.OpenClipboard()21 value =WC.GetClipboardData(win32con.CF_TEXT)22 WC.CloseClipboard()23 returnvalue24

    25 @staticmethod26 defsetText(value):27 '''设置剪切板的内容'''

    28 WC.OpenClipboard()29 WC.EmptyClipboard()30 WC.SetClipboardData(win32con.CF_UNICODETEXT, value)31 WC.CloseClipboard()32

    33

    34 if __name__ == '__main__':35 from selenium importwebdriver36

    37 value = 'python'

    38 driver =webdriver.Firefox()39 driver.get('http://www.baidu.com')40 query = driver.find_element_by_id('kw')41 ClipBoard.setText(value)42 clValue =ClipBoard.getText()43 query.send_keys(clValue.decode('utf-8'))

    clipboard.py-操作剪切板

    8f900a89c6347c561fdf2122f13be562.png

    961ddebeb323a10fe0623af514929fc1.png

    1 """

    2 ------------------------------------3 @Time : 2019/4/15 12:054 @Auth : linux超5 @File : keyboard.py6 @IDE : PyCharm7 @Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!8 ------------------------------------9 """

    10

    11 #模拟按键

    12 importwin32api13 importwin32con14 importtime15

    16

    17 classKeyBoard(object):18 """模拟按键"""

    19 #键盘码

    20 vk_code ={21 'enter' : 0x0D,22 'tab' : 0x09,23 'ctrl' : 0x11,24 'v' : 0x56,25 'a' : 0x41,26 'x' : 0x58

    27 }28

    29 @staticmethod30 defkeyDown(key_name):31 """按下键"""

    32 key_name =key_name.lower()33 try:34 win32api.keybd_event(KeyBoard.vk_code[key_name], 0, 0, 0)35 exceptException as e:36 print('未按下enter键')37 print(e)38

    39 @staticmethod40 defkeyUp(key_name):41 """抬起键"""

    42 key_name =key_name.lower()43 win32api.keybd_event(KeyBoard.vk_code[key_name], 0, win32con.KEYEVENTF_KEYUP, 0)44

    45 @staticmethod46 defoneKey(key):47 """模拟单个按键"""

    48 key =key.lower()49 KeyBoard.keyDown(key)50 time.sleep(2)51 KeyBoard.keyUp(key)52

    53 @staticmethod54 deftwoKeys(key1, key2):55 """模拟组合按键"""

    56 key1 =key1.lower()57 key2 =key2.lower()58 KeyBoard.keyDown(key1)59 KeyBoard.keyDown(key2)60 KeyBoard.keyUp(key1)61 KeyBoard.keyUp(key2)62

    63

    64 if __name__ == '__main__':65 from selenium importwebdriver66 driver =webdriver.Firefox()67 driver.get('http://www.baidu.com')68 driver.find_element_by_id('kw').send_keys('python')69 KeyBoard.twoKeys('ctrl', 'a')70 KeyBoard.twoKeys('ctrl', 'x')

    keyboard.py-模拟键盘

    通过测试项目设计,我们需要把测试数据存放在Excel文件中,把页面操作元素存在UI对象库中也就是一个配置文件,那么我们需要对Excel 和 ini文件解析,因此我们开始编写这两个方法,设计UI对象库和测试数据文件

    8f900a89c6347c561fdf2122f13be562.png

    961ddebeb323a10fe0623af514929fc1.png

    1 """

    2 ------------------------------------3 @Time : 2019/4/22 16:124 @Auth : linux超5 @File : parseExcelFile.py6 @IDE : PyCharm7 @Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!8 ------------------------------------9 """

    10 from openpyxl importload_workbook11 from config.conf importexcelPath12

    13

    14 classParseExcel(object):15

    16 def __init__(self):17 self.wk =load_workbook(excelPath)18 self.excelFile =excelPath19

    20 defgetSheetByName(self, sheetName):21 """获取sheet对象"""

    22 sheet =self.wk[sheetName]23 returnsheet24

    25 defgetRowNum(self, sheet):26 """获取有效数据的最大行号"""

    27 returnsheet.max_row28

    29 defgetColsNum(self, sheet):30 """获取有效数据的最大列号"""

    31 returnsheet.max_column32

    33 defgetRowValues(self, sheet, rowNum):34 """获取某一行的数据"""

    35 maxColsNum =self.getColsNum(sheet)36 rowValues =[]37 for colsNum in range(1, maxColsNum + 1):38 value =sheet.cell(rowNum, colsNum).value39 if value isNone:40 value = ''

    41 rowValues.append(value)42 returntuple(rowValues)43

    44 defgetColumnValues(self, sheet, columnNum):45 """获取某一列的数据"""

    46 maxRowNum =self.getRowNum(sheet)47 columnValues =[]48 for rowNum in range(2, maxRowNum + 1):49 value =sheet.cell(rowNum, columnNum).value50 if value isNone:51 value = ''

    52 columnValues.append(value)53 returntuple(columnValues)54

    55 defgetValueOfCell(self, sheet, rowNum, columnNum):56 """获取某一个单元格的数据"""

    57 value =sheet.cell(rowNum, columnNum).value58 if value isNone:59 value = ''

    60 returnvalue61

    62 defgetAllValuesOfSheet(self, sheet):63 """获取某一个sheet页的所有测试数据,返回一个元祖组成的列表"""

    64 maxRowNum =self.getRowNum(sheet)65 columnNum =self.getColsNum(sheet)66 allValues =[]67 for row in range(2, maxRowNum + 1):68 rowValues =[]69 for column in range(1, columnNum + 1):70 value =sheet.cell(row, column).value71 if value isNone:72 value = ''

    73 rowValues.append(value)74 allValues.append(tuple(rowValues))75 returnallValues76

    77

    78 if __name__ == '__main__':79 #excel = ParseExcel()

    80 #sheet = excel.getSheetByName('login')

    81 #print('行号:', excel.getRowNum(sheet))

    82 #print('列号:', excel.getColsNum(sheet))

    83 #84 #rowvalues = excel.getRowValues(sheet, 1)

    85 #columnvalues = excel.getColumnValues(sheet, 2)

    86 #valueofcell = excel.getValueOfCell(sheet, 1, 2)

    87 #allvalues = excel.getAllValuesOfSheet(sheet)

    88 #89 #print('第{}行数据{}'.format(1, rowvalues))

    90 #print('第{}列数据{}'.format(2, columnvalues))

    91 #print('{}{}单元格的内容{}'.format(1, 2, valueofcell))

    92 #print('login{}'.format(allvalues))

    93

    94 excel =ParseExcel()95 sheet = excel.getSheetByName('mail')96 print('行号:', excel.getRowNum(sheet))97 print('列号:', excel.getColsNum(sheet))98

    99 allvalues =excel.getAllValuesOfSheet(sheet)100

    101 print('sendmail{}'.format(allvalues))

    parseExcelFile.py-解析Excel文件

    8f900a89c6347c561fdf2122f13be562.png

    961ddebeb323a10fe0623af514929fc1.png

    1 """

    2 ------------------------------------3 @Time : 2019/4/18 10:544 @Auth : linux超5 @File : parseConFile.py6 @IDE : PyCharm7 @Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!8 ------------------------------------9 """

    10 importconfigparser11 from config.conf importconfigDir12

    13

    14 classParseConFile(object):15

    16 def __init__(self):17 self.file =configDir18 self.conf =configparser.ConfigParser()19 self.conf.read(self.file, encoding='utf-8')20

    21 defgetAllSections(self):22 """获取所有的section,返回一个列表"""

    23 returnself.conf.sections()24

    25 defgetAllOptions(self, section):26 """获取指定section下所有的option, 返回列表"""

    27 returnself.conf.options(section)28

    29 defgetLocatorsOrAccount(self, section, option):30 """获取指定section, 指定option对应的数据, 返回元祖和字符串"""

    31 try:32 locator =self.conf.get(section, option)33 if ('->' inlocator):34 locator = tuple(locator.split('->'))35 returnlocator36 exceptconfigparser.NoOptionError as e:37 print('error:', e)38 return 'error: No option "{}" in section: "{}"'.format(option, section)39

    40 defgetOptionValue(self, section):41 """获取指定section下所有的option和对应的数据,返回字典"""

    42 value =dict(self.conf.items(section))43 returnvalue44

    45

    46 if __name__ == '__main__':47 cf =ParseConFile()48 print(cf.getAllSections())49 print(cf.getAllOptions('126LoginAccount'))50 print(cf.getLocatorsOrAccount('126LoginAccount', 'username'))51 print(cf.getOptionValue('126LoginAccount'))

    parseConFile.py-解析配置文件

    8f900a89c6347c561fdf2122f13be562.png

    961ddebeb323a10fe0623af514929fc1.png

    1 [126LoginAccount];126邮箱正确的登录账号和密码;运行用例时请更换正确的用户名和密码

    2 username=linuxxiaochao3 password=xiaochao115204 [HomePageElements];126邮箱首页菜单栏元素

    5 homePage=id->_mail_tabitem_0_3text6 mailList=id->_mail_tabitem_1_4text7 applicationCenter=id->_mail_tabitem_2_5text8 inBox=id->_mail_tabitem_3_6text9 [LoginPageElements];126邮箱登录页面的元素

    10 frame=xpath->//div[@id="loginDiv"]/iframe11 username=xpath->//input[@name="email"]

    12 password=xpath->//input[@name="password"]

    13 loginBtn=xpath->//a[@id="dologin"]

    14 ferrorHead=xpath->//div[@class="ferrorhead"]

    15 [ContactPageElements];126邮箱添加联系人页面元素

    16 new_contact=xpath->//span[text()="新建联系人"]

    17 name=id->input_N18 mail=xpath->//div[@id="iaddress_MAIL_wrap"]//input[@class="nui-ipt-input"]

    19 star=xpath->//span[@class="nui-chk-text"]/preceding-sibling::span/b20 phone=xpath->//div[@id='iaddress_TEL_wrap']//input[@class='nui-ipt-input']

    21 comment=id->input_DETAIL22 commit=xpath->//span[text()='确 定']

    23 tooltip=xpath->//span[text()='请正确填写邮件地址。']

    24 [SendMailPageElements];126邮箱发送邮件页面元素

    25 writeMail=xpath->//div[@id='dvNavContainer']//span[text()='写 信']

    26 addressee=xpath->//input[@aria-label='收件人地址输入框,请输入邮件地址,多人时地址请以分号隔开']

    27 subject=xpath->//input[contains(@id, '_subjectInput')]

    28 iframe=xpath->//iframe[@class="APP-editor-iframe"]

    29 text=xpath->/html/body30 sendBtn=xpath->//header//span[text()='发送']

    31 expect=xpath->//h1[contains(@id,'_succInfo')]

    32 uploadAttachment=xpath->//div[@title="点击添加附件"]

    33 delete=xpath->//a[text()='删除']

    config.ini

    新建excel文件,分3个sheet,分别为:login,contact,mail #每个sheet中数据可自行填写,驱动测试用例执行不同的数据进行测试

    login

    18c10973a576f45fa907c9104016d098.png

    contact

    772625f2507cb5ab752fd8f0baf676c3.png

    mail

    3caf981166e22f16a7863ee2ac424538.png

    数据,UI对象库,解析方法都已经有了,接下来通过PageObject模式设计编写每个页面的操作及封装126邮箱的功能,以便后续设计用例调用

    8f900a89c6347c561fdf2122f13be562.png

    961ddebeb323a10fe0623af514929fc1.png

    1 """

    2 ------------------------------------3 @Time : 2019/4/20 8:454 @Auth : linux超5 @File : BasePage.py6 @IDE : PyCharm7 @Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!8 ------------------------------------9 """

    10 importtime11 from selenium.webdriver.support importexpected_conditions as EC12 from selenium.webdriver.support.wait importWebDriverWait as wd13 from selenium.webdriver.common.by importBy14 from selenium.common.exceptions importNoSuchWindowException, TimeoutException, \15 NoAlertPresentException, NoSuchFrameException16 from selenium importwebdriver17

    18 from util.clipboard importClipBoard19 from util.keyboard importKeyBoard20 from util.parseConFile importParseConFile21 from util.parseExcelFile importParseExcel22

    23

    24 classBasePage(object):25 """

    26 结合显示等待封装一些selenium 内置方法27 """

    28 cf =ParseConFile()29 excel =ParseExcel()30

    31 def __init__(self, driver, outTime=30):32 self.byDic ={33 'id': By.ID,34 'name': By.NAME,35 'class_name': By.CLASS_NAME,36 'xpath': By.XPATH,37 'link_text': By.LINK_TEXT38 }39 self.driver =driver40 self.outTime =outTime41

    42 deffindElement(self, by, locator):43 """

    44 find alone element45 :param by: eg: id, name, xpath, css.....46 :param locator: id, name, xpath for str47 :return: element object48 """

    49 try:50 print('[Info:Starting find the element "{}" by "{}"!]'.format(locator, by))51 element = wd(self.driver, self.outTime).until(lambdax : x.find_element(by, locator))52 exceptTimeoutException as t:53 print('error: found "{}" timeout!'.format(locator), t)54 exceptNoSuchWindowException as e:55 print('error: no such "{}"'.format(locator), e)56 exceptException as e:57 raisee58 else:59 #print('[Info:Had found the element "{}" by "{}"!]'.format(locator, by))

    60 returnelement61

    62 deffindElements(self, by, locator):63 """

    64 find group elements65 :param by: eg: id, name, xpath, css.....66 :param locator: eg: id, name, xpath for str67 :return: elements object68 """

    69 try:70 print('[Info:start find the elements "{}" by "{}"!]'.format(locator, by))71 elements = wd(self.driver, self.outTime).until(lambdax : x.find_element(by, locator))72 exceptTimeoutException as t:73 print(t)74 exceptNoSuchWindowException as e:75 print(e)76 exceptException as e:77 raisee78 else:79 #print('[Info:Had found the elements "{}" by "{}"!]'.format(locator, by))

    80 returnelements81

    82 defisElementExsit(self, by, locator):83 """

    84 assert element if exist85 :param by: eg: id, name, xpath, css.....86 :param locator: eg: id, name, xpath for str87 :return: if element return True else return false88 """

    89 if by.lower() inself.byDic:90 try:91 wd(self.driver, self.outTime).\92 until(EC.visibility_of_element_located((self.byDic[by], locator)))93 exceptTimeoutException:94 print('Error: element "{}" time out!'.format(locator))95 returnFalse96 exceptNoSuchWindowException:97 print('Error: element "{}" not exsit!'.format(locator))98 returnFalse99 returnTrue100 else:101 print('the "{}" error!'.format(by))102

    103 defisClick(self, by, locator):104 """判断是否可点击,返回元素对象"""

    105 if by.lower() inself.byDic:106 try:107 element =wd(self.driver, self.outTime).\108 until(EC.element_to_be_clickable((self.byDic[by], locator)))109 exceptException:110 returnFalse111 returnelement112 else:113 print('the "{}" error!'.format(by))114

    115 defisAlertAndSwitchToIt(self):116 """

    117 assert alert if exsit118 :return: alert obj119 """

    120 try:121 re =wd(self.driver, self.outTime).until(EC.alert_is_present())122 exceptNoAlertPresentException:123 returnFalse124 exceptException:125 returnFalse126 returnre127

    128 defswitchToFrame(self, by, locator):129 """判断frame是否存在,存在就跳到frame"""

    130 print('info:switching to iframe "{}"'.format(locator))131 if by.lower() inself.byDic:132 try:133 wd(self.driver, self.outTime).\134 until(EC.frame_to_be_available_and_switch_to_it((self.byDic[by], locator)))135 exceptTimeoutException as t:136 print('error: found "{}" timeout!'.format(locator), t)137 exceptNoSuchFrameException as e:138 print('error: no such "{}"'.format(locator), e)139 exceptException as e:140 raisee141 else:142 print('the "{}" error!'.format(by))143

    144 defswitchToDefaultFrame(self):145 """返回默认的frame"""

    146 print('info:switch back to default iframe')147 try:148 self.driver.switch_to.default_content()149 exceptException as e:150 print(e)151

    152 defgetAlertText(self):153 """获取alert的提示信息"""

    154 ifself.isAlertAndSwitchToIt():155 alert =self.isAlertAndSwitchToIt()156 returnalert.text157 else:158 returnNone159

    160 def getElementText(self, by, locator, name=None):161 """获取某一个元素的text信息"""

    162 try:163 element =self.findElement(by, locator)164 ifname:165 returnelement.get_attribute(name)166 else:167 returnelement.text168 except:169 print('get "{}" text failed return None'.format(locator))170 returnNone171

    172 defloadUrl(self, url):173 """加载url"""

    174 print('info: string upload url "{}"'.format(url))175 self.driver.get(url)176

    177 defgetSource(self):178 """获取页面源码"""

    179 returnself.driver.page_source180

    181 def sendKeys(self, by, locator, value=''):182 """写数据"""

    183 print('info:input "{}"'.format(value))184 try:185 element =self.findElement(by, locator)186 element.send_keys(value)187 exceptAttributeError as e:188 print(e)189

    190 defclear(self, by, locator):191 """清理数据"""

    192 print('info:clearing value')193 try:194 element =self.findElement(by, locator)195 element.clear()196 exceptAttributeError as e:197 print(e)198

    199 defclick(self, by, locator):200 """点击某个元素"""

    201 print('info:click "{}"'.format(locator))202 element =self.isClick(by, locator)203 ifelement:204 element.click()205 else:206 print('the "{}" unclickable!')207

    208 def sleep(self, num=0):209 """强制等待"""

    210 print('info:sleep "{}" minutes'.format(num))211 time.sleep(num)212

    213 defctrlV(self, value):214 """ctrl + V 粘贴"""

    215 print('info:pasting "{}"'.format(value))216 ClipBoard.setText(value)217 self.sleep(3)218 KeyBoard.twoKeys('ctrl', 'v')219

    220 defenterKey(self):221 """enter 回车键"""

    222 print('info:keydown enter')223 KeyBoard.oneKey('enter')224

    225 defwaitElementtobelocated(self, by, locator):226 """显示等待某个元素出现,且可见"""

    227 print('info:waiting "{}" to be located'.format(locator))228 try:229 wd(self.driver, self.outTime).until(EC.visibility_of_element_located((self.byDic[by], locator)))230 exceptTimeoutException as t:231 print('error: found "{}" timeout!'.format(locator), t)232 exceptNoSuchWindowException as e:233 print('error: no such "{}"'.format(locator), e)234 exceptException as e:235 raisee236

    237 defassertValueInSource(self, value):238 """断言某个关键字是否存在页面源码中"""

    239 print('info:assert "{}" in page source'.format(value))240 source =self.getSource()241 assert value in source, '关键字"{}"不存在源码中!'.format(value)242

    243 defassertStringContainsValue(self, String, value):244 """断言某段字符串包含另一个字符串"""

    245 print('info:assert "{}" contains "{}"'.format(String, value))246 assert value in String, '"{}"不包含"{}"!'.format(String, value)247

    248

    249 @staticmethod250 defgetSheet(sheetName):251 """获取某个sheet页的对象"""

    252 sheet =BasePage.excel.getSheetByName(sheetName)253 returnsheet254

    255

    256 if __name__ == "__main__":257 driver =webdriver.Firefox()258 frame = ('xpath', '//div[@id="loginDiv"]/ifram')259 wait =BasePage(driver)260 driver.get('https://mail.126.com/')261 wait.switchToFrame(*frame)262 username = wait.findElement('xpath', '//input[@name="email"]')263 username.send_keys('账号')264 if wait.isElementExsit('xpath', '//input[@name="password"]'):265 wait.findElement('xpath', '//input[@name="password"]').send_keys('xiaochao11520')266 wait.click('xpath', '//a[@id="dologin"]')

    BasePage.py-webdriver二次封装

    8f900a89c6347c561fdf2122f13be562.png

    961ddebeb323a10fe0623af514929fc1.png

    1 """

    2 ------------------------------------3 @Time : 2019/4/20 12:284 @Auth : linux超5 @File : HomePage.py6 @IDE : PyCharm7 @Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!8 ------------------------------------9 """

    10 from Page.BasePage importBasePage11

    12

    13 classHomePage(BasePage):14 #配置文件读取元素

    15 homePage = BasePage.cf.getLocatorsOrAccount('HomePageElements', 'homePage')16 mailList = BasePage.cf.getLocatorsOrAccount('HomePageElements', 'mailList')17 applicationCenter = BasePage.cf.getLocatorsOrAccount('HomePageElements', 'applicationCenter')18 inBox = BasePage.cf.getLocatorsOrAccount('HomePageElements', 'inBox')19 '''首页菜单选项'''

    20 def selectMenu(self, Menu='mailList'):21 """邮箱首页选择菜单"""

    22 if Menu == 'mailList':23 self.click(*HomePage.mailList)24 elif Menu == 'homePage':25 self.click(*HomePage.homePage)26 elif Menu == 'applicationCenter':27 self.click(*HomePage.applicationCenter)28 elif Menu == 'inBox':29 self.click(*HomePage.inBox)30 else:31 raise ValueError('''

    32 菜单选择错误!33 homePage->首页34 mailList->通讯录35 applicationCenter->应用中心36 inBox->收件箱''')37

    38 if __name__=='__main__':39 from selenium importwebdriver40 from Page.PageObject.LoginPage importLoginPage41 driver =webdriver.Firefox()42 login =LoginPage(driver)43 login.login('账号', 'xiaochao11520')44

    45 home =HomePage(driver)46 home.selectMenu()

    HomePage.py-邮箱首页选择菜单

    8f900a89c6347c561fdf2122f13be562.png

    961ddebeb323a10fe0623af514929fc1.png

    1 """

    2 ------------------------------------3 @Time : 2019/4/20 12:284 @Auth : linux超5 @File : LoginPage.py6 @IDE : PyCharm7 @Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!8 ------------------------------------9 """

    10 from Page.BasePage importBasePage11

    12

    13 classLoginPage(BasePage):14

    15 #配置文件读取元素

    16 frame = BasePage.cf.getLocatorsOrAccount('LoginPageElements', 'frame')17 username = BasePage.cf.getLocatorsOrAccount('LoginPageElements', 'username')18 password = BasePage.cf.getLocatorsOrAccount('LoginPageElements', 'password')19 loginBtn = BasePage.cf.getLocatorsOrAccount('LoginPageElements', 'loginBtn')20 ferrorHead = BasePage.cf.getLocatorsOrAccount('LoginPageElements', 'ferrorHead') #登录失败提示

    21

    22 deflogin(self, userName, passWord):23 '''登录'''

    24 print('-------staring login-------')25 self.loadUrl('https://mail.126.com')26 self.switchToFrame(*LoginPage.frame)27 self.clear(*LoginPage.username)28 self.sendKeys(*LoginPage.username, userName)29 self.clear(*LoginPage.password)30 self.sendKeys(*LoginPage.password, passWord)31 self.click(*LoginPage.loginBtn)32 self.switchToDefaultFrame()33 print('---------end login---------')34

    35 #add at 2019/04/19

    36 def assertTextEqString(self, expected, name =None):37 '''断言提示信息是否与期望的值相等'''

    38 self.switchToFrame(*LoginPage.frame)39 text = self.getElementText(*LoginPage.ferrorHead, name)40 self.switchToDefaultFrame()41 print('info: assert "{}" == "{}"'.format(text, expected))42 assert text == expected, '{} != {}'.format(text, expected)43

    44 if __name__=="__main__":45 from selenium importwebdriver46 driver =webdriver.Firefox()47 login = LoginPage(driver, 30)48 login.login('lin', '')49 login.assertTextEqString('请输入密码')

    LoginPage.py-封装登录功能

    8f900a89c6347c561fdf2122f13be562.png

    961ddebeb323a10fe0623af514929fc1.png

    1 """

    2 ------------------------------------3 @Time : 2019/4/20 12:294 @Auth : linux超5 @File : ContactPage.py6 @IDE : PyCharm7 @Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!8 ------------------------------------9 """

    10 from Page.BasePage importBasePage11

    12

    13 classContactPage(BasePage):14 #配置文件读取元素

    15 new_contact = BasePage.cf.getLocatorsOrAccount('ContactPageElements', 'new_contact')16 name = BasePage.cf.getLocatorsOrAccount('ContactPageElements', 'name')17 mail = BasePage.cf.getLocatorsOrAccount('ContactPageElements', 'mail')18 star = BasePage.cf.getLocatorsOrAccount('ContactPageElements', 'star')19 phone = BasePage.cf.getLocatorsOrAccount('ContactPageElements', 'phone')20 comment = BasePage.cf.getLocatorsOrAccount('ContactPageElements', 'comment')21 commit = BasePage.cf.getLocatorsOrAccount('ContactPageElements', 'commit')22 errortip = BasePage.cf.getLocatorsOrAccount('ContactPageElements', 'tooltip') #错误提示

    23

    24 defnewContact(self, Name, Mail, Star, Phone, Comment):25 """添加联系人"""

    26 print('--------string add contact--------')27 self.click(*ContactPage.new_contact)28 self.sendKeys(*ContactPage.name, Name)29 self.sendKeys(*ContactPage.mail, Mail)30 if Star == '1':31 self.click(*ContactPage.star)32 self.sendKeys(*ContactPage.phone, Phone)33 self.sendKeys(*ContactPage.comment, Comment)34 self.click(*ContactPage.commit)35 print('--------end add contact--------')36

    37 defassertErrorTip(self, excepted):38 """断言联系人添加失败时是否有提示信息"""

    39 text = self.getElementText(*ContactPage.errortip)40 print('info: assert "{}"=="{}"'.format(text, excepted))41 assert text ==excepted42

    43 if __name__ == '__main__':44 from selenium importwebdriver45 from Page.PageObject.LoginPage importLoginPage46 from Page.PageObject.HomePage importHomePage47 driver =webdriver.Firefox()48 home =HomePage(driver)49 login =LoginPage(driver)50 contact =ContactPage(driver)51

    52 login.login('账号', 'xiaochao11520')53 home.selectMenu()54 contact.newContact('281754041@qq.com')

    ContactPage.py-封装添加联系人功能

    8f900a89c6347c561fdf2122f13be562.png

    961ddebeb323a10fe0623af514929fc1.png

    1 """

    2 ------------------------------------3 @Time : 2019/4/20 9:164 @Auth : linux超5 @File : SendMailPage.py6 @IDE : PyCharm7 @Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!8 ------------------------------------9 """

    10 from Page.BasePage importBasePage11

    12

    13 classSendMailPage(BasePage):14 #配置文件读取元素

    15 writeMail = BasePage.cf.getLocatorsOrAccount('SendMailPageElements', 'writeMail')16 addressee = BasePage.cf.getLocatorsOrAccount('SendMailPageElements', 'addressee')17 subject = BasePage.cf.getLocatorsOrAccount('SendMailPageElements', 'subject')18 iframe = BasePage.cf.getLocatorsOrAccount('SendMailPageElements', 'iframe')19 text = BasePage.cf.getLocatorsOrAccount('SendMailPageElements', 'text')20 sendBtn = BasePage.cf.getLocatorsOrAccount('SendMailPageElements', 'sendBtn')21 expect = BasePage.cf.getLocatorsOrAccount('SendMailPageElements', 'expect')22 uploadAttachment = BasePage.cf.getLocatorsOrAccount('SendMailPageElements', 'uploadAttachment')23 delete = BasePage.cf.getLocatorsOrAccount('SendMailPageElements', 'delete')24

    25 def sendMail(self, Address, Subject, Text, PFA=''):26 """发送邮件功能"""

    27 print('------------string send mail---------------------')28 self.click(*SendMailPage.writeMail)29 self.sendKeys(*SendMailPage.addressee, Address)30 self.sendKeys(*SendMailPage.subject, Subject)31 self.switchToFrame(*SendMailPage.iframe)32 self.sendKeys(*SendMailPage.text, Text)33 self.switchToDefaultFrame()34 ifPFA:35 self.click(*SendMailPage.uploadAttachment)36 self.ctrlV(PFA)37 self.enterKey()38 self.waitElementtobelocated(*SendMailPage.delete)39 self.click(*SendMailPage.sendBtn)40 print('------------end send mail---------------------')41

    42 if __name__=='__main__':43 from Page.PageObject.LoginPage importLoginPage44 from selenium importwebdriver45 driver =webdriver.Firefox()46

    47 login =LoginPage(driver)48 login.login('账号', 'xiaochao11520')49 sendMail =SendMailPage(driver)50 sendMail.sendMail('281754043@qq.com', 'pytest', 'pytest实战实例', 1, 'D:\KeyWordDriverTestFrameWork\geckodriver.log')

    SendMailPage.py-封装发送邮件功能

    所有的准备工作都已经做好了,还有一个问题,我们的添加联系人和发送邮件应该是否应该在已经登录的前提下测试呢?答案是肯定的。所以我们在用例同目录下新建conftest.py文件并调用登录功能(为什么这么做,不明白的小伙伴可以去看一下我之前的文章,关于conftest.py的原理)

    8f900a89c6347c561fdf2122f13be562.png

    961ddebeb323a10fe0623af514929fc1.png

    1 """

    2 ------------------------------------3 @Time : 2019/4/20 15:104 @Auth : linux超5 @File : conftest.py6 @IDE : PyCharm7 @Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!8 ------------------------------------9 """

    10 importpytest11 from Page.PageObject.LoginPage importLoginPage12

    13

    14 #从配置文件中获取正确的用户名和密码

    15 userName = LoginPage.cf.getLocatorsOrAccount('126LoginAccount', 'username')16 passWord = LoginPage.cf.getLocatorsOrAccount('126LoginAccount', 'password')17 @pytest.fixture(scope='function')18 deflogin(driver):19 '''除登录用例,每一个用例的前置条件'''

    20 print('------------staring login------------')21 loginFunc = LoginPage(driver, 30)22 loginFunc.login(userName, passWord)23 yield

    24 print('------------end login------------')25 driver.delete_all_cookies()

    conftest.py-同用例目录下,调用登录功能

    ok,开始编写测试用例啦

    8f900a89c6347c561fdf2122f13be562.png

    961ddebeb323a10fe0623af514929fc1.png

    1 """

    2 ------------------------------------3 @Time : 2019/4/20 14:104 @Auth : linux超5 @File : test_loginCase.py6 @IDE : PyCharm7 @Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!8 ------------------------------------9 """

    10 importpytest11 from Page.PageObject.LoginPage importLoginPage12

    13

    14 @pytest.mark.loginTest15 classTestLogin(object):16

    17 #测试数据

    18 loginSheet = LoginPage.getSheet('login')19 data =LoginPage.excel.getAllValuesOfSheet(loginSheet)20

    21 #正确的帐号和密码

    22 userName = LoginPage.cf.getLocatorsOrAccount('126LoginAccount', 'username')23 passWord = LoginPage.cf.getLocatorsOrAccount('126LoginAccount', 'password')24

    25 @pytest.fixture()26 defteardown_func(self, driver):27 """

    28 执行每个用例之后要清除一下cookie,29 否则你第一个账号登录之后,重新加载网址还是登录状态,无法测试后面的账号30 """

    31 yield

    32 driver.delete_all_cookies()33

    34 @pytest.mark.parametrize('username, password, expect', data)35 deftest_login(self, teardown_func, driver, username, password, expect):36 """测试登录"""

    37 login = LoginPage(driver, 30)38 login.login(username, password)39 login.sleep(5)40 #增加登录失败时, 对提示信息的验证

    41 if username == TestLogin.userName and password ==TestLogin.passWord:42 login.assertValueInSource(expect)43 elif username == '':44 login.assertTextEqString(expect)45 elif username != '' and password == '':46 login.assertTextEqString(expect)47 elif username == '' and password == '':48 login.assertTextEqString(expect)49 else:50 login.assertTextEqString(expect)51

    52

    53 if __name__ == "__main__":54 pytest.main(['-v', 'test_loginCase.py'])

    test_loginCase.py-登录功能测试

    8f900a89c6347c561fdf2122f13be562.png

    961ddebeb323a10fe0623af514929fc1.png

    1 """

    2 ------------------------------------3 @Time : 2019/4/20 16:154 @Auth : linux超5 @File : test_contactCase.py6 @IDE : PyCharm7 @Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!8 ------------------------------------9 """

    10 importre11 importpytest12 from Page.PageObject.HomePage importHomePage13 from Page.PageObject.ContactPage importContactPage14

    15

    16 @pytest.mark.conatctTest17 classTestAddContact(object):18

    19 #测试数据

    20 contactSheet = ContactPage.getSheet('contact')21 data =ContactPage.excel.getAllValuesOfSheet(contactSheet)22

    23 @pytest.mark.newcontact24 @pytest.mark.parametrize('Name, Mail, Star, Phone, Comment, expect', data)25 deftest_NewContact(self, driver, login, Name, Mail, Star, Phone, Comment, expect):26 """测试添加联系人"""

    27 home_page =HomePage(driver)28 contact_page =ContactPage(driver)29 home_page.selectMenu()30 contact_page.newContact(Name, Mail, Star, Phone, Comment)31 home_page.sleep(5)32 #校验错误的邮箱是否提示信息正确

    33 if re.match(r'^.{1,}@[0-9a-zA-Z]{1,13}\..*$', Mail):34 contact_page.assertValueInSource(expect)35 else:36 contact_page.assertErrorTip(expect)37

    38 if __name__ == '__main__':39 pytest.main(['-v', 'test_contactCase.py'])

    test_contactCase.py-添加联系人功能测试

    8f900a89c6347c561fdf2122f13be562.png

    961ddebeb323a10fe0623af514929fc1.png

    1 """

    2 ------------------------------------3 @Time : 2019/4/20 10:044 @Auth : linux超5 @File : test_sendMailCase.py6 @IDE : PyCharm7 @Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!8 ------------------------------------9 """

    10 importpytest11 from Page.PageObject.SendMailPage importSendMailPage12

    13 @pytest.mark.sendMailTest14 classTestSendMail(object):15

    16 sendMailSheet = SendMailPage.getSheet('mail')17 data =SendMailPage.excel.getAllValuesOfSheet(sendMailSheet)18

    19 @pytest.mark.sendmail20 @pytest.mark.parametrize('Address, Subject, Text, PFA', data)21 deftest_sendMail(self, driver, login, Address, Subject, Text,PFA):22 """测试发送邮件,包括带附件的邮件"""

    23 send_mail =SendMailPage(driver)24 send_mail.sendMail(Address, Subject, Text, PFA)25 send_mail.sleep(5)26 assert send_mail.isElementExsit(*SendMailPage.expect)27

    28 if __name__=='__main__':29 pytest.main(['-v', 'test_sendMailCase.py'])

    test_sendMailCase.py-发送邮件功能测试

    问题

    用例已经写完了,有两个问题

    1.有没有发现我们的报告怎么生成的?也没有失败用例截图?

    2.我们貌似并没有编写驱动浏览器的代码?

    现在我们来解决这个两个问题

    根据pytest的conftest.py文件的原理,我们可以把驱动浏览器的代码写在一个全局的conftest.py文件里面。报告生成其实是通过命令 pytest --html=‘report.html’ --self-contained-html生成的,但是这样的报告对用例的描述不是很清晰,且没有对失败用例截图,也不方便我们分析项目的缺陷,我们也可以填写代码放到这个文件里面(关于报告修改的文章)

    8f900a89c6347c561fdf2122f13be562.png

    961ddebeb323a10fe0623af514929fc1.png

    1 """

    2 ------------------------------------3 @Time : 2019/4/12 14:104 @Auth : linux超5 @File : conftest.py6 @IDE : PyCharm7 @Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!8 ------------------------------------9 """

    10 importpytest11 from selenium importwebdriver12 from py._xmlgen importhtml13

    14 _driver =None15 #测试失败时添加截图和测试用例描述(用例的注释信息)

    16

    17 @pytest.mark.hookwrapper18 defpytest_runtest_makereport(item):19 """

    20 当测试失败的时候,自动截图,展示到html报告中21 :param item:22 """

    23 pytest_html = item.config.pluginmanager.getplugin('html')24 outcome = yield

    25 report =outcome.get_result()26 extra = getattr(report, 'extra', [])27

    28 if report.when == 'call' or report.when == "setup":29 xfail = hasattr(report, 'wasxfail')30 if (report.skipped and xfail) or (report.failed and notxfail):31 file_name = report.nodeid.replace("::", "_")+".png"

    32 screen_img =_capture_screenshot()33 iffile_name:34 html = '

    ' %screen_img36 extra.append(pytest_html.extras.html(html))37 report.extra =extra38 report.description = str(item.function.__doc__)39 report.nodeid = report.nodeid.encode("utf-8").decode("unicode_escape")40

    41

    42 @pytest.mark.optionalhook43 defpytest_html_results_table_header(cells):44 cells.insert(1, html.th('Description'))45 cells.insert(2, html.th('Test_nodeid'))46 cells.pop(2)47

    48

    49 @pytest.mark.optionalhook50 defpytest_html_results_table_row(report, cells):51 cells.insert(1, html.td(report.description))52 cells.insert(2, html.td(report.nodeid))53 cells.pop(2)54

    55

    56 def_capture_screenshot():57 """

    58 截图保存为base6459 :return:60 """

    61 return_driver.get_screenshot_as_base64()62 #这里我设置的级别是模块级别,也就是每个测试文件运行一次

    63 #可以设置为session,全部用例执行一次,但是针对126邮箱的话

    64 #登录次数太多会叫你验证,如果验证就没法执行用例了,我没有对验证处理(处理比较复杂)

    65

    66

    67 @pytest.fixture(scope='module')68 defdriver():69 global_driver70 print('------------open browser------------')71 _driver =webdriver.Firefox()72

    73 yield_driver74 print('------------close browser------------')75 _driver.quit()

    conftest.py-全局conftest.py文件

    最后呢,为了减小项目维护成本,我们把一些全局的配置项,放到我们的功能配置文件中共全局使用,包括运行用例的一些命令字符串,可以自行修改

    8f900a89c6347c561fdf2122f13be562.png

    961ddebeb323a10fe0623af514929fc1.png

    1 """

    2 ------------------------------------3 @Time : 2019/4/20 16:504 @Auth : linux超5 @File : conf.py6 @IDE : PyCharm7 @Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!8 ------------------------------------9 """

    10 from datetime importdatetime11 importos12 #项目根目录

    13 projectDir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))14 #报告目录

    15 reportDir = os.path.join(projectDir, 'report')16 #ui对象库config.ini文件所在目录

    17 configDir = os.path.join(projectDir, 'config', 'config.ini')18 #测试数据所在目录

    19 excelPath = os.path.join(projectDir, 'data', 'tcData.xlsx')20 #当前时间

    21 currentTime = datetime.now().strftime('%H_%M_%S')22

    23 #邮件配置信息

    24 #邮件服务器

    25 smtpServer = 'smtp.qq.com'

    26 #发送者

    27 fromUser = '账号@qq.com'

    28 #发送者密码

    29 fromPassWord = 'mhxvqpewblldbjhf'

    30 #接收者

    31 toUser = ['账号@qq.com']#可以同时发送给多人,追加到列表中

    32 #邮件标题

    33 subject = 'xx项目自动化测试报告'

    34 #邮件正文

    35 contents = '测试报告正文'

    36 #报告名称

    37 htmlName = r'{}\testReport{}.html'.format(reportDir, currentTime)38

    39 #脚本执行命令

    40 args = r'pytest --html=' + htmlName+ ' ' + '--self-contained-html'

    41 #modify by linuxchao at 2019/4/25

    42 args_login = r'pytest --html='+ htmlName+ ' ' + '-m' + ' ' + 'loginTest'+ '--self-contained-html'

    43 args_contact = r'pytest --html='+ htmlName+ ' ' + '-m' + ' ' + 'contactTest'+ '--self-contained-html'

    44 args_sendmail = r'pytest --html='+ htmlName+ ' ' + '-m' + ' ' + 'sendMailTest'+ '--self-contained-html'

    conf.py-全局配置文件

    运行项目

    通过命令运行

    1.cmd切换到项目的根目录,执行pytest --html=‘report.html’ --self-contained-html命令(此运行方式,无法发送测试报告邮件)

    这种方式感觉有点low,我们换另外一种方式,可以通过os模块自动执行相关命令,编写运行用例代码

    8f900a89c6347c561fdf2122f13be562.png

    961ddebeb323a10fe0623af514929fc1.png

    1 """

    2 ------------------------------------3 @Time : 2019/4/15 16:144 @Auth : linux超5 @File : RunTestCase.py6 @IDE : PyCharm7 @Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!8 ------------------------------------9 """

    10 importsys11 sys.path.append('.')12 from config.conf import *

    13 from util.sendMailForReprot importSendMailWithReport14

    15

    16 defmain():17 #判断项目的根目录是否在sys.path中,没有就添加

    18 if projectDir not insys.path:19 sys.path.append(projectDir)20 #执行用例

    21 os.system(args)22 #发送邮件

    23 SendMailWithReport.send_mail(24 smtpServer, fromUser, fromPassWord,25 toUser, subject, contents,26 htmlName)27

    28

    29 if __name__ == '__main__':30 main()

    RunTestCase.py-执行用例文件

    我们可以直接执行这个文件执行所用的测试用例了!

    其实我们运行用例往往不只是 使用pytest --html=‘report.html’ --self-contained-html 这样一个简单的命令运行,通常会添加很多的命令选项,比如-v,-q,-s等等,那么怎么办呢?这时候就用到了pytest.ini配置文件了

    8f900a89c6347c561fdf2122f13be562.png

    961ddebeb323a10fe0623af514929fc1.png

    1 [pytest]2 addopts=-vqs3 testpaths=./TestCases4 markers=

    5 loginTest: Run login test cases6 contactTest: Run add contact test cases7 sendMailTest: Run send mail test cases

    pytest.ini-pytest配置文件

    测试输出

    1.自动生成html格式报告,其中报告里面附带用例执行日志明细,及用例失败自动截图(部分报告展示)

    2c212d0140a6ee4319ea4b68369dab28.png

    41034173d07e2720069218bad4b9e7b6.png

    2.自动发送测试邮件给指定用户

    0bbd4eae21b68ac0507c920b87361be7.png

    项目源码

    源码在本人github上,可自行下载!

    PS:最后还是附上我们的QQ交流群:878565760  真心希望所有对测试感兴趣,想入门,想提升自己测试能力的小伙伴加入!

    展开全文
  • ◇ 具备专家级自动化测试解决方案、自动化测试平台框架设计开发、自动化测试团队管理培训经验。◇ 具备专家级的功能专项测试、性能专项测试经验及丰富的渗透测试经验。◇ 具备丰富的持续集成/持续交付(CI/CD)开发、...
  • UI 自动化测试过程中,面对复杂的业务场景,经常会遇到这样的挑战: 简单的录制/回放速度快,但无法适应复杂场景; 编写自动化测试脚本比较灵活,但工作量大且可维护性差; 以往的封装技术(PageObject)...
  • 前面章节利用本地 “html” 文件的虚拟场景辅助我们了解了元素的定位、元素的操作,熟悉了正常...从这一章节开始,将对之前接口自动化测试篇章所使用的旅游网这样的一个项目,进行真实的UI自动化脚本的设计。.........
  • 上周和大家分享了Python+uiautomator2+weditor UI自动化测试实战(1):环境搭建,接下来和大家分享一下在具体的实战中的UI自动化的运用吧。 下面的代码主要演示了登录腾讯视频,然后切换到个人中心页面,用QQ登录后再...
  • 接口自动化测试项目实战
  • pytest自动化测试
  • 基于JAVA实现的WEB端UI自动化 - WebDriver框架篇 - Jenkins[定时计划执行任务] 基于JAVA实现的WEB端UI自动化 - WebDriver框架篇 - 内部框架及UI自动化测试框架思维导图 完结!撒花!✿✿ヽ(°▽°)ノ✿ 1 - Web自动...
  • 今天我会详细的描述一个【UI自动化实战在实际工作中的应用】,这是之前贝壳找房我们团队做的一个真实UI自动化案例,由于APP版本更新迭代太多次,代码也有缺失,所以一些细节没法完全描述清楚。但我会把做这个项目的...
  • UI自动化测试该怎么做?

    千次阅读 2021-01-29 21:05:39
    UI自动化测试一直都是如此的令人纠结,自动化测试初学者总是拿它入门,但有些经验丰富者对其又是毁誉参半,抑或抛出分层自动化测试那个经典的“金字塔”,来说明UI自动化测试还是少做为好。
  • 自动化测试过程中,不可缺少的一部分就是 "测试报告" 。在前面的接口自动化测试的章节,其实已经讲了一些,最终是使用 "HTMLTESTRUNNER" 生成的测试报告。今天依然用其生成一个 "WEB自动化登录Case" 的测试报告。....
  • 什么样的项目适合UI自动化测试

    千次阅读 2020-12-03 17:20:49
    我们在考虑做自动化测试之前,一定要先分析一下,这个项目到底适不适合做自动化测试,避免在不太适合自动化测试项目中痛苦挣扎,既浪费了大量的人力和时间,又收效甚微。下面简单列举一下评估一下项目是否适合做...
  • 学习自动化测试最难的是没有合适的项目练习。 测试本身既要讲究科学,又有艺术成分,单单学几个 API 的调用很难应付工作中具体的问题。 你得知道什么场景下需要添加显性等待,什么时候元素定位需要写得更加优雅,为...
  • Python UI自动化测试实操

    千次阅读 2020-05-26 17:56:39
    UI 自动化框架主要的实验的目的是:完成了登录页面的自动化登录与打开会员中心的页面这一自动化的过程。 废话不多说,直接上代码截图: 我们首先来看看整个工程的目录结构,这样以便于了解项目的调用关系: ...
  • web UI 自动化测试

    千次阅读 2022-02-26 09:35:24
    自动化测试的介入点: 系统测试, 可行性分析 框架选择,需求分析,计划,测试用例设计,无人值守,提交报考,运行维护 python +selenium环境搭建 1)python 环境的安装与搭建 2)pycharm 的安装 3) 解决pyCharm...
  • 一.环境准备 二.测试地址https://mail.126.com三.测试范围1.126电子邮箱登录功能测试-验证正确帐号密码登录成功-验证错误用户名密码登录失败(有很多情况,...项目设计1.python编程语言设计测试脚本。2.webdriver驱动
  • JS执行器在UI自动化测试中的应用

    千次阅读 2022-03-23 19:27:32
    在进行UI自动化过程会遇到滚动条下拉、隐藏元素定位、只读属性元素的编辑、富文本处理等,此时可以使用JS执行器简化我们的一些处理操作。 具体应用 JS执行器的使用步骤: 1.先写个JS脚本,如果需要获取操作后的值...
  • 对于如何开展一个Web项目UI自动化测试,首先需要建立一个自动化测试的小组,小组理想状态下有4个人员,测试开发、中高级自动化测试工程师、2个初级自动化工程师;非理想的情况下,可能只需要一个人。
  • 接口自动化测试.pdf

    2020-08-04 17:30:18
    python+ddt+unittest数据驱动,接口自动化测试框架介绍,用于团队内部培训和技术分享。
  • 篇幅较长,要耐心阅读哦~ 基础知识简要回顾 持续集成、持续交付的好处与产生的...Selenium自动化测试项目介绍 用例业务内容:测试百度网首页搜索关键词之后,跳转页面标题的正确性 python代码实现 Web UI 测试框架 .
  • 在上一篇章就软件自动化测试当中最简单也是最常用接口自动化测试进行了一个比较完整性的实战。可是当我们投入到实际工作应用中就会发现,虽然接口测试很有效也很容易推广开来,但是很多时候真正需要测试验证的不仅仅...
  • pytest+selenium+allure UI自动化测试框架

    千次阅读 2022-04-22 15:32:56
    selenium业务逻辑层+pytest执行+allure报告的UI自动化框架 注意:需要自行下载allure插件
  • 如果你在项目中使用过WebUI自动化测试,那么如下的一些状况想必你肯定遇见过: 1、当端到端的WebUI自动化测试执行失败后,你也无法确定是被测Web服务失效了还是执行测试机出问题了。 2、页面有了一些完全不会影响...
  • UI自动化测试介绍

    2021-04-06 21:50:29
    1.什么是自动化测试 由程序代替人工进行系统校验的过程 1.1自动化测试能解决什么 回归测试(冒烟测试) 针对之前老的功能进行测试 (通过自动化的代码来实现) 针对上一个版本的问题的回归 兼容性测试 web实例...
  • 本框架是一套基于selenium+Python3.7+yaml+Robot Framework而设计的数据驱动UI自动化测试框架,Robot Framework 作为执行器,本框架整体特点为:用例与代码分离;用例驱动测试的执行;分层设计、脚本模块化;自动准备...
  • 上一章《曲鸟全栈UI自动化教学(六):开始实战吧!实战环境准备》 我们通过使用禅道真正的实现了第一个实战脚本,文末的练习题小伙伴实现了吗? 二、公布上一章练习题答案 记得把你对应谷歌浏览器版本的chromedriver...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 13,749
精华内容 5,499
关键字:

ui自动化测试项目实战