首页 > 编程语言 >Python+Selenium+Pytest+Allure+Jenkins实现的Web自动化框架

Python+Selenium+Pytest+Allure+Jenkins实现的Web自动化框架

时间:2023-12-29 18:11:24浏览次数:40  
标签:__ Web Python self driver Allure import docker def

目录

一、测试的项目

jpress-4.1.5 博客搭建平台

Linux服务器环境:JDK(1.8.0)+Tomcat(apache-tomcat-8.5.91)+MySql(8.0)

准备工作:将jpress-4.1.5.war 上传到服务器,复制到 /usr/local/tomcat/webapps 目录下,会自动解压缩。

访问:http://服务器ip:端口/jpress-4.1.5/admin

二、需求分析

本项目要测试的是一个开源的Java项目,测试的功能包括:

  1. 用户注册
  2. 用户登录
  3. 后台管理员登录
  4. 添加文章
  5. 删除文章

本项目通过自动化测试的方法来验证,用户注册、用户登录、管理员登录、添加文章、信息的完整性、正确性。比如必填项、电子邮件的格式、密码一致性、识别验证码。

其中,识别验证码是本项目的难点,通过调用第三方接口实现。

三、用例设计-部分用例举例

四、框架说明

本框架是一套基于 Python+Selenium+Pytest+Allure+Jenkins 而设计的数据驱动Web自动化测试的框架。

  • 技术栈:Python、Pytest、Excel、Json、MySql、Allure、Jenkins。

4.1 测试框架结构图如下:

4.2 项目功能

Python+Selenium+Pytest+Allure+Jenkins web自动化框架,使用Page Object设计模式,将页面的元素和元素之间的操作方法进行分离。它有三层架构,分别为:基础封装层BasePage,PO页面对象层,TestCase测试用例层。

同时使用DDT数据驱动测试思想,将测试数据和测试用例分离,提高代码复用率,减少重复代码的编写。

该框架能够结合 Pytest 进行单元测试,记录日志,并生成 allure 测试报告,最后进行 Jenkins 集成项目实现集成部署,并发送测试报告邮件。

五、代码设计与功能说明

5.1 POM简介:Page Object Modle页面对象模型

  1. 页面对象模型(POM)是一种设计模式,用来管理维护一组web元素集的对象库
  2. 在POM下,应用程序的每一个页面都有一个对应的Page Class
  3. 每一个Page Class 维护着该web页的元素集合、操作这些元素的方法
  4. Page Class中的方法命名最好根据对应的业务场景进行

中心思想:定位元素和测试用例分开,实现代码松耦合的效果

5.2 基础封装层:pages/basePage.py

点击查看代码
class BasePage(object):
    def __init__(self,driver):
        self.driver = driver

    def find_element(self,*loc):
        return self.driver.find_element(*loc)

    def type_text(self,text,*loc):
        self.find_element(*loc).send_keys(text)

    def click(self,*loc):
        self.find_element(*loc).click()

    def clear(self,*loc):
        self.find_element(*loc).clear()

    def get_title(self):
        return self.driver.title

封装查找元素、输入测试数据、点击、清空等常用操作方法。

5.3 PO页面对象层:pages/userLoginPage.py

点击查看代码
from time import sleep
from selenium.webdriver.common.by import By
from pages.basePage import BasePage

class UserLoginPage(BasePage):
    username_input = (By.NAME,'user')
    pwd_input = (By.NAME,'pwd')
    captcha_input = (By.NAME, 'captcha')
    login_btn = (By.CLASS_NAME,'btn')

    def __init__(self,driver):
        BasePage.__init__(self,driver)

    def goto_login_page(self):
        self.driver.get('http://192.168.10.184:8072/jpress-4.1.5/user/login')

    def input_username(self,username):
        self.clear(*self.username_input)
        self.type_text(username,*self.username_input)

    def input_pwd(self,pwd):
        self.clear(*self.pwd_input)
        self.type_text(pwd, *self.pwd_input)

    def input_captcha(self,captcha):
        self.clear(*self.captcha_input)
        self.type_text(captcha,*self.captcha_input)

    def click_login_btn(self):
        self.click(*self.login_btn)

pages/userLoginPage.py 维护了用户登录页面的元素集合:用户名、密码、验证码、登录按钮;操作这些元素的方法:进入用户登录页面、输入用户名、输入密码、输入验证码、点击登录按钮。

5.4 TestCase测试用例层:testcases/testUserLogin.py

点击查看代码
# -*- coding: utf-8 -*-
import pytest
from selenium.webdriver.support.wait import WebDriverWait
from pages.userLoginPage import UserLoginPage
from selenium.webdriver.support import expected_conditions as EC
from selenium import webdriver
from time import sleep
from util import util
from selenium.webdriver.common.by import By

class TestUserLogin(object):
    login_data=[
        ('','admin','这是必填内容'),
        ('admin', '', '这是必填内容'),
        ('admin', 'admin', '用户中心')
    ]

    def setup_class(self):
        option = webdriver.ChromeOptions()
        # 无头模式
        option.add_argument('headless')
        # 沙盒模式运行
        option.add_argument('no-sandbox')
        # 渲染时写入/tmp
        option.add_argument('disable-dev-shm-usage')
        # 专门应对无头浏览器不能最大化屏幕的方案
        option.add_argument("--window-size=1920,1050")

        self.driver = webdriver.Chrome(options=option)
        self.loginPage = UserLoginPage(self.driver)
        self.loginPage.goto_login_page()
        self.logger = util.get_logger()


    # 测试用户登录
    @pytest.mark.parametrize('username,pwd,expected',login_data)
    def test_user_login_username(self,username,pwd,expected):
        # 输入用户名
        self.loginPage.input_username(username)
        # 输入密码
        self.loginPage.input_pwd(pwd)
        # 记录日志
        self.logger.debug('输入用户名称:%s,输入的用户密码:%s',username,pwd)

        if username=='':
            # 点击登录
            self.loginPage.click_login_btn()
            sleep(1)
            try:
                # assert self.driver.find_element_by_id('user-error').text == expected
                assert self.driver.find_element(By.ID,'user-error').text == expected
            except AssertionError as ae:
                self.logger.error("报错,报错:%s", "用户名为空登录", exc_info=1)
        elif pwd == '':
            # 点击登录
            self.loginPage.click_login_btn()
            sleep(1)
            try:
                # assert self.driver.find_element_by_id('pwd-error').text == expected
                assert self.driver.find_element(By.ID,'pwd-error').text == expected
            except AssertionError as ae:
                self.logger.error("报错,报错:%s", "密码为空登录", exc_info=1)
        else:
            # 自动识别验证码
            captcha = util.get_code(self.driver, 'captcha-img')
            # 输入验证码
            self.loginPage.input_captcha(captcha)
            # 点击登录
            self.loginPage.click_login_btn()
            # 等待提示框
            WebDriverWait(self.driver, 5).until(EC.title_is(expected))
            sleep(1)
            # 验证
            try:
                assert self.driver.title == expected
            except AssertionError as ae:
                self.logger.error("报错,报错:%s", "登录", exc_info=1)

if __name__ == '__main__':
    pytest.main(['testUserLogin.py'])

说明:

1)使用chrome的headless无头模式,是因为在Linux服务器上执行该脚本时,服务器上的Chrome无GUI。

2)使用了ddt的思想,将测试数据和测试脚本分离,且一套测试脚本可以执行多组不同测试数据的用例。

3)调用了日志模块,将脚本运行过程日志及报错日志,记录到日志文件中,方便排查问题。

5.5 工具类模块:解决验证码识别、日志打印等问题

点击查看代码

import base64
import json
import os
import pickle
import random
import string
import time
import requests
from PIL import Image
from selenium import webdriver
from selenium.webdriver.common.by import By
import logging.handlers
import datetime

_custom_url = "http://api.jfbym.com/api/YmServer/customApi"
_token = "Z-lvHdVz4hyuYT8_SmrG53VDH7EW4req4mpkN1rQIeY"
_headers = {
    'Content-Type': 'application/json'
}


def common_verify(image, verify_type="10110"):
    # 10110 通用数英1-4位
    payload = {
        "image": base64.b64encode(image).decode(),
        "token": _token,
        "type": verify_type
    }
    resp = requests.post(_custom_url, headers=_headers, data=json.dumps(payload))
    print(resp.text)
    return resp.json()['data']['data']

# id 是要截图的元素的id
def save_img(driver,id):
    # 获取验证码图片
    t = time.time()
    path = os.path.dirname(os.path.dirname(__file__)) + '\\screenshots'
    picture_name1 = path + '\\' + str(t) + '.png'

    driver.save_screenshot(picture_name1)
    # ce = driver.find_element_by_id(id)  # id = captchaimg
    ce = driver.find_element(By.ID,id)

    left = ce.location['x']
    top = ce.location['y']
    right = ce.size['width'] + left
    height = ce.size['height'] + top

    im = Image.open(picture_name1)
    # 抠图
    img = im.crop((left, top, right, height))
    t = time.time()
    picture_name2 = path + '\\' + str(t) + '.png'
    img.save(picture_name2)  # 把验证码的图保存下来
    return picture_name2

def get_code(driver,id):
    picture = save_img(driver,id)
    with open(picture, 'rb') as f:
        s = f.read()
    return common_verify(s)

def gen_random_str():
    rand_str = ''.join(random.sample(string.ascii_letters + string.digits, 8))
    return rand_str

def save_cookie(driver, path):
    with open(path, 'wb') as filehandler:
        cookies = driver.get_cookies()
        print(cookies)
        pickle.dump(cookies, filehandler)


def load_cookie(driver, path):
    with open(path, 'rb') as cookiesfile:
        cookies = pickle.load(cookiesfile)
        for cookie in cookies:
            driver.add_cookie(cookie)

def get_logger():
    path = os.path.dirname(os.path.dirname(__file__)) + '\\logs\\'
    logger = logging.getLogger('mylogger')
    logger.setLevel(logging.DEBUG)

    rf_handler = logging.handlers.TimedRotatingFileHandler(path+'all.log',when='midnight',interval=1,backupCount=7,atTime=datetime.time(0,0,0,0))
    rf_handler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s"'))
    f_handler = logging.FileHandler(path+'error.log')
    f_handler.setLevel(logging.ERROR)
    f_handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(filename)s[:%(lineno)d] - %(message)s"))

    logger.addHandler(rf_handler)
    logger.addHandler(f_handler)
    return logger

if __name__ == '__main__':
    driver = webdriver.Chrome()
    driver.get('http://192.168.10.184:8072/jpress-4.1.5/admin/login')
    id = "captcha-img"
    print(get_code(driver, id))

5.6 主运行方法:runall.py

点击查看代码
# -*- coding: utf-8 -*-
import pytest
import os
from testcases import *

if __name__ == '__main__':
    #批量执行文件夹下的测试用例
    pytest.main(['-s', '-v',
                 'testcases/testAdminLogin.py',
                 'testcases/testArticle.py',
                 'testcases/testUserLogin.py',
                 'testcases/testUserRegister.py',
                 '-q',
                 '--clean-alluredir',
                 '--alluredir', 'reports'])
    os.system("allure generate reports -c -o reports/allure_result/")

总结:

1)批量执行多个测试用例

2)生成Allure测试报告,并将测试报告文件放到 reports/allure_result/路径下

六、GitLab管理代码

虚拟机的Linux发行版:

前提条件:

  • 安装Docker
  • 安装Docker-compose

6.1 安装gitlab

1)服务器中保证有docker、docker-compose

$ docker version
$ docker compose version

2)访问安全
云服务器:设置安全组【阿里云服务器设置】
虚拟机:关闭防火墙

$ systemctl stop firewalld

3)在/usr/local下创建/docker/ gitlab_docker目录

$ cd /usr/local
$ sudo mkdir docker
$ cd docker
$ sudo mkdir gitlab_docker
$ cd gitlab_docker/

4)在/usr/local/docker/gitlab_docker下创建docker-compose.yml文件

$ sudo vi docker-compose.yml

用于编写启动docker的配置信息的文件
配置文件内容:

external_url说明:

  • 虚拟机:虚拟机的ip;
  • 云服务器:服务器的公网ip

5)重启docker,启动gitlab,会自动拉取镜像

$ sudo systemctl docker restart
$ sudo docker compose up -d

查看启动日志:

$ sudo docker logs –f gitlab

6)进入gitlab容器内部查看root密码

$ docker exec –it gitlab bash
$ cat /etc/gitlab/initial_root_password

该文件记录了初始密码(24h后删除),登陆成功后修改密码,如:
Ki91HCFAUcp6Lg1iXCQsT+5M7aFzj6IFOjk16NIvj/I=

2、登录gitlab修改用户密码
3、创建project,设置仓库

4、上传管理代码

七、Jenkins持续集成

7.1 安装JDK

版本:jdk-8u231-linux-x64.tar.gz

$ sudo tar zxvf jdk-8u151-linux-x64.tar.gz -C /usr/local
$ cd /usr/local
$ sudo mv jdk1.8.0_151/ jdk/

7.2 安装Jenkins

$ cd /usr/local
$ mkdir docker
$ cd docker
$ mkdir jenkins_docker
$ cd jenkins_docker/

创建docker-compose.yml文件:

重启docker、启动jenkins

$ sudo systemctl restart docker
启动:
$ sudo docker compose up –d
查看日志是否启动出现问题:
$ sudo docker logs –f jenkins

登录Jenkins、安装默认插件等。

7.3 配置Jenkins

1. 将 /usr/local下的jdk、maven挪到docker/jenkins_docker/data
/usr/local/docker/jenkins_docker/data$ sudo mv /usr/local/jdk/ ./

2. 指定容器卷下的jdk和jenkins
容器内的路径:/var/jenkins_home/
【系统管理-全局工具配置-JDK】
jdk配置:/var/jenkins_home/jdk

3.服务器配置/etc/profile文件:
# jdk env
export JAVA_HOME=/usr/local/docker/jenkins_docker/data/jdk
export PATH=$PATH:$JAVA_HOME/bin

4. 生效配置文件
$ source /etc/profile

7.4 Jenkins环境其他配置

1)Jenkins 容器内部安装Python3,方便在构建脚本中运行py文件

2)Jenkins 容器内部安装 Chrome浏览器和对应版本的chromedriver驱动

7.5 创建SeleniumJpress任务并配置

1)源码管理:从gitlab拉取代码

2)构建步骤:运行runall.py 脚本

3)构建后操作:Allure 报告的存放地址

4)构建后邮件通知:

7.6 系统的配置补充

1)环境变量:Jenkins容器中的环境变量

2)邮件通知:注意密码是授权码

3)SSH服务器配置

4)JDK

5)Aiiure Commandline

八、构建结果及报告查看

控制台输出:

Allure报告:

标签:__,Web,Python,self,driver,Allure,import,docker,def
From: https://www.cnblogs.com/hqq2019-10/p/17935471.html

相关文章

  • postman 如何请求soap的webService方法
    1、SOAP的WebService的一个关键,就是传送的数据,一般都是xml。 如果你的传输格式这里选择xml,也是不行的。 2、要改请求的content-type。另起一行,将Content-Type的值改为text/xml;charset=UTF-8 ......
  • python_selenium定位页面元素
    页面元素常用的定位方法有id、name、css、xpathid和name直接通过对应的值定位class属性值有多个值时,使用css选择器定位只需要其中一个属性值即可,使用xpath时,属性值需要全部写上示例:<inputtype=""autocomplete="on"placeholder="密码"id="pwdid"name="password"c......
  • Python趣味入门11:函数作为参数以及匿名函数
    Python函数本质上是一段代码的集合,扩展对于函数的高级用法,有利于更好的认识Python,函数是Python的核心。本篇扩展了函数的2个应用,一是把函数本身作为参数,二是介绍了匿名函数的应用。1、以函数作为参数:1.1概念Python里可以把函数本身当成是参数,比如Python内置函数map可以通过传......
  • Python趣味入门10:推倒繁琐化烦为简的推导式
    前言《西部世界》的德洛丽丝进入了MAZE迷宫,假设她需要列出一系列的平方数作为密码,来进入迷宫。在以往的代码生成类似的数列需要使用循环语句,写多行语句。Python有了推导式,只需要1句就足够了,下面介绍本篇的主角。本篇的主角是推导式comprehensions(又称解析式),是Python的大杀器之一......
  • python学习笔记3(概述、开发工具、编写方法、print简单语法)
    上一节学习了环境准备、职业方向、执行方式三个方面学习本节内容:(一)概述1、了解python语言1989年荷兰人发明、面向对向、解释型设计哲学:优雅、明确、简单,“人生苦短,我用python”脱水语言,能够和其他语言编写的模块轻松联结,“脱水语言”2、版本发展历程发展历程比较简单,前两天我在网上......
  • python opencv保存摄像头视频
    要使用Python和OpenCV将摄像头视频保存为.mp4格式,您需要使用cv2.VideoWriter的fourcc参数指定视频编码器。在大多数情况下,使用cv2.VideoWriter_fourcc(*'mp4v')将视频保存为.mp4格式是一个好选择。以下是一个示例代码: python复制importcv2 #0代表的是电脑上的默认摄像头......
  • `pip freeze` 是一个命令,它会列出所有已安装的Python库及其版本号。这个命令在Python
    pipfreeze是一个命令,它会列出所有已安装的Python库及其版本号。这个命令在Python的包管理器pip中使用,主要用于生成一个项目的依赖列表。这个列表可以用于在其他环境中重新创建相同的库设置,通常通过使用pipinstall-rrequirements.txt命令,其中requirements.txt是由pipfree......
  • Python 实现HTML 转Word
    之前文章分享过如何使用Spire.DocforPython库将Word文档转为HTML格式,反过来,该库也能实现HTML到Word文档的转换。通过代码进行转换,避免了手动复制粘贴费时间,并且可能会出现错误或格式混乱等问题。Spire.DocforPython库能转换一个HTML文件为WordDocx格式,也能直接将HTML字符......
  • Python 设置word编辑限制 或解除word编辑限制
    限制编辑是一种控制文档或文件的访问权限的功能,它可以防止未经授权的用户对文档进行修改或编辑。通过启用编辑限制,您可以设定不同级别的权限,例如只允许查看、只允许评论或只允许特定用户进行编辑。本文将介绍如何使用Spire.DocforPython在Python程序中限制对Word文档的编辑......
  • 【Python机器学习课程设计】基于卷积神经网络的动物图像分类+数据分析
    一、选题背景在现代社会中,图像分类是计算机视觉领域的一个重要任务。动物图像分类具有广泛的应用,例如生态学研究、动物保护、农业监测等。通过对动物图像进行自动分类,可以帮助人们更好地了解动物种类、数量和分布情况,从而支持相关领域的决策和研究。本研究的目标是使用卷积神......