Selenium3与Python3实战 Web自动化测试框架

Posted 付俊捷

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Selenium3与Python3实战 Web自动化测试框架相关的知识,希望对你有一定的参考价值。

1、行为驱动环境搭建:

  • 行为驱动:Behavie
  • 断言:Pyhamcrest
  • 环境语言:Python
  • 框架:Selenium

安装:

1
2
3
pip install behave
 
pip install pyhamcrest

我们需要新建features包,在该包下要包含至少一个.feature文件和steps包,steps下包含对应执行的测试用例.py文件,场景描述文件写在.feature文件里

目录结构:

 

 

1)新建项目:feature_test

2)新建包:features

3)在features包中新建文件:register.feature  ,写入场景描述:

复制代码
# register.feature

Feature: Register User
  As a developer
  This is my frist BDD project
Scenario: register_test # Given open register website When I open the register website Then I except that the title is "注册" # Scenario: input user_info # Then I set with useremail "nan@163.com" # And I set with username "nan" # And I set with password "111111" # And I set with code "tests" # And I click with register_button # Then I except that text "验证码错误"
复制代码

 

4)在features包中新建environment.py环境: 

复制代码
from selenium import webdriver

def before_all(context):
    context.driver = webdriver.Chrome()

def after_all(context):
    context.driver.close()
复制代码

 

5)在features包中新建steps包

6)在steps包下新建文件:register_user.py:

复制代码
from behave import *

use_step_matcher(\'re\')  # 正则匹配

@when(\'I open the register website\')
def step_register(context):
    context.driver.get("http://www.5itest.cn/register")

@then(\'I except that the title is "([^"]*)"\')  # 匹配除"外的所有字符,0个或多个
def step_register1(context,title_name):
    title = context.driver.title  # 获取网页的title
    assert title_name in title # assert断言
复制代码

 

注意:BDD运行方式,是在终端,进入项目中,执行:behave ,本项目执行如下

 

运行结果:

 


 

 


 

二、日志模块的使用 

 1、logging基本介绍

 logging模块是Python内置的标准模块,主要用于输出运行日志,可以设置输出日志的等级、日志保存路径、日志文件回滚等;相比print,具备如下优点:

  • 可以通过设置不同的日志等级,在release版本中只输出重要信息,而不必显示大量的调试信息;
  • print将所有信息都输出到标准输出中,严重影响开发者从标准输出中查看其它数据;logging则可以由开发者决定将信息输出到什么地方,以及怎么输出;
复制代码
import logging
import sys
 
# 获取logger实例,如果参数为空则返回root logger
logger = logging.getLogger("AppName")
 
# 指定logger输出格式
formatter = logging.Formatter(\'%(asctime)s %(levelname)-8s: %(message)s\')
 
# 文件日志
file_handler = logging.FileHandler("test.log")
file_handler.setFormatter(formatter)  # 可以通过setFormatter指定输出格式
 
# 控制台日志
console_handler = logging.StreamHandler(sys.stdout)
console_handler.formatter = formatter  # 也可以直接给formatter赋值
 
# 为logger添加的日志处理器
logger.addHandler(file_handler)
logger.addHandler(console_handler)
 
# 指定日志的最低输出级别,默认为WARN级别
logger.setLevel(logging.INFO)
 
# 输出不同级别的log
logger.debug(\'this is debug info\')
logger.info(\'this is information\')
logger.warn(\'this is warning message\')
logger.error(\'this is error message\')
logger.fatal(\'this is fatal message, it is same as logger.critical\')
logger.critical(\'this is critical message\')
 
# 2016-10-08 21:59:19,493 INFO    : this is information
# 2016-10-08 21:59:19,493 WARNING : this is warning message
# 2016-10-08 21:59:19,493 ERROR   : this is error message
# 2016-10-08 21:59:19,493 CRITICAL: this is fatal message, it is same as logger.critical
# 2016-10-08 21:59:19,493 CRITICAL: this is critical message
 
# 移除一些日志处理器
logger.removeHandler(file_handler)
复制代码

 

格式化输出日志:

复制代码
# 格式化输出
 
service_name = "Booking"
logger.error(\'%s service is down!\' % service_name)  # 使用python自带的字符串格式化,不推荐
logger.error(\'%s service is down!\', service_name)  # 使用logger的格式化,推荐
logger.error(\'%s service is %s!\', service_name, \'down\')  # 多参数格式化
logger.error(\'{} service is {}\'.format(service_name, \'down\')) # 使用format函数,推荐
 
# 2016-10-08 21:59:19,493 ERROR   : Booking service is down!
复制代码

 

复制代码
logging.basicConfig函数各参数:

filename:指定日志文件名;

filemode:和file函数意义相同,指定日志文件的打开模式,\'w\'或者\'a\';

format:指定输出的格式和内容,format可以输出很多有用的信息,

参数:作用

%(levelno)s:打印日志级别的数值
%(levelname)s:打印日志级别的名称
%(pathname)s:打印当前执行程序的路径,其实就是sys.argv[0]
%(filename)s:打印当前执行程序名
%(funcName)s:打印日志的当前函数
%(lineno)d:打印日志的当前行号
%(asctime)s:打印日志的时间
%(thread)d:打印线程ID
%(threadName)s:打印线程名称
%(process)d:打印进程ID
%(message)s:打印日志信息
datefmt:指定时间格式,同time.strftime();

level:设置日志级别,默认为logging.WARNNING;

stream:指定将日志的输出流,可以指定输出到sys.stderr,sys.stdout或者文件,默认输出到sys.stderr,当stream和filename同时指定时,stream被忽略;
复制代码
复制代码
# StreamHandler:logging.StreamHandler;日志输出到流,可以是sys.stderr,sys.stdout或者文件
# FileHandler:logging.FileHandler;日志输出到文件
# BaseRotatingHandler:logging.handlers.BaseRotatingHandler;基本的日志回滚方式
# RotatingHandler:logging.handlers.RotatingHandler;日志回滚方式,支持日志文件最大数量和日志文件回滚
# TimeRotatingHandler:logging.handlers.TimeRotatingHandler;日志回滚方式,在一定时间区域内回滚日志文件
# SocketHandler:logging.handlers.SocketHandler;远程输出日志到TCP/IP sockets
# DatagramHandler:logging.handlers.DatagramHandler;远程输出日志到UDP sockets
# SMTPHandler:logging.handlers.SMTPHandler;远程输出日志到邮件地址
# SysLogHandler: logging.handlers.SysLogHandler;日志输出到syslog
# NTEventLogHandler: logging.handlers.NTEventLogHandler;远程输出日志到Windows NT/2000/XP的事件日志
# MemoryHandler:logging.handlers.MemoryHandler;日志输出到内存中的指定buffer
# HTTPHandler:logging.handlers.HTTPHandler;通过"GET"或者"POST"远程输出到HTTP服务器
复制代码

 

设置消息等级

# FATAL:致命错误
# CRITICAL:特别糟糕的事情,如内存耗尽、磁盘空间为空,一般很少使用
# ERROR:发生错误时,如IO操作失败或者连接问题
# WARNING:发生很重要的事件,但是并不是错误时,如用户登录密码错误
# INFO:处理请求或者状态变化等日常事务
# DEBUG:调试过程中使用DEBUG等级,如算法中每个循环的中间状态

记录异常信息

当你使用logging模块记录异常信息时,不需要传入该异常对象,只要你直接调用logger.error() 或者 logger.exception()就可以将当前异常记录下来。

复制代码
# 记录异常信息
 
try:
    1 / 0
except:
    # 等同于error级别,但是会额外记录当前抛出的异常堆栈信息
    logger.exception(\'this is an exception message\')
 
# 2016-10-08 21:59:19,493 ERROR   : this is an exception message
# Traceback (most recent call last):
#   File "D:/Git/py_labs/demo/use_logging.py", line 45, in 
#     1 / 0
# ZeroDivisionError: integer division or modulo by zero
复制代码

Configuration 配置方法

logging的配置大致有下面几种方式。

  1. 通过代码进行完整配置,参考开头的例子,主要是通过getLogger方法实现。
  2. 通过代码进行简单配置,下面有例子,主要是通过basicConfig方法实现。
  3. 通过配置文件,下面有例子,主要是通过 logging.config.fileConfig(filepath)

 

demo:日志模块应用

 info级别以上内容会记入指定日志文件中

复制代码
import logging
import os
import datetime

class UserLog(object):

    def __init__(self):
        self.logger1 = logging.getLogger(__name__)  # 生成log对象
        # 以下三行代码为清空上次创建log文件
        logging.Logger.manager.loggerDict.pop(__name__)  # 清空当前文件存于loggerDict的日志实例。(logging 模块为了保证同一个名称引用同一个日志实例,所以就把所有的日志实例全部存在了一个 loggerDict 的字典里,要释放某实例则需清除)
        self.logger1.handlers=[]  # 将handers清空
        self.logger1.removeHandler(self.logger1.handlers) # 移除当前文件的logging相关配置
        if not self.logger1.handlers:  # 如果logger.handlers列表为空,则新添加,否则表示文件存在,可以直接去写日志

            self.logger1.setLevel(logging.DEBUG)
            #控制台输出日志
            #consle = logging.StreamHandler()
            #logger.addHandler(consle)

            #文件名字
            log_dir = os.path.dirname(os.path.abspath(__file__))
            log_file = datetime.datetime.now().strftime("%Y-%m-%d") + ".log"
            log_name = os.path.join(log_dir,"logs",log_file) # 日志文件名
            #文件输出日志
            self.file_handle = logging.FileHandler(log_name,\'a\',encoding=\'utf-8\')
            self.file_handle.setLevel(logging.INFO)
            formatter = logging.Formatter(\'%(asctime)s %(filename)s--> %(funcName)s %(levelno)s: %(levelname)s ----->%(message)s\')
            self.file_handle.setFormatter(formatter)
            self.logger1.addHandler(self.file_handle)


    def get_log(self):
        return self.logger1
    
    def close_handle(self):
        self.logger1.removeHandler(self.file_handle)
        self.file_handle.close()

# if __name__ == \'__main__\':
#     user = UserLog()
#     log = user.get_log()
#     log.debug(\'test\')
#     user.close_handle()
复制代码

 

"""
logging.Logger.manager.loggerDict.pop(__name__) self.logger1.handlers=[] self.logger1.removeHandler(self.logger1.handlers)
"""

解析:logging 模块为了保证同一个名称引用同一个日志实例,所以就把所有的日志实例全部存 在了一个 loggerDict 的字典里,。除非程序退出, 否则创建的日志实例引用是不会释放的, 日志实例里的 handlers 也不会释放.所以需要做以上操作处理。


 

在测试用例中操作:

复制代码
# 1)setUpClass
    @classmethod
    def setUpClass(cls):
        cls.log = UserLog()
        cls.logger = cls.log.get_log()
        cls.driver = webdriver.Chrome()
        cls.driver.get(\'http://www.5itest.cn/register\')
        cls.driver.maximize_window()
        cls.file_name = setting.code_path
        cls.login = RegisterBusiness(cls.driver)

# 2)setUp
    def setUp(self):
        self.driver.refresh()
        self.logger.info("this is chrome_tset")

# 3)tearDownClass
    @classmethod
    def tearDownClass(cls):
        cls.log.close_handle()
        cls.driver.close()
复制代码

 

执行结果:

 


 三、持续集成

1、Jenkins 环境搭建,参考:https://www.cnblogs.com/Eric15/p/9834120.html

2、新建任务

 Jenkins安装及配置好环境后,我们到Jenkins安装目录下执行命令:jenki.exe start  ,启动服务器

 浏览器访问:127.0.0.1:8080 , 进入服务器(需账号密码)

 1)新建我们的第一个任务

  

  2)命名并选择‘构建一个自由风格的软件项目’

 

 3)进入下一步后,暂时只需要进行下面简单配置操作:

  源码管理 → 先选无

  构建 → 选择:执行Windows批处理命令

  在命令里配置执行程序:python G:\\Python\\python_selenium\\case\\first_case.py

  保存

  执行 ‘立即构建’,之后可点击 ‘控制台输出’查看运行结果

  跳出界面 → 


 

因本项目是在虚拟环境下配置的,故在构建时,python执行文件应该选择虚拟环境中的python执行文件:

 


 

3、Jenkins邮件通知

 

 

以上是关于Selenium3与Python3实战 Web自动化测试框架的主要内容,如果未能解决你的问题,请参考以下文章

Selenium3与Python3实战 Web自动化测试框架

Selenium3与Python3实战 Web自动化测试框架

Selenium3与Python3实战 Web自动化测试框架

Selenium3 Python3 Web自动化测试从基础到项目实战之二浏览器的不同设置

python3.6+selenium3.13 自动化测试项目实战一

selenium3 web自动化测试框架 三:Unittest介绍及项目实战中的运用