【Selenium+Pytest+allure报告生成自动化测试框架】附带项目源码和项目部署文档

目录

前言

【文章末尾给大家留下了大量的福利】

测试框架简介

首先管理时间

添加配置文件

conf.py

config.ini

读取配置文件

记录操作日志

简单理解POM模型

简单学习元素定位

管理页面元素

封装Selenium基类

创建页面对象

简单了解Pytest

pytest.ini

编写测试用例

conftest.py

执行用例

发送邮件

pytest使用allure测试报告

allure安装

allure初体验

allure装饰器介绍

报告的生成和展示

allure发生错误截图

开源地址

前言

selenium自动化+ pytest测试框架+allure报告

本章你需要

  • 一定的python基础——至少明白类与对象,封装继承

  • 一定的selenium基础——本篇不讲selenium,不会的可以自己去看selenium中文翻译网

  • 文章末尾给大家留下了大量的福利】

 

测试框架简介

  • 测试框架有什么优点呢:

    • 代码复用率高,如果不使用框架的话,代码会很冗余
    • 可以组装日志、报告、邮件等一些高级功能
    • 提高元素等数据的可维护性,元素发生变化时,只需要更新一下配置文件
    • 使用更灵活的PageObject设计模式
  • 测试框架的整体目录

 

 这样一个简单的框架结构就清晰了。

知道了以上这些我们就开始吧!

我们在项目中先按照上面的框架指引,建好每一项目录。

注意:python包为是的,都需要添加一个__init__.py文件以标识此目录为一个python包。


首先管理时间

首先呢,因为我们很多的模块会用到时间戳,或者日期等等字符串,所以我们先单独把时间封装成一个模块。

然后让其他模块来调用即可。在utils目录新建times.py模块

<span style="color:#596172"><span style="background-color:#ffffff"><code class="language-python"><span style="color:#5c6370"><em>#!/usr/bin/env python3</em></span>
<span style="color:#5c6370"><em># -*- coding:utf-8 -*-</em></span>
<span style="color:#7171bf">import</span> time
<span style="color:#7171bf">import</span> datetime
<span style="color:#7171bf">from</span> functools <span style="color:#7171bf">import</span> wraps
 
 
<span style="color:#7171bf">def</span> <span style="color:#61aeee">timestamp</span>():
    <span style="color:#98c379">"""时间戳"""</span>
    <span style="color:#7171bf">return</span> time.time()
 
 
<span style="color:#7171bf">def</span> <span style="color:#61aeee">dt_strftime</span>(fmt=<span style="color:#98c379">"%Y%m"</span>):
    <span style="color:#98c379">"""
    datetime格式化时间
    :param fmt "%Y%m%d %H%M%S
    """</span>
    <span style="color:#7171bf">return</span> datetime.datetime.now().strftime(fmt)
 
 
<span style="color:#7171bf">def</span> <span style="color:#61aeee">sleep</span>(seconds=<span style="color:#d19a66">1.0</span>):
    <span style="color:#98c379">"""
    睡眠时间
    """</span>
    time.sleep(seconds)
 
 
<span style="color:#7171bf">def</span> <span style="color:#61aeee">running_time</span>(func):
    <span style="color:#98c379">"""函数运行时间"""</span>
 
<span style="color:#61aeee">    @wraps(func)</span>
    <span style="color:#7171bf">def</span> <span style="color:#61aeee">wrapper</span>(*args, **kwargs):
        start = timestamp()
        res = func(*args, **kwargs)
        <span style="color:#7171bf">print</span>(<span style="color:#98c379">"校验元素done!用时%.3f秒!"</span> % (timestamp() - start))
        <span style="color:#7171bf">return</span> res
 
    <span style="color:#7171bf">return</span> wrapper
 
 
<span style="color:#7171bf">if</span> __name__ == <span style="color:#98c379">'__main__'</span>:
    <span style="color:#7171bf">print</span>(dt_strftime(<span style="color:#98c379">"%Y%m%d%H%M%S"</span>))
 
</code></span></span>

添加配置文件

配置文件总是项目中必不可少的部分!

将固定不变的信息集中在固定的文件中

conf.py

项目中都应该有一个文件对整体的目录进行管理,我也在这个python项目中设置了此文件。

在项目config目录创建conf.py文件,所有的目录配置信息写在这个文件里面。

<span style="color:#596172"><span style="background-color:#ffffff"><code class="language-python"><span style="color:#5c6370"><em>#!/usr/bin/env python3</em></span>
<span style="color:#5c6370"><em># -*- coding:utf-8 -*-</em></span>
<span style="color:#7171bf">import</span> os
<span style="color:#7171bf">from</span> selenium.webdriver.common.by <span style="color:#7171bf">import</span> By
<span style="color:#7171bf">from</span> utils.times <span style="color:#7171bf">import</span> dt_strftime
 
 
<span style="color:#7171bf">class</span> <span style="color:#61aeee">ConfigManager</span>(<span style="color:#61aeee">object</span>):
    <span style="color:#5c6370"><em># 项目目录</em></span>
    BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
 
    <span style="color:#5c6370"><em># 页面元素目录</em></span>
    ELEMENT_PATH = os.path.join(BASE_DIR, <span style="color:#98c379">'page_element'</span>)
 
    <span style="color:#5c6370"><em># 报告文件</em></span>
    REPORT_FILE = os.path.join(BASE_DIR, <span style="color:#98c379">'report.html'</span>)
 
    <span style="color:#5c6370"><em># 元素定位的类型</em></span>
    LOCATE_MODE = {
        <span style="color:#98c379">'css'</span>: By.CSS_SELECTOR,
        <span style="color:#98c379">'xpath'</span>: By.XPATH,
        <span style="color:#98c379">'name'</span>: By.NAME,
        <span style="color:#98c379">'id'</span>: By.ID,
        <span style="color:#98c379">'class'</span>: By.CLASS_NAME
    }
 
    <span style="color:#5c6370"><em># 邮件信息</em></span>
    EMAIL_INFO = {
        <span style="color:#98c379">'username'</span>: <span style="color:#98c379">'1084502012@qq.com'</span>,  <span style="color:#5c6370"><em># 切换成你自己的地址</em></span>
        <span style="color:#98c379">'password'</span>: <span style="color:#98c379">'QQ邮箱授权码'</span>,
        <span style="color:#98c379">'smtp_host'</span>: <span style="color:#98c379">'smtp.qq.com'</span>,
        <span style="color:#98c379">'smtp_port'</span>: <span style="color:#d19a66">465</span>
    }
 
    <span style="color:#5c6370"><em># 收件人</em></span>
    ADDRESSEE = [
        <span style="color:#98c379">'1084502012@qq.com'</span>,
    ]
 
<span style="color:#61aeee">    @property</span>
    <span style="color:#7171bf">def</span> <span style="color:#61aeee">log_file</span>(self):
        <span style="color:#98c379">"""日志目录"""</span>
        log_dir = os.path.join(self.BASE_DIR, <span style="color:#98c379">'logs'</span>)
        <span style="color:#7171bf">if</span> <span style="color:#7171bf">not</span> os.path.exists(log_dir):
            os.makedirs(log_dir)
        <span style="color:#7171bf">return</span> os.path.join(log_dir, <span style="color:#98c379">'{}.log'</span>.<span style="color:#7171bf">format</span>(dt_strftime()))
 
<span style="color:#61aeee">    @property</span>
    <span style="color:#7171bf">def</span> <span style="color:#61aeee">ini_file</span>(self):
        <span style="color:#98c379">"""配置文件"""</span>
        ini_file = os.path.join(self.BASE_DIR, <span style="color:#98c379">'config'</span>, <span style="color:#98c379">'config.ini'</span>)
        <span style="color:#7171bf">if</span> <span style="color:#7171bf">not</span> os.path.exists(ini_file):
            <span style="color:#7171bf">raise</span> FileNotFoundError(<span style="color:#98c379">"配置文件%s不存在!"</span> % ini_file)
        <span style="color:#7171bf">return</span> ini_file
 
 
cm = ConfigManager()
<span style="color:#7171bf">if</span> __name__ == <span style="color:#98c379">'__main__'</span>:
    <span style="color:#7171bf">print</span>(cm.BASE_DIR)
</code></span></span>

这个conf文件我模仿了Django的settings.py文件的设置风格,但是又有些许差异。

在这个文件中我们可以设置自己的各个目录,也可以查看自己当前的目录。

遵循了约定:不变的常量名全部大写,函数名小写。看起来整体美观。

config.ini

在项目config目录新建一个config.ini文件,里面暂时先放入我们的需要测试的URL

<span style="color:#596172"><span style="background-color:#ffffff"><code class="language-ini"><span style="color:#e06c75">[HOST]</span>
<span style="color:#d19a66">HOST</span> = https://www.baidu.com
</code></span></span>

读取配置文件

配置文件创建好了,接下来我们需要读取这个配置文件以使用里面的信息。

我们在common目录中新建一个readconfig.py文件

<span style="color:#596172"><span style="background-color:#ffffff"><code class="language-python"><span style="color:#5c6370"><em>#!/usr/bin/env python3</em></span>
<span style="color:#5c6370"><em># -*- coding:utf-8 -*-</em></span>
<span style="color:#7171bf">import</span> configparser
<span style="color:#7171bf">from</span> config.conf <span style="color:#7171bf">import</span> cm
 
HOST = <span style="color:#98c379">'HOST'</span>
 
 
<span style="color:#7171bf">class</span> <span style="color:#61aeee">ReadConfig</span>(<span style="color:#61aeee">object</span>):
    <span style="color:#98c379">"""配置文件"""</span>
 
    <span style="color:#7171bf">def</span> <span style="color:#61aeee">__init__</span>(self):
        self.config = configparser.RawConfigParser()  <span style="color:#5c6370"><em># 当有%的符号时请使用Raw读取</em></span>
        self.config.read(cm.ini_file, encoding=<span style="color:#98c379">'utf-8'</span>)
 
    <span style="color:#7171bf">def</span> <span style="color:#61aeee">_get</span>(self, section, option):
        <span style="color:#98c379">"""获取"""</span>
        <span style="color:#7171bf">return</span> self.config.get(section, option)
 
    <span style="color:#7171bf">def</span> <span style="color:#61aeee">_set</span>(self, section, option, value):
        <span style="color:#98c379">"""更新"""</span>
        self.config.<span style="color:#7171bf">set</span>(section, option, value)
        <span style="color:#7171bf">with</span> <span style="color:#7171bf">open</span>(cm.ini_file, <span style="color:#98c379">'w'</span>) <span style="color:#7171bf">as</span> f:
            self.config.write(f)
 
<span style="color:#61aeee">    @property</span>
    <span style="color:#7171bf">def</span> <span style="color:#61aeee">url</span>(self):
        <span style="color:#7171bf">return</span> self._get(HOST, HOST)
 
 
ini = ReadConfig()
 
<span style="color:#7171bf">if</span> __name__ == <span style="color:#98c379">'__main__'</span>:
    <span style="color:#7171bf">print</span>(ini.url)
</code></span></span>

可以看到我们用python内置的configparser模块对config.ini文件进行了读取。

对于url值的提取,我使用了高阶语法@property属性值,写法更简单。


记录操作日志

日志,大家应该都很熟悉这个名词,就是记录代码中的动作。

utils目录中新建logger.py文件。

这个文件就是我们用来在自动化测试过程中记录一些操作步骤的。

<span style="color:#596172"><span style="background-color:#ffffff"><code class="language-python"><span style="color:#5c6370"><em>#!/usr/bin/env python3</em></span>
<span style="color:#5c6370"><em># -*- coding:utf-8 -*-</em></span>
<span style="color:#7171bf">import</span> logging
<span style="color:#7171bf">from</span> config.conf <span style="color:#7171bf">import</span> cm
 
 
<span style="color:#7171bf">class</span> <span style="color:#61aeee">Log</span>:
    <span style="color:#7171bf">def</span> <span style="color:#61aeee">__init__</span>(self):
        self.logger = logging.getLogger()
        <span style="color:#7171bf">if</span> <span style="color:#7171bf">not</span> self.logger.handlers:
            self.logger.setLevel(logging.DEBUG)
 
            <span style="color:#5c6370"><em># 创建一个handle写入文件</em></span>
            fh = logging.FileHandler(cm.log_file, encoding=<span style="color:#98c379">'utf-8'</span>)
            fh.setLevel(logging.INFO)
 
            <span style="color:#5c6370"><em># 创建一个handle输出到控制台</em></span>
            ch = logging.StreamHandler()
            ch.setLevel(logging.INFO)
 
            <span style="color:#5c6370"><em># 定义输出的格式</em></span>
            formatter = logging.Formatter(self.fmt)
            fh.setFormatter(formatter)
            ch.setFormatter(formatter)
 
            <span style="color:#5c6370"><em># 添加到handle</em></span>
            self.logger.addHandler(fh)
            self.logger.addHandler(ch)
 
<span style="color:#61aeee">    @property</span>
    <span style="color:#7171bf">def</span> <span style="color:#61aeee">fmt</span>(self):
        <span style="color:#7171bf">return</span> <span style="color:#98c379">'%(levelname)s\t%(asctime)s\t[%(filename)s:%(lineno)d]\t%(message)s'</span>
 
 
log = Log().logger
 
<span style="color:#7171bf">if</span> __name__ == <span style="color:#98c379">'__main__'</span>:
    log.info(<span style="color:#98c379">'hello world'</span>)
</code></span></span>

在终端中运行该文件,就看到命令行打印出了:

<span style="color:#596172"><span style="background-color:#ffffff"><code class="language-shell">INFO    2020-12-01 16:00:05,467    [logger.py:38]    hello world
</code></span></span>

然后在项目logs目录下生成了当月的日志文件。


简单理解POM模型

由于下面要讲元素相关的,所以首先理解一下POM模型

Page Object模式具有以下几个优点。

该观点来自 《Selenium自动化测试——基于Python语言》

  • 抽象出对象可以最大程度地降低开发人员修改页面代码对测试的影响, 所以, 你仅需要对页
    面对象进行调整, 而对测试没有影响;
  • 可以在多个测试用例中复用一部分测试代码;
  • 测试代码变得更易读、 灵活、 可维护

Page Object模式图

  •  basepage ——selenium的基类,对selenium的方法进行封装
  • pageelements——页面元素,把页面元素单独提取出来,放入一个文件中
  • searchpage ——页面对象类,把selenium方法和页面元素进行整合
  • testcase ——使用pytest对整合的searchpage进行测试用例编写
     

通过上图我们可以看出,通过POM模型思想,我们把:

  • selenium方法
  • 页面元素
  • 页面对象
  • 测试用例

以上四种代码主体进行了拆分,虽然在用例很少的情况下做会增加代码,但是当用例多的时候意义很大,代码量会在用例增加的时候显著减少。我们维护代码变得更加直观明显,代码可读性也变得比工厂模式强很多,代码复用率也极大的得到了提高。


简单学习元素定位

在日常的工作中,我见过很多在浏览器中直接在浏览器中右键Copy Xpath复制元素的同学。这样获得的元素表达式放在 webdriver 中去运行往往是不够稳定的,像前端的一些微小改动,都会引起元素无法定位的NoSuchElementException报错。

所以在实际工作和学习中我们应该加强自己的元素定位能力,尽可能的采用xpath和CSS selector 这种相对稳定的定位语法。由于CSS selector的语法生硬难懂,对新手很不友好,而且相比xpath缺少一些定位语法。所以我们选择xpath进行我们的元素定位语法。
 

xpath#

语法规则

 

定位工具

  • chropath
    • 优点:这是一个Chrome浏览器的测试定位插件,类似于firepath,本人试用了一下整体感觉非常好。对小白的友好度很好。
    • 缺点:安装这个插件需要FQ。
  • Katalon录制工具
    • 录制出来的脚本里面也会有定位元素的信息
  • 自己写——本人推荐这种
    • 优点:本人推荐的方式,因为当熟练到一定程度的时候,写出来的会更直观简洁,并且在运行自动化测试中出现问题时,能快速定位。
    • 缺点:需要一定xpathCSS selector语法积累,不太容易上手。

管理页面元素

本教程选择的测试地址是百度首页,所以对应的元素也是百度首页的。

项目框架设计中有一个目录page_element就是专门来存放定位元素的文件的。

通过对各种配置文件的对比,我在这里选择的是YAML文件格式。其易读,交互性好。

我们在page_element中新建一个search.yaml文件。

<span style="color:#596172"><span style="background-color:#ffffff"><code class="language-yaml"><span style="color:#98c379">搜索框:</span> <span style="color:#98c379">"id==kw"</span>
<span style="color:#98c379">候选:</span> <span style="color:#98c379">"css==.bdsug-overflow"</span>
<span style="color:#98c379">搜索候选:</span> <span style="color:#98c379">"css==#form div li"</span>
<span style="color:#98c379">搜索按钮:</span> <span style="color:#98c379">"id==su"</span>
</code></span></span>

元素定位文件创建好了,下来我们需要读取这个文件。

common目录中创建readelement.py文件。

<span style="color:#596172"><span style="background-color:#ffffff"><code class="language-python"><span style="color:#5c6370"><em>#!/usr/bin/env python3</em></span>
<span style="color:#5c6370"><em># -*- coding:utf-8 -*-</em></span>
<span style="color:#7171bf">import</span> os
<span style="color:#7171bf">import</span> yaml
<span style="color:#7171bf">from</span> config.conf <span style="color:#7171bf">import</span> cm
 
 
<span style="color:#7171bf">class</span> <span style="color:#61aeee">Element</span>(<span style="color:#61aeee">object</span>):
    <span style="color:#98c379">"""获取元素"""</span>
 
    <span style="color:#7171bf">def</span> <span style="color:#61aeee">__init__</span>(self, name):
        self.file_name = <span style="color:#98c379">'%s.yaml'</span> % name
        self.element_path = os.path.join(cm.ELEMENT_PATH, self.file_name)
        <span style="color:#7171bf">if</span> <span style="color:#7171bf">not</span> os.path.exists(self.element_path):
            <span style="color:#7171bf">raise</span> FileNotFoundError(<span style="color:#98c379">"%s 文件不存在!"</span> % self.element_path)
        <span style="color:#7171bf">with</span> <span style="color:#7171bf">open</span>(self.element_path, encoding=<span style="color:#98c379">'utf-8'</span>) <span style="color:#7171bf">as</span> f:
            self.data = yaml.safe_load(f)
 
    <span style="color:#7171bf">def</span> <span style="color:#61aeee">__getitem__</span>(self, item):
        <span style="color:#98c379">"""获取属性"""</span>
        data = self.data.get(item)
        <span style="color:#7171bf">if</span> data:
            name, value = data.split(<span style="color:#98c379">'=='</span>)
            <span style="color:#7171bf">return</span> name, value
        <span style="color:#7171bf">raise</span> ArithmeticError(<span style="color:#98c379">"{}中不存在关键字:{}"</span>.<span style="color:#7171bf">format</span>(self.file_name, item))
 
 
<span style="color:#7171bf">if</span> __name__ == <span style="color:#98c379">'__main__'</span>:
    search = Element(<span style="color:#98c379">'search'</span>)
    <span style="color:#7171bf">print</span>(search[<span style="color:#98c379">'搜索框'</span>])
</code></span></span>

通过特殊方法__getitem__实现调用任意属性,读取yaml中的值。

这样我们就实现了定位元素的存储和调用。

但是还有一个问题,我们怎么样才能确保我们写的每一项元素不出错,人为的错误是不可避免的,但是我们可以通过代码来运行对文件的审查。当前也不能所有问题都能发现。

所以我们编写一个文件,在script脚本文件目录中创建inspect.py文件,对所有的元素yaml文件进行审查。
 

<span style="color:#596172"><span style="background-color:#ffffff"><code class="language-python"><span style="color:#5c6370"><em>#!/usr/bin/env python3</em></span>
<span style="color:#5c6370"><em># -*- coding:utf-8 -*-</em></span>
<span style="color:#7171bf">import</span> os
<span style="color:#7171bf">import</span> yaml
<span style="color:#7171bf">from</span> config.conf <span style="color:#7171bf">import</span> cm
<span style="color:#7171bf">from</span> utils.times <span style="color:#7171bf">import</span> running_time
 
 
<span style="color:#61aeee">@running_time</span>
<span style="color:#7171bf">def</span> <span style="color:#61aeee">inspect_element</span>():
    <span style="color:#98c379">"""检查所有的元素是否正确
    只能做一个简单的检查
    """</span>
    <span style="color:#7171bf">for</span> files <span style="color:#7171bf">in</span> os.listdir(cm.ELEMENT_PATH):
        _path = os.path.join(cm.ELEMENT_PATH, files)
        <span style="color:#7171bf">with</span> <span style="color:#7171bf">open</span>(_path, encoding=<span style="color:#98c379">'utf-8'</span>) <span style="color:#7171bf">as</span> f:
            data = yaml.safe_load(f)
        <span style="color:#7171bf">for</span> k <span style="color:#7171bf">in</span> data.values():
            <span style="color:#7171bf">try</span>:
                pattern, value = k.split(<span style="color:#98c379">'=='</span>)
            <span style="color:#7171bf">except</span> ValueError:
                <span style="color:#7171bf">raise</span> Exception(<span style="color:#98c379">"元素表达式中没有`==`"</span>)
            <span style="color:#7171bf">if</span> pattern <span style="color:#7171bf">not</span> <span style="color:#7171bf">in</span> cm.LOCATE_MODE:
                <span style="color:#7171bf">raise</span> Exception(<span style="color:#98c379">'%s中元素【%s】没有指定类型'</span> % (_path, k))
            <span style="color:#7171bf">elif</span> pattern == <span style="color:#98c379">'xpath'</span>:
                <span style="color:#7171bf">assert</span> <span style="color:#98c379">'//'</span> <span style="color:#7171bf">in</span> value,\
                    <span style="color:#98c379">'%s中元素【%s】xpath类型与值不配'</span> % (_path, k)
            <span style="color:#7171bf">elif</span> pattern == <span style="color:#98c379">'css'</span>:
                <span style="color:#7171bf">assert</span> <span style="color:#98c379">'//'</span> <span style="color:#7171bf">not</span> <span style="color:#7171bf">in</span> value, \
                    <span style="color:#98c379">'%s中元素【%s]css类型与值不配'</span> % (_path, k)
            <span style="color:#7171bf">else</span>:
                <span style="color:#7171bf">assert</span> value, <span style="color:#98c379">'%s中元素【%s】类型与值不匹配'</span> % (_path, k)
 
 
<span style="color:#7171bf">if</span> __name__ == <span style="color:#98c379">'__main__'</span>:
    inspect_element()
</code></span></span>

执行该文件:

<span style="color:#596172"><span style="background-color:#ffffff"><code class="language-powershell">校验元素done!用时<span style="color:#d19a66">0.002</span>秒!
</code></span></span>

可以看到,很短的时间内,我们就对所填写的YAML文件进行了审查。

现在我们基本所需要的组件已经大致完成了。

接下来我们将进行最重要的一环,封装selenium。


封装Selenium基类

在工厂模式种我们是这样写的:

<span style="color:#596172"><span style="background-color:#ffffff"><code class="language-python"><span style="color:#5c6370"><em>#!/usr/bin/env python3</em></span>
<span style="color:#5c6370"><em># -*- coding:utf-8 -*-</em></span>
<span style="color:#7171bf">import</span> time
<span style="color:#7171bf">from</span> selenium <span style="color:#7171bf">import</span> webdriver
 
 
driver = webdriver.Chrome()
driver.get(<span style="color:#98c379">'https://www.baidu.com'</span>)
driver.find_element_by_xpath(<span style="color:#98c379">"//input[@id='kw']"</span>).send_keys(<span style="color:#98c379">'selenium'</span>)
driver.find_element_by_xpath(<span style="color:#98c379">"//input[@id='su']"</span>).click()
time.sleep(<span style="color:#d19a66">5</span>)
driver.quit()
</code></span></span>

很直白,简单,又明了。

创建driver对象,打开百度网页,搜索selenium,点击搜索,然后停留5秒,查看结果,最后关闭浏览器。

那我们为什么要封装selenium的方法呢。首先我们上述这种较为原始的方法,基本不适用于平时做UI自动化测试的,因为在UI界面实际运行情况远远比较复杂,可能因为网络原因,或者控件原因,我们元素还没有显示出来,就进行点击或者输入。所以我们需要封装selenium方法,通过内置的显式等待或一定的条件语句,才能构建一个稳定的方法。而且把selenium方法封装起来,有利于平时的代码维护。

我们在page目录创建webpage.py文件。
 

<span style="color:#596172"><span style="background-color:#ffffff"><code class="language-python"><span style="color:#5c6370"><em>#!/usr/bin/env python3</em></span>
<span style="color:#5c6370"><em># -*- coding:utf-8 -*-</em></span>
<span style="color:#98c379">"""
selenium基类
本文件存放了selenium基类的封装方法
"""</span>
<span style="color:#7171bf">from</span> selenium.webdriver.support <span style="color:#7171bf">import</span> expected_conditions <span style="color:#7171bf">as</span> EC
<span style="color:#7171bf">from</span> selenium.webdriver.support.ui <span style="color:#7171bf">import</span> WebDriverWait
<span style="color:#7171bf">from</span> selenium.common.exceptions <span style="color:#7171bf">import</span> TimeoutException
 
<span style="color:#7171bf">from</span> config.conf <span style="color:#7171bf">import</span> cm
<span style="color:#7171bf">from</span> utils.times <span style="color:#7171bf">import</span> sleep
<span style="color:#7171bf">from</span> utils.logger <span style="color:#7171bf">import</span> log
 
 
<span style="color:#7171bf">class</span> <span style="color:#61aeee">WebPage</span>(<span style="color:#61aeee">object</span>):
    <span style="color:#98c379">"""selenium基类"""</span>
 
    <span style="color:#7171bf">def</span> <span style="color:#61aeee">__init__</span>(self, driver):
        <span style="color:#5c6370"><em># self.driver = webdriver.Chrome()</em></span>
        self.driver = driver
        self.timeout = <span style="color:#d19a66">20</span>
        self.wait = WebDriverWait(self.driver, self.timeout)
 
    <span style="color:#7171bf">def</span> <span style="color:#61aeee">get_url</span>(self, url):
        <span style="color:#98c379">"""打开网址并验证"""</span>
        self.driver.maximize_window()
        self.driver.set_page_load_timeout(<span style="color:#d19a66">60</span>)
        <span style="color:#7171bf">try</span>:
            self.driver.get(url)
            self.driver.implicitly_wait(<span style="color:#d19a66">10</span>)
            log.info(<span style="color:#98c379">"打开网页:%s"</span> % url)
        <span style="color:#7171bf">except</span> TimeoutException:
            <span style="color:#7171bf">raise</span> TimeoutException(<span style="color:#98c379">"打开%s超时请检查网络或网址服务器"</span> % url)
 
<span style="color:#61aeee">    @staticmethod</span>
    <span style="color:#7171bf">def</span> <span style="color:#61aeee">element_locator</span>(func, locator):
        <span style="color:#98c379">"""元素定位器"""</span>
        name, value = locator
        <span style="color:#7171bf">return</span> func(cm.LOCATE_MODE[name], value)
 
    <span style="color:#7171bf">def</span> <span style="color:#61aeee">find_element</span>(self, locator):
        <span style="color:#98c379">"""寻找单个元素"""</span>
        <span style="color:#7171bf">return</span> WebPage.element_locator(<span style="color:#7171bf">lambda</span> *args: self.wait.until(
            EC.presence_of_element_located(args)), locator)
 
    <span style="color:#7171bf">def</span> <span style="color:#61aeee">find_elements</span>(self, locator):
        <span style="color:#98c379">"""查找多个相同的元素"""</span>
        <span style="color:#7171bf">return</span> WebPage.element_locator(<span style="color:#7171bf">lambda</span> *args: self.wait.until(
            EC.presence_of_all_elements_located(args)), locator)
 
    <span style="color:#7171bf">def</span> <span style="color:#61aeee">elements_num</span>(self, locator):
        <span style="color:#98c379">"""获取相同元素的个数"""</span>
        number = <span style="color:#7171bf">len</span>(self.find_elements(locator))
        log.info(<span style="color:#98c379">"相同元素:{}"</span>.<span style="color:#7171bf">format</span>((locator, number)))
        <span style="color:#7171bf">return</span> number
 
    <span style="color:#7171bf">def</span> <span style="color:#61aeee">input_text</span>(self, locator, txt):
        <span style="color:#98c379">"""输入(输入前先清空)"""</span>
        sleep(<span style="color:#d19a66">0.5</span>)
        ele = self.find_element(locator)
        ele.clear()
        ele.send_keys(txt)
        log.info(<span style="color:#98c379">"输入文本:{}"</span>.<span style="color:#7171bf">format</span>(txt))
 
    <span style="color:#7171bf">def</span> <span style="color:#61aeee">is_click</span>(self, locator):
        <span style="color:#98c379">"""点击"""</span>
        self.find_element(locator).click()
        sleep()
        log.info(<span style="color:#98c379">"点击元素:{}"</span>.<span style="color:#7171bf">format</span>(locator))
 
    <span style="color:#7171bf">def</span> <span style="color:#61aeee">element_text</span>(self, locator):
        <span style="color:#98c379">"""获取当前的text"""</span>
        _text = self.find_element(locator).text
        log.info(<span style="color:#98c379">"获取文本:{}"</span>.<span style="color:#7171bf">format</span>(_text))
        <span style="color:#7171bf">return</span> _text
 
<span style="color:#61aeee">    @property</span>
    <span style="color:#7171bf">def</span> <span style="color:#61aeee">get_source</span>(self):
        <span style="color:#98c379">"""获取页面源代码"""</span>
        <span style="color:#7171bf">return</span> self.driver.page_source
 
    <span style="color:#7171bf">def</span> <span style="color:#61aeee">refresh</span>(self):
        <span style="color:#98c379">"""刷新页面F5"""</span>
        self.driver.refresh()
        self.driver.implicitly_wait(<span style="color:#d19a66">30</span>)
</code></span></span>

在文件中我们对主要用了显式等待对selenium的click,send_keys等方法,做了二次封装。提高了运行的成功率。

好了我们完成了POM模型的一半左右的内容。接下来我们们进入页面对象。


创建页面对象

page_object目录下创建一个searchpage.py文件。

<span style="color:#596172"><span style="background-color:#ffffff"><code class="language-python"><span style="color:#5c6370"><em>#!/usr/bin/env python3</em></span>
<span style="color:#5c6370"><em># -*- coding:utf-8 -*-</em></span>
<span style="color:#7171bf">from</span> page.webpage <span style="color:#7171bf">import</span> WebPage, sleep
<span style="color:#7171bf">from</span> common.readelement <span style="color:#7171bf">import</span> Element
 
search = Element(<span style="color:#98c379">'search'</span>)
 
 
<span style="color:#7171bf">class</span> <span style="color:#61aeee">SearchPage</span>(<span style="color:#61aeee">WebPage</span>):
    <span style="color:#98c379">"""搜索类"""</span>
 
    <span style="color:#7171bf">def</span> <span style="color:#61aeee">input_search</span>(self, content):
        <span style="color:#98c379">"""输入搜索"""</span>
        self.input_text(search[<span style="color:#98c379">'搜索框'</span>], txt=content)
        sleep()
 
<span style="color:#61aeee">    @property</span>
    <span style="color:#7171bf">def</span> <span style="color:#61aeee">imagine</span>(self):
        <span style="color:#98c379">"""搜索联想"""</span>
        <span style="color:#7171bf">return</span> [x.text <span style="color:#7171bf">for</span> x <span style="color:#7171bf">in</span> self.find_elements(search[<span style="color:#98c379">'候选'</span>])]
 
    <span style="color:#7171bf">def</span> <span style="color:#61aeee">click_search</span>(self):
        <span style="color:#98c379">"""点击搜索"""</span>
        self.is_click(search[<span style="color:#98c379">'搜索按钮'</span>])
</code></span></span>

在该文件中我们对,输入搜索关键词,点击搜索,搜索联想,进行了封装。

并配置了注释。

在平时中我们应该养成写注释的习惯,因为过一段时间后,没有注释,代码读起来很费劲。

好了我们的页面对象此时业已完成了。下面我们开始编写测试用例。在开始测试用了之前我们先熟悉一下pytest测试框架。


简单了解Pytest

打开pytest框架的官网。pytest: helps you write better programs — pytest documentation

<span style="color:#596172"><span style="background-color:#ffffff"><code class="language-python"><span style="color:#5c6370"><em># content of test_sample.py</em></span>
<span style="color:#7171bf">def</span> <span style="color:#61aeee">inc</span>(x):
    <span style="color:#7171bf">return</span> x + <span style="color:#d19a66">1</span>
 
 
<span style="color:#7171bf">def</span> <span style="color:#61aeee">test_answer</span>():
    <span style="color:#7171bf">assert</span> inc(<span style="color:#d19a66">3</span>) == <span style="color:#d19a66">5</span>
</code></span></span>

pytest.ini

pytest项目中的配置文件,可以对pytest执行过程中操作做全局控制。

在项目根目录新建pytest.ini文件。

<span style="color:#596172"><span style="background-color:#ffffff"><code class="language-ini"><span style="color:#e06c75">[pytest]</span>
<span style="color:#d19a66">addopts</span> = --html=report.html --self-contained-html
</code></span></span>

addopts 指定执行时的其他参数说明:

  • --html=report/report.html --self-contained-html 生成pytest-html带样式的报告
  • -s 输出我们用例中的调式信息
    • -q 安静的进行测试
    • -v 可以输出用例更加详细的执行信息,比如用例所在的文件及用例名称等

编写测试用例

我们将使用pytest编写测试用例。

TestCase目录中创建test_search.py文件。

<span style="color:#596172"><span style="background-color:#ffffff"><code class="language-python"><span style="color:#5c6370"><em>#!/usr/bin/env python3</em></span>
<span style="color:#5c6370"><em># -*- coding:utf-8 -*-</em></span>
<span style="color:#7171bf">import</span> re
<span style="color:#7171bf">import</span> pytest
<span style="color:#7171bf">from</span> utils.logger <span style="color:#7171bf">import</span> log
<span style="color:#7171bf">from</span> common.readconfig <span style="color:#7171bf">import</span> ini
<span style="color:#7171bf">from</span> page_object.searchpage <span style="color:#7171bf">import</span> SearchPage
 
 
<span style="color:#7171bf">class</span> <span style="color:#61aeee">TestSearch</span>:
<span style="color:#61aeee">    @pytest.fixture(scope=<span style="color:#3388aa">'function'</span>, autouse=<span style="color:#56b6c2">True</span>)</span>
    <span style="color:#7171bf">def</span> <span style="color:#61aeee">open_baidu</span>(self, drivers):
        <span style="color:#98c379">"""打开百度"""</span>
        search = SearchPage(drivers)
        search.get_url(ini.url)
 
    <span style="color:#7171bf">def</span> <span style="color:#61aeee">test_001</span>(self, drivers):
        <span style="color:#98c379">"""搜索"""</span>
        search = SearchPage(drivers)
        search.input_search(<span style="color:#98c379">"selenium"</span>)
        search.click_search()
        result = re.search(<span style="color:#98c379">r'selenium'</span>, search.get_source)
        log.info(result)
        <span style="color:#7171bf">assert</span> result
 
    <span style="color:#7171bf">def</span> <span style="color:#61aeee">test_002</span>(self, drivers):
        <span style="color:#98c379">"""测试搜索候选"""</span>
        search = SearchPage(drivers)
        search.input_search(<span style="color:#98c379">"selenium"</span>)
        log.info(<span style="color:#7171bf">list</span>(search.imagine))
        <span style="color:#7171bf">assert</span> <span style="color:#7171bf">all</span>([<span style="color:#98c379">"selenium"</span> <span style="color:#7171bf">in</span> i <span style="color:#7171bf">for</span> i <span style="color:#7171bf">in</span> search.imagine])
 
 
<span style="color:#7171bf">if</span> __name__ == <span style="color:#98c379">'__main__'</span>:
    pytest.main([<span style="color:#98c379">'TestCase/test_search.py'</span>])
 
</code></span></span>

我们测试用了就编写好了。

  • pytest.fixture 这个实现了和unittest的setup,teardown一样的前置启动,后置清理的装饰器。

  • 第一个测试用例:

    • 我们实现了在百度selenium关键字,并点击搜索按钮,并在搜索结果中,用正则查找结果页源代码,返回数量大于10我们就认为通过。
  • 第二个测试用例:

    • 我们实现了,搜索selenium,然后断言搜索候选中的所有结果有没有selenium关键字。

最后我们的在下面写一个执行启动的语句。

这时候我们应该进入执行了,但是还有一个问题,我们还没有把driver传递。

conftest.py

我们在项目根目录下新建一个conftest.py文件。

<span style="color:#596172"><span style="background-color:#ffffff"><code class="language-python"><span style="color:#5c6370"><em>#!/usr/bin/env python3</em></span>
<span style="color:#5c6370"><em># -*- coding:utf-8 -*-</em></span>
<span style="color:#7171bf">import</span> pytest
<span style="color:#7171bf">from</span> py.xml <span style="color:#7171bf">import</span> html
<span style="color:#7171bf">from</span> selenium <span style="color:#7171bf">import</span> webdriver
 
 
driver = <span style="color:#56b6c2">None</span>
 
 
<span style="color:#61aeee">@pytest.fixture(scope=<span style="color:#3388aa">'session'</span>, autouse=<span style="color:#56b6c2">True</span>)</span>
<span style="color:#7171bf">def</span> <span style="color:#61aeee">drivers</span>(request):
    <span style="color:#7171bf">global</span> driver
    <span style="color:#7171bf">if</span> driver <span style="color:#7171bf">is</span> <span style="color:#56b6c2">None</span>:
        driver = webdriver.Chrome()
        driver.maximize_window()
 
    <span style="color:#7171bf">def</span> <span style="color:#61aeee">fn</span>():
        driver.quit()
 
    request.addfinalizer(fn)
    <span style="color:#7171bf">return</span> driver
 
 
<span style="color:#61aeee">@pytest.hookimpl(hookwrapper=<span style="color:#56b6c2">True</span>)</span>
<span style="color:#7171bf">def</span> <span style="color:#61aeee">pytest_runtest_makereport</span>(item):
    <span style="color:#98c379">"""
    当测试失败的时候,自动截图,展示到html报告中
    :param item:
    """</span>
    pytest_html = item.config.pluginmanager.getplugin(<span style="color:#98c379">'html'</span>)
    outcome = <span style="color:#7171bf">yield</span>
    report = outcome.get_result()
    report.description = <span style="color:#7171bf">str</span>(item.function.__doc__)
    extra = <span style="color:#7171bf">getattr</span>(report, <span style="color:#98c379">'extra'</span>, [])
 
    <span style="color:#7171bf">if</span> report.when == <span style="color:#98c379">'call'</span> <span style="color:#7171bf">or</span> report.when == <span style="color:#98c379">"setup"</span>:
        xfail = <span style="color:#7171bf">hasattr</span>(report, <span style="color:#98c379">'wasxfail'</span>)
        <span style="color:#7171bf">if</span> (report.skipped <span style="color:#7171bf">and</span> xfail) <span style="color:#7171bf">or</span> (report.failed <span style="color:#7171bf">and</span> <span style="color:#7171bf">not</span> xfail):
            file_name = report.nodeid.replace(<span style="color:#98c379">"::"</span>, <span style="color:#98c379">"_"</span>) + <span style="color:#98c379">".png"</span>
            screen_img = _capture_screenshot()
            <span style="color:#7171bf">if</span> file_name:
                html = <span style="color:#98c379">'<div><img src="data:image/png;base64,%s" alt="screenshot" style="width:1024px;height:768px;" '</span> \
                       <span style="color:#98c379">'onclick="window.open(this.src)" align="right"/></div>'</span> % screen_img
                extra.append(pytest_html.extras.html(html))
        report.extra = extra
 
 
<span style="color:#7171bf">def</span> <span style="color:#61aeee">pytest_html_results_table_header</span>(cells):
    cells.insert(<span style="color:#d19a66">1</span>, html.th(<span style="color:#98c379">'用例名称'</span>))
    cells.insert(<span style="color:#d19a66">2</span>, html.th(<span style="color:#98c379">'Test_nodeid'</span>))
    cells.pop(<span style="color:#d19a66">2</span>)
 
 
<span style="color:#7171bf">def</span> <span style="color:#61aeee">pytest_html_results_table_row</span>(report, cells):
    cells.insert(<span style="color:#d19a66">1</span>, html.td(report.description))
    cells.insert(<span style="color:#d19a66">2</span>, html.td(report.nodeid))
    cells.pop(<span style="color:#d19a66">2</span>)
 
 
<span style="color:#7171bf">def</span> <span style="color:#61aeee">pytest_html_results_table_html</span>(report, data):
    <span style="color:#7171bf">if</span> report.passed:
        <span style="color:#7171bf">del</span> data[:]
        data.append(html.div(<span style="color:#98c379">'通过的用例未捕获日志输出.'</span>, class_=<span style="color:#98c379">'empty log'</span>))
 
 
<span style="color:#7171bf">def</span> <span style="color:#61aeee">_capture_screenshot</span>():
    <span style="color:#98c379">'''
    截图保存为base64
    :return:
    '''</span>
    <span style="color:#7171bf">return</span> driver.get_screenshot_as_base64()
 
</code></span></span>

conftest.py测试框架pytest的胶水文件,里面用到了fixture的方法,封装并传递出了driver。


执行用例

以上我们已经编写完成了整个框架和测试用例。

我们进入到当前项目的主目录执行命令:

<span style="color:#596172"><span style="background-color:#ffffff"><code class="language-powershell">pytest
</code></span></span>

命令行输出:

<span style="color:#596172"><span style="background-color:#ffffff"><code class="language-powershell">Test session starts (platform: win32, Python <span style="color:#d19a66">3.7</span>.<span style="color:#d19a66">7</span>, pytest <span style="color:#d19a66">5.3</span>.<span style="color:#d19a66">2</span>, py<span style="color:#7171bf">test-sugar</span> <span style="color:#d19a66">0.9</span>.<span style="color:#d19a66">2</span>)
cachedir: .pytest_cache
metadata: {<span style="color:#98c379">'Python'</span>: <span style="color:#98c379">'3.7.7'</span>, <span style="color:#98c379">'Platform'</span>: <span style="color:#98c379">'Windows-10-10.0.18362-SP0'</span>, <span style="color:#98c379">'Packages'</span>: {<span style="color:#98c379">'pytest'</span>: <span style="color:#98c379">'5.3.2'</span>, <span style="color:#98c379">'py'</span>: <span style="color:#98c379">'1.8.0'</span>, <span style="color:#98c379">'pluggy'</span>: <span style="color:#98c379">'0.13.1'</span>}, <span style="color:#98c379">'Plugins'</span>: {<span style="color:#98c379">'forked'</span>: <span style="color:#98c379">'1.1.3'</span>, <span style="color:#98c379">'html'</span>: <span style="color:#98c379">'2.0.1'</span>, <span style="color:#98c379">'metadata'</span>: <span style="color:#98c379">'1.8.0'</span>, <span style="color:#98c379">'ordering'</span>: <span style="color:#98c379">'0.6'</span>, <span style="color:#98c379">'rerunfailures'</span>: <span style="color:#98c379">'8.0'</span>, <span style="color:#98c379">'sugar'</span>: <span style="color:#98c379">'0.9.2'</span>, <span style="color:#98c379">'xdist'</span>: <span style="color:#98c379">'1.31.0'</span>}, <span style="color:#98c379">'JAVA_HOME'</span>: <span style="color:#98c379">'D:\\Program Files\\Java\\jdk1.8.0_131'</span>}
rootdir: C:\Users\hoou\PycharmProjects\web<span style="color:#56b6c2">-demotest</span>, inifile: pytest.ini
plugins: forked<span style="color:#56b6c2">-1</span>.<span style="color:#d19a66">1.3</span>, html<span style="color:#56b6c2">-2</span>.<span style="color:#d19a66">0.1</span>, metadata<span style="color:#56b6c2">-1</span>.<span style="color:#d19a66">8.0</span>, ordering<span style="color:#56b6c2">-0</span>.<span style="color:#d19a66">6</span>, rerunfailures<span style="color:#56b6c2">-8</span>.<span style="color:#d19a66">0</span>, sugar<span style="color:#56b6c2">-0</span>.<span style="color:#d19a66">9.2</span>, xdist<span style="color:#56b6c2">-1</span>.<span style="color:#d19a66">31.0</span>
collecting ... 
DevTools listening on ws://<span style="color:#d19a66">127.0</span>.<span style="color:#d19a66">0.1</span>:<span style="color:#d19a66">10351</span>/devtools/browser/<span style="color:#d19a66">78</span>bef34d<span style="color:#56b6c2">-b94c-4087-b724-34fb6b2ef6d1</span>
 
 TestCase\test_search.py::TestSearch.test_001 ✓                                                                                              <span style="color:#d19a66">50</span>% █████     
 
 TestCase\test_search.py::TestSearch.test_002 ✓                                                                                             <span style="color:#d19a66">100</span>% ██████████
<span style="color:#56b6c2">-------------------------------</span> generated html file: file://C:\Users\hoou\PycharmProjects\web<span style="color:#56b6c2">-demotest</span>\report\report.html <span style="color:#56b6c2">--------------------------------</span> 
 
Results (<span style="color:#d19a66">12.90</span>s):
       <span style="color:#d19a66">2</span> passed
</code></span></span>

可以看到两条用例已经执行成功了。

项目的report目录中生成了一个report.html文件。

这就是生成的测试报告文件。


发送邮件

当项目执行完成之后,需要发送到自己或者其他人邮箱里查看结果。

我们编写发送邮件的模块。

utils目录中新建send_mail.py文件

<span style="color:#596172"><span style="background-color:#ffffff"><code class="language-python"><span style="color:#5c6370"><em>#!/usr/bin/env python3</em></span>
<span style="color:#5c6370"><em># -*- coding:utf-8 -*-</em></span>
<span style="color:#7171bf">import</span> zmail
<span style="color:#7171bf">from</span> config.conf <span style="color:#7171bf">import</span> cm
 
 
<span style="color:#7171bf">def</span> <span style="color:#61aeee">send_report</span>():
    <span style="color:#98c379">"""发送报告"""</span>
    <span style="color:#7171bf">with</span> <span style="color:#7171bf">open</span>(cm.REPORT_FILE, encoding=<span style="color:#98c379">'utf-8'</span>) <span style="color:#7171bf">as</span> f:
        content_html = f.read()
    <span style="color:#7171bf">try</span>:
        mail = {
            <span style="color:#98c379">'from'</span>: <span style="color:#98c379">'1084502012@qq.com'</span>,
            <span style="color:#98c379">'subject'</span>: <span style="color:#98c379">'最新的测试报告邮件'</span>,
            <span style="color:#98c379">'content_html'</span>: content_html,
            <span style="color:#98c379">'attachments'</span>: [cm.REPORT_FILE, ]
        }
        server = zmail.server(*cm.EMAIL_INFO.values())
        server.send_mail(cm.ADDRESSEE, mail)
        <span style="color:#7171bf">print</span>(<span style="color:#98c379">"测试邮件发送成功!"</span>)
    <span style="color:#7171bf">except</span> Exception <span style="color:#7171bf">as</span> e:
        <span style="color:#7171bf">print</span>(<span style="color:#98c379">"Error: 无法发送邮件,{}!"</span>, <span style="color:#7171bf">format</span>(e))
 
 
<span style="color:#7171bf">if</span> __name__ == <span style="color:#98c379">"__main__"</span>:
    <span style="color:#98c379">'''请先在config/conf.py文件设置QQ邮箱的账号和密码'''</span>
    send_report()
</code></span></span>

执行该文件:

<span style="color:#596172"><span style="background-color:#ffffff"><code class="language-shell">测试邮件发送成功!
</code></span></span>

可以看到测试报告邮件已经发送成功了。打开邮箱。

 

成功收到了邮件。

这个demo项目就算是整体完工了;是不是很有心得,在发送邮件的那一刻很有成就感。

最后,想必你已经对pytest+selenium框架有了一个整体的认知了,在自动化测试的道路上又上了一层台阶。


pytest使用allure测试报告

  • 选用的项目为Selenium自动化测试Pytest框架实战,在这个项目的基础上说allure报告。

allure安装

  • 首先安装python的allure-pytest包
<span style="color:#596172"><span style="background-color:#ffffff"><code class="language-shell">pip install allure-pytest
</code></span></span>
  • 然后安装allure的command命令行程序

MacOS直接使用homebrew工具执行 brew install allure 即可安装,不用配置下载包和配置环境

在GitHub下载安装程序https://github.com/allure-framework/allure2/releases

但是由于GitHub访问太慢,我已经下载好并放在了群文件里面

下载完成后解压放到一个文件夹。我的路径是D:\Program Files\allure-2.13.3

然后配置环境变量: 在系统变量path中添加D:\Program Files\allure-2.13.3\bin,然后确定保存。

打开cmd,输入allure,如果结果显示如下则表示成功了:

<span style="color:#596172"><span style="background-color:#ffffff"><code class="language-powershell">C:\Users\hoou>allure
Usage: allure [<span style="color:#d19a66">options</span>] [<span style="color:#d19a66">command</span>] [<span style="color:#d19a66">command</span> <span style="color:#d19a66">options</span>]
  Options:
    <span style="color:#56b6c2">--help</span>
      Print commandline help.
    <span style="color:#56b6c2">-q</span>, <span style="color:#56b6c2">--quiet</span>
      <span style="color:#7171bf">Switch</span> on the quiet mode.
      Default: false
    <span style="color:#56b6c2">-v</span>, <span style="color:#56b6c2">--verbose</span>
      <span style="color:#7171bf">Switch</span> on the verbose mode.
      Default: false
    <span style="color:#56b6c2">--version</span>
      Print commandline version.
      Default: false
  Commands:
    generate      Generate the report
      Usage: generate [<span style="color:#d19a66">options</span>] The directories with allure results
        Options:
          <span style="color:#56b6c2">-c</span>, <span style="color:#56b6c2">--clean</span>
            Clean Allure report directory before generating a new one.
            Default: false
          <span style="color:#56b6c2">--config</span>
            Allure commandline config path. <span style="color:#7171bf">If</span> specified overrides values from
            <span style="color:#56b6c2">--profile</span> and <span style="color:#56b6c2">--configDirectory</span>.
          <span style="color:#56b6c2">--configDirectory</span>
            Allure commandline configurations directory. By default uses
            ALLURE_HOME directory.
          <span style="color:#56b6c2">--profile</span>
            Allure commandline configuration profile.
          <span style="color:#56b6c2">-o</span>, <span style="color:#56b6c2">--report-dir</span>, <span style="color:#56b6c2">--output</span>
            The directory to generate Allure report into.
            Default: allure<span style="color:#56b6c2">-report</span>
 
    serve      Serve the report
      Usage: serve [<span style="color:#d19a66">options</span>] The directories with allure results
        Options:
          <span style="color:#56b6c2">--config</span>
            Allure commandline config path. <span style="color:#7171bf">If</span> specified overrides values from
            <span style="color:#56b6c2">--profile</span> and <span style="color:#56b6c2">--configDirectory</span>.
          <span style="color:#56b6c2">--configDirectory</span>
            Allure commandline configurations directory. By default uses
            ALLURE_HOME directory.
          <span style="color:#56b6c2">-h</span>, <span style="color:#56b6c2">--host</span>
            This host will be used to <span style="color:#7171bf">start</span> web server <span style="color:#7171bf">for</span> the report.
          <span style="color:#56b6c2">-p</span>, <span style="color:#56b6c2">--port</span>
            This port will be used to <span style="color:#7171bf">start</span> web server <span style="color:#7171bf">for</span> the report.
            Default: <span style="color:#d19a66">0</span>
          <span style="color:#56b6c2">--profile</span>
            Allure commandline configuration profile.
 
    open      Open generated report
      Usage: open [<span style="color:#d19a66">options</span>] The report directory
        Options:
          <span style="color:#56b6c2">-h</span>, <span style="color:#56b6c2">--host</span>
            This host will be used to <span style="color:#7171bf">start</span> web server <span style="color:#7171bf">for</span> the report.
          <span style="color:#56b6c2">-p</span>, <span style="color:#56b6c2">--port</span>
            This port will be used to <span style="color:#7171bf">start</span> web server <span style="color:#7171bf">for</span> the report.
            Default: <span style="color:#d19a66">0</span>
 
    plugin      Generate the report
      Usage: plugin [<span style="color:#d19a66">options</span>]
        Options:
          <span style="color:#56b6c2">--config</span>
            Allure commandline config path. <span style="color:#7171bf">If</span> specified overrides values from
            <span style="color:#56b6c2">--profile</span> and <span style="color:#56b6c2">--configDirectory</span>.
          <span style="color:#56b6c2">--configDirectory</span>
            Allure commandline configurations directory. By default uses
            ALLURE_HOME directory.
          <span style="color:#56b6c2">--profile</span>
            Allure commandline configuration profile.
</code></span></span>

allure初体验

改造一下之前的测试用例代码

<span style="color:#596172"><span style="background-color:#ffffff"><code class="language-python"><span style="color:#5c6370"><em>#!/usr/bin/env python3</em></span>
<span style="color:#5c6370"><em># -*- coding:utf-8 -*-</em></span>
<span style="color:#7171bf">import</span> re
<span style="color:#7171bf">import</span> pytest
<span style="color:#7171bf">import</span> allure
<span style="color:#7171bf">from</span> utils.logger <span style="color:#7171bf">import</span> log
<span style="color:#7171bf">from</span> common.readconfig <span style="color:#7171bf">import</span> ini
<span style="color:#7171bf">from</span> page_object.searchpage <span style="color:#7171bf">import</span> SearchPage
 
 
<span style="color:#61aeee">@allure.feature(<span style="color:#3388aa">"测试百度模块"</span>)</span>
<span style="color:#7171bf">class</span> <span style="color:#61aeee">TestSearch</span>:
<span style="color:#61aeee">    @pytest.fixture(scope=<span style="color:#3388aa">'function'</span>, autouse=<span style="color:#56b6c2">True</span>)</span>
    <span style="color:#7171bf">def</span> <span style="color:#61aeee">open_baidu</span>(self, drivers):
        <span style="color:#98c379">"""打开百度"""</span>
        search = SearchPage(drivers)
        search.get_url(ini.url)
 
<span style="color:#61aeee">    @allure.story(<span style="color:#3388aa">"搜索selenium结果用例"</span>)</span>
    <span style="color:#7171bf">def</span> <span style="color:#61aeee">test_001</span>(self, drivers):
        <span style="color:#98c379">"""搜索"""</span>
        search = SearchPage(drivers)
        search.input_search(<span style="color:#98c379">"selenium"</span>)
        search.click_search()
        result = re.search(<span style="color:#98c379">r'selenium'</span>, search.get_source)
        log.info(result)
        <span style="color:#7171bf">assert</span> result
 
<span style="color:#61aeee">    @allure.story(<span style="color:#3388aa">"测试搜索候选用例"</span>)</span>
    <span style="color:#7171bf">def</span> <span style="color:#61aeee">test_002</span>(self, drivers):
        <span style="color:#98c379">"""测试搜索候选"""</span>
        search = SearchPage(drivers)
        search.input_search(<span style="color:#98c379">"selenium"</span>)
        log.info(<span style="color:#7171bf">list</span>(search.imagine))
        <span style="color:#7171bf">assert</span> <span style="color:#7171bf">all</span>([<span style="color:#98c379">"selenium"</span> <span style="color:#7171bf">in</span> i <span style="color:#7171bf">for</span> i <span style="color:#7171bf">in</span> search.imagine])
        
<span style="color:#7171bf">if</span> __name__ == <span style="color:#98c379">'__main__'</span>:
    pytest.main([<span style="color:#98c379">'TestCase/test_search.py'</span>, <span style="color:#98c379">'--alluredir'</span>, <span style="color:#98c379">'./allure'</span>])
    os.system(<span style="color:#98c379">'allure serve allure'</span>)
</code></span></span>

然后运行一下:

注意:如果你使用的是pycharm编辑器,请跳过该运行方式,直接使用.bat.sh的方式运行

<span style="color:#596172"><span style="background-color:#ffffff"><code class="language-shell">***
 
 
------------------------------- generated html file: file://C:\Users\hoou\PycharmProjects\web-demotest\report.html -------------------------------- 
 
Results (12.97s):
       2 passed
Generating report to temp directory...
Report successfully generated to C:\Users\hoou\AppData\Local\Temp\112346119265936111\allure-report
Starting web server...
2020-06-18 22:52:44.500:INFO::main: Logging initialized @1958ms to org.eclipse.jetty.util.log.StdErrLog
Server started at <http://172.18.47.241:6202/>. Press <Ctrl+C> to exit
</code></span></span>

命令行会出现如上提示,接着浏览器会自动打开:

 点击左下角En即可选择切换为中文。

 

是不是很清爽很友好,比pytest-html更舒服。

allure装饰器介绍

 

报告的生成和展示

刚才的两个命令:

  • 生成allure原始报告到report/allure目录下,生成的全部为json或txt文件。
<span style="color:#596172"><span style="background-color:#ffffff"><code class="language-powershell">pytest TestCase/test_search.py <span style="color:#56b6c2">--alluredir</span> ./allure
</code></span></span>
  • 在一个临时文件中生成报告并启动浏览器打开
<span style="color:#596172"><span style="background-color:#ffffff"><code class="language-powershell">allure serve allure
</code></span></span>

但是在关闭浏览器之后这个报告就再也打不开了。不建议使用这种。

所以我们必须使用其他的命令,让allure可以指定生成的报告目录。

我们在项目的根目录中创建run_case.py文件,内容如下:

<span style="color:#596172"><span style="background-color:#ffffff"><code class="language-python"><span style="color:#5c6370"><em>#!/usr/bin/env python3</em></span>
<span style="color:#5c6370"><em># -*- coding:utf-8 -*-</em></span>
<span style="color:#7171bf">import</span> sys
<span style="color:#7171bf">import</span> subprocess
 
WIN = sys.platform.startswith(<span style="color:#98c379">'win'</span>)
 
 
<span style="color:#7171bf">def</span> <span style="color:#61aeee">main</span>():
   <span style="color:#98c379">"""主函数"""</span>
   steps = [
       <span style="color:#98c379">"venv\\Script\\activate"</span> <span style="color:#7171bf">if</span> WIN <span style="color:#7171bf">else</span> <span style="color:#98c379">"source venv/bin/activate"</span>,
       <span style="color:#98c379">"pytest --alluredir allure-results --clean-alluredir"</span>,
       <span style="color:#98c379">"allure generate allure-results -c -o allure-report"</span>,
       <span style="color:#98c379">"allure open allure-report"</span>
   ]
   <span style="color:#7171bf">for</span> step <span style="color:#7171bf">in</span> steps:
       subprocess.run(<span style="color:#98c379">"call "</span> + step <span style="color:#7171bf">if</span> WIN <span style="color:#7171bf">else</span> step, shell=<span style="color:#56b6c2">True</span>)
 
 
<span style="color:#7171bf">if</span> __name__ == <span style="color:#98c379">"__main__"</span>:
   main()
</code></span></span>

命令释义:

1、使用pytest生成原始报告,里面大多数是一些原始的json数据,加入--clean-alluredir参数清除allure-results历史数据。

<span style="color:#596172"><span style="background-color:#ffffff"><code class="language-powershell">pytest <span style="color:#56b6c2">--alluredir</span> allure<span style="color:#56b6c2">-results</span> <span style="color:#56b6c2">--clean-alluredir</span>
</code></span></span>
  • --clean-alluredir 清除allure-results历史数据

2、使用generate命令导出HTML报告到新的目录

<span style="color:#596172"><span style="background-color:#ffffff"><code class="language-powershell">allure generate allure<span style="color:#56b6c2">-results</span> <span style="color:#56b6c2">-o</span> allure<span style="color:#56b6c2">-report</span>
</code></span></span>

3、使用open命令在浏览器中打开HTML报告

<span style="color:#596172"><span style="background-color:#ffffff"><code class="language-powershell">allure open allure<span style="color:#56b6c2">-report</span>
</code></span></span>

好了我们运行一下该文件。

<span style="color:#596172"><span style="background-color:#ffffff"><code class="language-powershell">Results (<span style="color:#d19a66">12.85</span>s):
       <span style="color:#d19a66">2</span> passed
Report successfully generated to c:\Users\hoou\PycharmProjects\web<span style="color:#56b6c2">-demotest</span>\allure<span style="color:#56b6c2">-report</span>
Starting web server...
<span style="color:#d19a66">2020</span><span style="color:#56b6c2">-06-18</span> <span style="color:#d19a66">23</span>:<span style="color:#d19a66">30</span>:<span style="color:#d19a66">24.122</span>:INFO::main: Logging initialized @<span style="color:#d19a66">260</span>ms to org.eclipse.jetty.util.log.StdErrLog
Server started at <http://<span style="color:#d19a66">172.18</span>.<span style="color:#d19a66">47.241</span>:<span style="color:#d19a66">7932</span>/>. Press <Ctrl+C> to <span style="color:#7171bf">exit</span>
</code></span></span>

 

可以看到运行成功了。

在项目中的allure-report文件夹也生成了相应的报告。

 

allure发生错误截图

上面的用例全是运行成功的,没有错误和失败的,那么发生了错误怎么样在allure报告中生成错误截图呢,我们一起来看看。

首先我们先在config/conf.py文件中添加一个截图目录和截图文件的配置。

<span style="color:#596172"><span style="background-color:#ffffff"><code class="language-python">+++
 
<span style="color:#7171bf">class</span> <span style="color:#61aeee">ConfigManager</span>(<span style="color:#61aeee">object</span>):
 
		...
    
<span style="color:#61aeee">    @property</span>
    <span style="color:#7171bf">def</span> <span style="color:#61aeee">screen_path</span>(self):
        <span style="color:#98c379">"""截图目录"""</span>
        screenshot_dir = os.path.join(self.BASE_DIR, <span style="color:#98c379">'screen_capture'</span>)
        <span style="color:#7171bf">if</span> <span style="color:#7171bf">not</span> os.path.exists(screenshot_dir):
            os.makedirs(screenshot_dir)
        now_time = dt_strftime(<span style="color:#98c379">"%Y%m%d%H%M%S"</span>)
        screen_file = os.path.join(screenshot_dir, <span style="color:#98c379">"{}.png"</span>.<span style="color:#7171bf">format</span>(now_time))
        <span style="color:#7171bf">return</span> now_time, screen_file
	
  	...
+++
</code></span></span>

然后我们修改项目目录中的conftest.py

<span style="color:#596172"><span style="background-color:#ffffff"><code class="language-python"><span style="color:#5c6370"><em>#!/usr/bin/env python3</em></span>
<span style="color:#5c6370"><em># -*- coding:utf-8 -*-</em></span>
<span style="color:#7171bf">import</span> base64
<span style="color:#7171bf">import</span> pytest
<span style="color:#7171bf">import</span> allure
<span style="color:#7171bf">from</span> py.xml <span style="color:#7171bf">import</span> html
<span style="color:#7171bf">from</span> selenium <span style="color:#7171bf">import</span> webdriver
 
<span style="color:#7171bf">from</span> config.conf <span style="color:#7171bf">import</span> cm
<span style="color:#7171bf">from</span> common.readconfig <span style="color:#7171bf">import</span> ini
<span style="color:#7171bf">from</span> utils.times <span style="color:#7171bf">import</span> timestamp
<span style="color:#7171bf">from</span> utils.send_mail <span style="color:#7171bf">import</span> send_report
 
driver = <span style="color:#56b6c2">None</span>
 
 
<span style="color:#61aeee">@pytest.fixture(scope=<span style="color:#3388aa">'session'</span>, autouse=<span style="color:#56b6c2">True</span>)</span>
<span style="color:#7171bf">def</span> <span style="color:#61aeee">drivers</span>(request):
    <span style="color:#7171bf">global</span> driver
    <span style="color:#7171bf">if</span> driver <span style="color:#7171bf">is</span> <span style="color:#56b6c2">None</span>:
        driver = webdriver.Chrome()
        driver.maximize_window()
 
    <span style="color:#7171bf">def</span> <span style="color:#61aeee">fn</span>():
        driver.quit()
 
    request.addfinalizer(fn)
    <span style="color:#7171bf">return</span> driver
 
 
<span style="color:#61aeee">@pytest.hookimpl(hookwrapper=<span style="color:#56b6c2">True</span>)</span>
<span style="color:#7171bf">def</span> <span style="color:#61aeee">pytest_runtest_makereport</span>(item):
    <span style="color:#98c379">"""
    当测试失败的时候,自动截图,展示到html报告中
    :param item:
    """</span>
    pytest_html = item.config.pluginmanager.getplugin(<span style="color:#98c379">'html'</span>)
    outcome = <span style="color:#7171bf">yield</span>
    report = outcome.get_result()
    report.description = <span style="color:#7171bf">str</span>(item.function.__doc__)
    extra = <span style="color:#7171bf">getattr</span>(report, <span style="color:#98c379">'extra'</span>, [])
 
    <span style="color:#7171bf">if</span> report.when == <span style="color:#98c379">'call'</span> <span style="color:#7171bf">or</span> report.when == <span style="color:#98c379">"setup"</span>:
        xfail = <span style="color:#7171bf">hasattr</span>(report, <span style="color:#98c379">'wasxfail'</span>)
        <span style="color:#7171bf">if</span> (report.skipped <span style="color:#7171bf">and</span> xfail) <span style="color:#7171bf">or</span> (report.failed <span style="color:#7171bf">and</span> <span style="color:#7171bf">not</span> xfail):
            screen_img = _capture_screenshot()
            <span style="color:#7171bf">if</span> screen_img:
                html = <span style="color:#98c379">'<div><img src="data:image/png;base64,%s" alt="screenshot" style="width:1024px;height:768px;" '</span> \
                       <span style="color:#98c379">'onclick="window.open(this.src)" align="right"/></div>'</span> % screen_img
                extra.append(pytest_html.extras.html(html))
        report.extra = extra
 
 
<span style="color:#7171bf">def</span> <span style="color:#61aeee">pytest_html_results_table_header</span>(cells):
    cells.insert(<span style="color:#d19a66">1</span>, html.th(<span style="color:#98c379">'用例名称'</span>))
    cells.insert(<span style="color:#d19a66">2</span>, html.th(<span style="color:#98c379">'Test_nodeid'</span>))
    cells.pop(<span style="color:#d19a66">2</span>)
 
 
<span style="color:#7171bf">def</span> <span style="color:#61aeee">pytest_html_results_table_row</span>(report, cells):
    cells.insert(<span style="color:#d19a66">1</span>, html.td(report.description))
    cells.insert(<span style="color:#d19a66">2</span>, html.td(report.nodeid))
    cells.pop(<span style="color:#d19a66">2</span>)
 
 
<span style="color:#7171bf">def</span> <span style="color:#61aeee">pytest_html_results_table_html</span>(report, data):
    <span style="color:#7171bf">if</span> report.passed:
        <span style="color:#7171bf">del</span> data[:]
        data.append(html.div(<span style="color:#98c379">'通过的用例未捕获日志输出.'</span>, class_=<span style="color:#98c379">'empty log'</span>))
 
 
<span style="color:#7171bf">def</span> <span style="color:#61aeee">pytest_html_report_title</span>(report):
    report.title = <span style="color:#98c379">"pytest示例项目测试报告"</span>
 
 
<span style="color:#7171bf">def</span> <span style="color:#61aeee">pytest_configure</span>(config):
    config._metadata.clear()
    config._metadata[<span style="color:#98c379">'测试项目'</span>] = <span style="color:#98c379">"测试百度官网搜索"</span>
    config._metadata[<span style="color:#98c379">'测试地址'</span>] = ini.url
 
 
<span style="color:#7171bf">def</span> <span style="color:#61aeee">pytest_html_results_summary</span>(prefix, summary, postfix):
    <span style="color:#5c6370"><em># prefix.clear() # 清空summary中的内容</em></span>
    prefix.extend([html.p(<span style="color:#98c379">"所属部门: XX公司测试部"</span>)])
    prefix.extend([html.p(<span style="color:#98c379">"测试执行人: 随风挥手"</span>)])
 
 
<span style="color:#7171bf">def</span> <span style="color:#61aeee">pytest_terminal_summary</span>(terminalreporter, exitstatus, config):
    <span style="color:#98c379">"""收集测试结果"""</span>
    result = {
        <span style="color:#98c379">"total"</span>: terminalreporter._numcollected,
        <span style="color:#98c379">'passed'</span>: <span style="color:#7171bf">len</span>(terminalreporter.stats.get(<span style="color:#98c379">'passed'</span>, [])),
        <span style="color:#98c379">'failed'</span>: <span style="color:#7171bf">len</span>(terminalreporter.stats.get(<span style="color:#98c379">'failed'</span>, [])),
        <span style="color:#98c379">'error'</span>: <span style="color:#7171bf">len</span>(terminalreporter.stats.get(<span style="color:#98c379">'error'</span>, [])),
        <span style="color:#98c379">'skipped'</span>: <span style="color:#7171bf">len</span>(terminalreporter.stats.get(<span style="color:#98c379">'skipped'</span>, [])),
        <span style="color:#5c6370"><em># terminalreporter._sessionstarttime 会话开始时间</em></span>
        <span style="color:#98c379">'total times'</span>: timestamp() - terminalreporter._sessionstarttime
    }
    <span style="color:#7171bf">print</span>(result)
    <span style="color:#7171bf">if</span> result[<span style="color:#98c379">'failed'</span>] <span style="color:#7171bf">or</span> result[<span style="color:#98c379">'error'</span>]:
        send_report()
 
 
<span style="color:#7171bf">def</span> <span style="color:#61aeee">_capture_screenshot</span>():
    <span style="color:#98c379">"""截图保存为base64"""</span>
    now_time, screen_file = cm.screen_path
    driver.save_screenshot(screen_file)
    allure.attach.file(screen_file,
                       <span style="color:#98c379">"失败截图{}"</span>.<span style="color:#7171bf">format</span>(now_time),
                       allure.attachment_type.PNG)
    <span style="color:#7171bf">with</span> <span style="color:#7171bf">open</span>(screen_file, <span style="color:#98c379">'rb'</span>) <span style="color:#7171bf">as</span> f:
        imagebase64 = base64.b64encode(f.read())
    <span style="color:#7171bf">return</span> imagebase64.decode()
</code></span></span>

来看看我们修改了什么:

  • 我们修改了_capture_screenshot函数

在里面我们使用了webdriver截图生成文件,并使用allure.attach.file方法将文件添加到了allure测试报告中。

并且我们还返回了图片的base64编码,这样可以让pytest-html的错误截图和allure都能生效。

运行一次得到两份报告,一份是简单的一份是好看内容丰富的。

现在我们在测试用例中构建一个预期的错误测试一个我们的这个代码。
 

修改test_002测试用例

<span style="color:#596172"><span style="background-color:#ffffff"><code class="language-python">        <span style="color:#7171bf">assert</span> <span style="color:#7171bf">not</span> <span style="color:#7171bf">all</span>([<span style="color:#98c379">"selenium"</span> <span style="color:#7171bf">in</span> i <span style="color:#7171bf">for</span> i <span style="color:#7171bf">in</span> search.imagine])
</code></span></span>

运行一下:

可以看到allure报告中已经有了这个错误的信息。

再来看看pytest-html中生成的报告:

 

可以看到两份生成的报告都附带了错误的截图,真是鱼和熊掌可以兼得呢。

好了,到这里可以说allure的报告就先到这里了,以后发现allure其他的精彩之处我再来分享。


开源地址

为了方便学习交流,本次的示例项目已经保存在网盘了,直接点击下方小卡片免费获取。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/43430.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

保护数字世界的壁垒

随着科技的不断发展和互联网的普及&#xff0c;我们的生活日益依赖于数字化的世界。然而&#xff0c;随之而来的是网络安全威胁的不断增加。网络攻击、数据泄露和身份盗窃等问题已经成为我们所面临的现实。因此&#xff0c;网络安全变得尤为重要&#xff0c;我们需要采取措施来…

什么是分布式操作系统?我们为什么需要分布式操作系统?

分布式操作系统是一种特殊的操作系统&#xff0c;本质上属于多机操作系统&#xff0c;是传统单机操作系统的发展和延伸。它是将一个计算机系统划分为多个独立的计算单元(或者也可称为节点)&#xff0c;这些节点被部署到每台计算机上&#xff0c;然后被网络连接起来&#xff0c;…

Win10环境下Android Studio中运行Flutter HelloWorld项目

一、引言 Android Studio是Android的官方IDE(Integrated Development Environment)。它专为Android而打造&#xff0c;可以加快开发速度&#xff0c;为Android设备构建最高品质的应用。 Flutter是Google推出并开源的移动应用开发框架&#xff0c;主打跨平台、高保真、高性能。开…

【STL】list用法试做_底层实现

目录 一&#xff0c;list 使用 1. list 文档介绍 2. 常见接口 1. list中的sort 2. list sort 与 vector sort效率对比 3. 关于迭代器失效 4. clear 二&#xff0c;list 实现 1.框架搭建 2. 迭代器类——核心框架 3. operator-> 实现 4. const——迭代…

【计算机网络 01】说在前面 信息服务 因特网 ISP RFC技术文档 边缘与核心 交换方式 定义与分类 网络性能指标 计算机网络体系结构 章节小结

第一章--概述 说在前面1.1 计算机网络 信息时代作用1.2 因特网概述1.3 三种交换方式1.4 计算机网络 定义与分类1.5 计算机网络的性能指标1.6 计算机网络体系结构1 常见的计算机网络体系结构2 计算机网络体系结构分层的必要性3 计算机网络体系结构分层思想举例4 计算机网络体系结…

RuntimeError: DataLoader worker (pid 2105929) is killed by signal: Killed.

PyTorch DataLoader num_workers Test - 加快速度 可以利用PyTorch DataLoader类的多进程功能来加快神经网络训练过程。 加快训练进程 为了加快训练过程&#xff0c;我们将利用DataLoader类的num_workers可选属性。 num_workers属性告诉DataLoader实例要使用多少个子进程进…

23.多项式与非多项式曲线拟合对比(matlab程序)

1.简述 拟合标准&#xff1a; (1)原始数据向量与拟合向量之间的距离最小&#xff0c;该距离的度量一般使用误差平方和表示&#xff0c;即均方误差&#xff1a;R||Q-Y||22 (2)当均方误差最小时&#xff0c;说明构造的拟合向量与原始向量最为接近&#xff0c;这种曲线拟合的方法…

git commit -m时候没有保存package.json等文件

项目场景&#xff1a; 提示&#xff1a;git add . 和 git commit -m "保存" 操作&#xff0c;没有保存package.json等文件。 解决方案&#xff1a; 1.确保 package.json 文件没有被列在 .gitignore 文件中。打开 .gitignore 文件&#xff0c;检查是否有类似于 packa…

论文工具——ChatGPT结合PlotNeuralNet快速出神经网络深度学习模型图

文章目录 引言正文PlotNeuralNet安装使用使用python进行编辑使用latex进行编辑 样例利用chatGPT使用chatGPT生成Latex代码利用chatGPT生成对应的python代码 总结引用 引言 介绍如何安装PlotNeuralNet工具&#xff0c;并结合chatGPT减少学习成本&#xff0c;快速出图。将按照软…

4.2 Bootstrap HTML编码规范

文章目录 Bootstrap HTML编码规范语法HTML5 doctype语言属性IE 兼容模式字符编码引入 CSS 和 JavaScript 文件HTML5 spec links 实用为王属性顺序布尔&#xff08;boolean&#xff09;型属性减少标签的数量JavaScript 生成的标签 Bootstrap HTML编码规范 语法 用两个空格来代替…

通过 EXPLAIN 分析 SQL 的执行计划

通过 EXPLAIN 分析 SQL 的执行计划 EXPLAIN SELECTleave_station_area_id,ROUND( ( SUM( station_dist ) / 1000 ) / ( SUM( station_travel_time ) / 60 ), 2 ) evnPeakAvgSpeedFROMV3_SHIFT_ANALYSISWHERESTAT_DATE DATE_SUB( CURRENT_DATE, INTERVAL 1 DAY )AND LEAVE_STA…

NetSuite财务报表General Ledger Report的缺陷及改造案例

本周有用户提到一个特殊的业务场景&#xff0c;比较有代表性&#xff0c;在此分享。 问题 “如果在一张JE中&#xff0c;某个科目既有借又有贷&#xff0c;金额相同。那么在General Ledger Report中此JE的借贷都显示为0。这与事实不符&#xff0c;所以是不对的。” JE 155&a…

vue3-element-plus,控制表格多选的数量

1. 需求描述 控制表格的多选&#xff0c;最多只能选择5条数据&#xff0c;并且其他项禁用 2. 需求描述 <!-- selection-change 当选择项发生变化时会触发该事件--><template><el-tableref"multipleTableRef"v-loading"loading":data"…

[Linux] CentOS7 中 pip3 install 可能出现的 ssl 问题

由于解决问题之后, 才写的博客, 所以没有图片记录. 尽量描述清楚一些 今天写代码的时候, 突然发现 文件里用了#define定义宏之后, coc.nvim的coc-clangd补全就用不了 :checkhealth了一下, 发现nvim忘记支持python3了 尝试pip3 install neovim的时候, 发现会警告然后安装失败.…

网络安全(黑客)自学路线笔记

一、什么是黑客&#xff1f; 黑客泛指IT技术主攻渗透窃取攻击技术的电脑高手&#xff0c;现阶段黑客所需要掌握的远远不止这些。 二、为什么要学习黑客技术&#xff1f; 其实&#xff0c;网络信息空间安全已经成为海陆空之外的第四大战场&#xff0c;除了国与国之间的博弈&am…

4.数据类型

JS数据类型整体分为两大类: ➢基本数据类型 ➢引用数据类型 4.1数据类型-数字类型(Number) 即我们数学中学习到的数字&#xff0c;可以是整数、小数、正数、负数。 let age 18 //整数 let price 88.99 //小数JavaScript中的正数、负数、小数等统一称为数字类型 注意…

【测试开发】Python+Django实现接口测试工具

PythonDjango接口自动化 引言&#xff1a; 最近被几个公司实习生整自闭了&#xff0c;没有基础&#xff0c;想学自动化又不知道怎么去学&#xff0c;没有方向没有头绪&#xff0c;说白了其实就是学习过程中没有成就感&#xff0c;所以学不下去。出于各种花里胡哨的原因&#xf…

关于 Qt中的QString内容存在\u0000使用QChart(0x00)消除 的解决方法

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/131860574 红胖子(红模仿)的博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软…

解密动态内存管理的奥秘(含内存4个函数)

目录 一.为什么存在动态内存管理 二.动态内存函数的介绍 1. malloc函数&#xff08;memory alloc 内存开辟&#xff09; 函数介绍&#xff1a; malloc函数使用举例代码&#xff1a; 2.free&#xff08;释放&#xff09; 函数介绍&#xff1a; 代码的示例&#xff1a…

【Linux】初识多线程深入理解进程地址空间

目录 1 多线程的引入 1.1 相关概念 1.2 Linux操作系统理解多线程 特殊的进程结构 创建子进程的过程 创建多线程 进程与线程之间的关系 1.3 对多线程结构的管理 Windows管理多线程 Linux管理多线程 1.4 理解多线程与多进程相比&#xff0c;调度的成本更低 2 深入理…