• 11
  • 11
分享
  • 我为 locust 加了个 “生成测试报告” 功能——软件测试圈
  • 恬恬圈 2020-12-02 10:53:11 字数 6367 阅读 2673 收藏 11

Locust 是一个轻量级的性能测试工具,和大名鼎鼎的 JMeter 相比,没有那么大而全的功能,但针对一些简单的压测场景,Locust 无疑是个好选择。本文并非深入教学帖,所以下面只通过一个最简单的场景为例,来大致介绍一下它。

1 使用说明

假设咱现在要测试一个站点 http://test.valval.cool,测试目标为其中两个接口的性能,分别为 GET /api1 和 POST /api2。

首先安装

$ pip install locust

接着编写 python 脚本如下,可以命名为 locustfile.py:

from locust import HttpUser, task, between
class WebsiteUser(HttpUser):
    host = "http://test.valval.cool"
    wait_time = between(0, 0)
    @task
    def api1(self):
        self.client.get("/api1")
    @task
    def api2(self):
        self.client.post("/api2", data={'key': 'value'})

启动脚本:

$ locust -f locustfile.py

打开浏览器,访问 http://localhost:8089,可以看到一个还算友好的 WebUI

1.png

在这里设定本次测试的 并发数(最多有几个模拟用户一起发请求)和 孵化率(每秒新增几个用户),点击下面的开始按钮就正式启动压测了。这里要提一点,对比 JMeter 这类使用多线程方式来实现并发的传统模式,Locust 则使用 gevent 通过协程的方式模拟并发用户,资源的消耗非常之少。

启动后,默认看到的是 Statistics 页面,展示内容为当前测试的一些性能指标

2.png

这些指标并不是十分丰富,主要就反映了接口的 响应时间 和 RPS 吞吐量 这两方面。但我觉得这正是 Locust 的魅力,简单 够用,我们平常大部分的压测其实就是这样简简单单的 “拧螺丝” 场景,并不是人人都需要 “造火箭”。

我们在页面上探索一下,点进 Charts 页面,哇!不错,这里还提供了 3 个很棒的图表曲线,可以观察整个测试过程中采样数据的动态变化。

3.png

好了就夸到这里,下面来说说 Locust 让我抓狂的地方。

2 测试报告之难

如果你安装的是 1.2 之前的版本,你会发现测试完成后,想要优雅地获取一份测试报告是很艰难的。Download Data 页面仅提供了几个可读性很差的 csv 文件下载,并且仅针对整个测试的统计结果,无法体现过程中采样数据的变化趋势,所以我们必须再将 Charts 里的 3 个图表挨个下载下来。

4.png

经过一番折腾,最终你得到了下面这样的 3 个 csv 和 3 张图片,不仅操作繁琐,且这些零散的东西可读性非常差。

5.png

更可怕的是,因为历史数据仅保存在浏览器的页面里, reload 以后之前的数据就会消失,所以如果在测试过程不小心关闭或刷新了一下网页,那么图表就会像下面这样被无情清空...

6.png

我太难了!只是想要一份适合人类阅读的报告,咋就这么复杂!

3 自己开发生成报告

算了,自己动手,丰衣足食,我们一起尝试为 loucst 添加一个友好的生成报告功能吧。

先下载源代码,这里以 1.1.1 版本代码为例

$ git clone https://github.com/locustio/locust
$ git chekcout 1.1.1

大致浏览一下项目结构,把可能涉及的几个重要文件和目录标注了出来,如下:

locust
|-- test   // 测试用例在这里
|-- user
|-- rpc
|-- util
|-- contrib
|-- static  // WebUI 所用到的静态文件目录
|   |-- img
|   |-- chart.js
|   |-- locust.js  // 页面上比较主要的 js 逻辑都写在这里
|   |-- style.css
|   `-- ...
|-- templates  // WebUI 模板文件目录
|   |-- index.html  // 目前只有这一个页面
|-- __init__.py
|-- __main__.py
|-- main.py  // locust 启动主入口
|-- stats.py  // 统计指标相关的逻辑 大部分在这里
|-- web.py  // 使用 Flask 创建了一个 web app
|-- argument_parser.py
|-- clients.py
|-- env.py
|-- event.py
|-- exception.py
|-- log.py
|-- runners.py
`-- shape.py

来理一下思路:期望生成的报告内容目前 WebUI 上都已有提供,也就是那 3 个 csv 和 3 张图表,所以我们需要分析一下 web.py,将这些要呈现的数据集中到一个 view 中,然后增加一个页面,把上述内容一并渲染展示即可。

看看那 3 个 csv 的数据是从哪来的,直接在页面上就可以查到对应的链接,url 分别为 /stats/requests/csv、/stats/failures/csv、/exceptions/csv 如下:

7.png

在 web.py 中找到了 /stats/requests/csv 对应的 view 是 request_stats_csv,如下:

@app.route("/stats/requests/csv")
@self.auth_required_if_enabled
def request_stats_csv():
    data = StringIO()
    writer = csv.writer(data)
    requests_csv(self.environment.runner.stats, writer)
    response = make_response(data.getvalue())
    file_name = "requests_{0}.csv".format(time())
    disposition = "attachment;filename={0}".format(file_name)
    response.headers["Content-type"] = "text/csv"
    response.headers["Content-disposition"] = disposition
    return response

可以看出,最重要的是 requests_csv(self.environment.runner.stats, writer) 这句,目标数据来源于 stats。紧接着跟进 requests_csv 函数,会发现响应时间和吞吐量等数据对应 stats 中的变量名分别为 median_response_time、avg_response_time、max_response_time、total_rps 等。另外两个 view 分别是 failures_stats_csv 和 exceptions_csv,分析方法类似就不赘述了。

结合上面对源码的探究,我们可以新建一个类似下面这样的 view,将上文中提到的 3 个 csv 中的内容集中在一起。

@app.route("/stats/report")
@self.auth_required_if_enabled
def stats_report():
    stats = self.environment.runner.stats
    requests_statistics = list(chain(sort_stats(stats.entries), [stats.total]))
    failures_statistics = sort_stats(stats.errors)
    exceptions_statistics = []
    for exc in environment.runner.exceptions.values():
        exc['nodes'] = ", ".join(exc["nodes"])
        exceptions_statistics.append(exc)
    return render_template('report.html', **locals())

UI 方面,在 templates 目录下新建一个名为 report.html 的模板文件,在其中渲染 requests_statistics、failures_statistics、exceptions_statistics 为表格形式即可。效果如下:

8.png

接下来,还有 3 张图表要展示到报告中。咱先研究下 Charts 页面里的那几个图怎么来的,查看 index.html 源码发现,图表使用 echarts 生成,浏览器每隔 2 秒会发一次 ajax 请求,获取实时的采样数据后 push 到图表数据中。整个过程如下,节选自 locust.js 文件:

var rpsChart = new LocustLineChart($(".charts-container"), "Total Requests per Second", ["RPS", "Failures/s"], "reqs/s", ['#00ca5a', '#ff6d6d']);
var responseTimeChart = new LocustLineChart($(".charts-container"), "Response Times (ms)", ["Median Response Time", "95% percentile"], "ms");
var usersChart = new LocustLineChart($(".charts-container"), "Number of Users", ["Users"], "users");
function updateStats() {
    $.get('./stats/requests', function (report) {
        window.report = report;
        renderTable(report);
        renderWorkerTable(report);
        if (report.state !== "stopped"){
            // get total stats row
            var total = report.stats[report.stats.length-1];
            // update charts
            rpsChart.addValue([total.current_rps, total.current_fail_per_sec]);
            responseTimeChart.addValue([report.current_response_time_percentile_50, report.current_response_time_percentile_95]);
            usersChart.addValue([report.user_count]);
        } else {
            appearStopped();
        }
        setTimeout(updateStats, 2000);
    });
}
updateStats();

这也就是刷新页面后图表就被清空的原因了!我们的测试报告可不能基于前端页面保存的数据啊,那太不可靠了。这要怎么做呢?暂时没有思路,我们继续研究一下源码,看看有没有什么启发。

看到 main.py 中有这么一段,十分激动

if options.csv_prefix:
    gevent.spawn(stats_writer, environment, options.csv_prefix, full_history=options.stats_history_enabled).link_exception(greenlet_exception_handler)

在启动 locust 时如果带上了 --csv 和 --csv-full-history 参数的话,就会触发这段,将启动一个协程来执行 stats_writer 函数。跟进 stats_writer 会发现,他的功能是定时将当前实时的采样数据写入本地的 csv 文件中这个函数和我们要做的事情很像。

那我们就仿照它再写一个类似的协程,定时将采样数据保存到服务端内存中即可。代码类似这样:

def stats_history(runner):
    """Save current stats info to history for charts of report."""
    while True:
        stats = runner.stats
        r = {
            'time': datetime.datetime.now().strftime("%H:%M:%S"),
            'current_rps': stats.total.current_rps or 0,
            'current_fail_per_sec': stats.total.current_fail_per_sec or 0,
            'response_time_percentile_95': stats.total.get_current_response_time_percentile(0.95) or 0,
            'response_time_percentile_50': stats.total.get_current_response_time_percentile(0.5) or 0,
            'user_count': runner.user_count or 0,
        }
        stats.history.append(r)
        gevent.sleep(HISTORY_STATS_INTERVAL_SEC)

最后,在 Download Data 页面加上个 生成报告 的链接,大功告成!来看看完整的效果。

9.png

10.png

4 结语

阅读完此篇,希望你能有如下收获:

如果你从未了解过 Locust,希望此文为你打开了一扇窗,可以在今后尝试使用它,欲了解深入的高级使用技巧可进一步查阅网络上大量的现成文章。

如果你曾使用过 Locust,也同样有为生成测试报告而发愁的经历,那么现在你的困扰得以解决了。

如果你是开发爱好者,希望本文带着你手拉手一起体验了,从发现问题到思考分析,再到通过编码去解决问题的全过程。


作者:taojy123

原文链接:https://www.jianshu.com/p/50f65e72af73


  • 【留下美好印记】
    赞赏支持
登录 后发表评论
+ 关注

热门文章

    最新讲堂

      • 推荐阅读
      • 换一换
          • 背景APP Push的定义为在手机终端锁屏状态下通知栏展示或在操作前台顶端弹出的消息通知,点击后可唤起对应的APP,并在APP内跳转到指定页面。push流程:push流程对应的测试方案针对整个push消息下发、客户端接收上报流程,测试点可以梳理为:能收到push消息、push消息能正常点击调起APP并跳转到指定页面。push测试工具的实现1、push消息发送工具:push消息发送工具的具体使用:push发送工具的设计与实现:支持多APP、环境、push消息类型的选择,可以给指定用户uid或者设备token发送自定义的push消息。2、push可达性分析工具push可达性分析工具的具体使用:pu...
            0 0 927
            分享
          • 在写测试用例的时候是否有这些困扰:测试用例写了很多条,感觉有冗余,要精简又无从下手;测试用例写了很多条,但是总感觉还少了点啥;如果你不能同意更多,就请继续看看我们是怎么解决这两个问题的。一、用例条数过多很久之前,我有专门制定过测试用例改进计划,两步走策略。第一步,保证全面性,能考虑到的测试点都进行罗列,尽量全的罗列,保证没有遗漏。第二步,在全面性有保障的前提下,适当进行用例的删减,保证用例的针对性。但是随着计划的推进,我们就一直在处在第一步的边缘,迟迟无法跨越到第二步。主要原因有两个:一个是和开发人员的持续信任感没有建立,特别是测试过程中如果发现一些提测说明中没有提到的修改点的问题时,这种不信...
            2 3 1614
            分享
          •   1、前言  相信很多小伙伴在测试功能时都会在UI的显示这块下功夫,但是有些时候正常的文字和数据都是很难捕捉到异常情况,这些异常一般只有等到特定条件满足时才会显示出来。比如我之前有做过一个项目,它的首页面是一个展示甲方公司资产的页面,上面有各种的数据,大部分都是整数类的,且支持小数形式。有一次我就突发奇想,如果给它们罗列到小数点后面5位会咋样。于是我特意构造了小数点后面7位的资产数据,结果不做不知道,一做吓一跳,整个页面的布局彻底紊乱了,当时前端开发还是基于谷歌浏览器,谷歌显示尚且如此,其它浏览器如IE就更不可想象了。我当即就将该Bug标为紧急让开发去改!其实我们在做UI测试时一定要对异常多...
            0 0 98
            分享
          • 要说测试人员职业生涯当中最在意也是最绕不开的一个终极话题就是如何和开发人员相处。相信很多测试人员在面试的时候也遇到过这个问题:你是如何 和开发人员相(si)处(bi)的呢?要说起测试人员和开发人员的博弈,就不得不提到一个著名的思维测试-囚徒困境。在这场著名的思维试验中,两个罪犯即两个同案犯被逮捕了,他们被分别关到两个牢房里接受审讯。他们都被告知:"如果你保持沉默,你会被判处一年徒刑;如果你出卖同伴,你会获得自由;但如果你的同伴出卖了你,你就会蹲两年大狱。"出于竞争性的私利"两个囚徒实际上都有动力去出卖对方。然而,就如同下图所显示的,如果两个囚徒互相出卖,则他们获得...
            1 0 2701
            分享
          •   1 Mantis工具应用技巧  BUG跟踪工具你都了解哪些?禅道、JIRA、Mantis、BugZilla?  在我的项目中,使用的是Mantis工具来管理缺陷。  用过Mantis系统的伙伴应该都知道,Mantis是一个开源缺陷跟踪系统,以Web可视化UI界面进行操作,进行项目管理及缺陷跟踪。  虽然Mantis系统有如下的功能特性:  1、可定制Email通知功能;  2、支持多项目、多语言;  3、权限设置灵活,不同角色有不同权限,每个项目可设为公开或私有状态,每个缺陷可设为公开或私有状态,每个缺陷可以在不同项目间移动;  4、具有方便的缺陷关联功能,除重复缺陷外,每个缺陷都可以链接...
            11 11 2011
            分享
      • 51testing软件测试圈微信