接口测试 ApiTestEngine (2) 探索优雅的测试用例描述方式

debugtalk · 发布于 2017年07月07日 · 最后由 keithmork 回复于 2017年10月12日 · 3864 次阅读
本帖已被设为精华帖!

《ApiTestEngine 演进之路(1)搭建基础框架》一文中,我们完成了ApiTestEngine基础框架的搭建,并实现了简单接口的测试功能。

接下来,我们就针对复杂类型的接口(例如包含签名校验等机制),通过对接口的业务参数和技术细节进行分离,实现简洁优雅的接口测试用例描述。

传统的测试用例编写方式

对于在自动化测试中将测试数据代码实现进行分离的好处,我之前已经讲过多次,这里不再重复。

测试数据与代码实现分离后,简单的接口还好,测试用例编写不会有什么问题;但是当面对复杂一点的接口(例如包含签名校验等机制)时,我们编写自动化测试用例还是会比较繁琐。

我们从一个最常见的案例入手,看下编写自动化测试用例的过程,相信大家看完后就会对上面那段话有很深的感受。

以API接口服务(Mock Server)的创建新用户功能为例,该接口描述如下:

请求数据:
Url: http://127.0.0.1:5000/api/users/1000
Method: POST
Headers: {"content-type": "application/json", "Random": "A2dEx", "Authorization": "47f135c33e858f2e3f55156ae9f78ee1"}
Body: {"name": "user1", "password": "123456"}
######
预期的正常响应数据:
Status_Code: 201
Headers: {'Date': 'Fri, 23 Jun 2017 07:05:41 GMT', 'Content-Length': '54', 'Content-Type': 'application/json', 'Server': 'Werkzeug/0.12.2 Python/2.7.13'}
Body: {"msg": "user created successfully.", "success": true, "uuid": "JsdfwerL"}

其中,请求Headers中的Random字段是一个5位长的随机字符串,Authorization字段是一个签名值,签名方式为TOKEN+RequestBody+Random拼接字符串的MD5值。更具体的,RequestBody要求字典的Key值按照由小到大的排序方式。接口请求成功后,返回的是一个JSON结构,里面的success字段标识请求成功与否的状态,如果成功,uuid字段标识新创建用户的唯一ID。

相信只要是接触过接口测试的同学对此应该都会很熟悉,这也是后台系统普遍采用的签名校验方式。在具体的系统中,可能字符串拼接方式或签名算法存在差异,但是模式基本上都是类似的。

那么面对这样一个接口,我们会怎样编写接口测试用例呢?

首先,请求的数据是要有的,我们会先准备一个可用的账号,例如{"password": "123456", "name": "user1"}

然后,由于接口存在签名校验机制,因此我们除了要知道服务器端使用的TOKEN(假设为debugtalk)外,还要准备好Random字段和Authorization字段。Random字段好说,我们随便生成一个,例如A2dExAuthorization字段就会复杂不少,需要我们按照规定先将RequestBody根据字典的Key值进行排序,得到{"name": "user1", "password": "123456"},然后与TOKENRandom字段拼接字符串得到debugtalk{"password": "123456", "name": "user1"}A2dEx,接着再找一个MD5工具,计算得到签名值a83de0ff8d2e896dbd8efb81ba14e17d

最后,我们才可以完成测试用例的编写。假如我们采用YAML编写测试用例,那么用例写好后应该就是如下样子。

-
    name: create user which does not exist
    request:
        url: http://127.0.0.1:5000/api/users/1000
        method: POST
        headers:
            Content-Type: application/json
            authorization: a83de0ff8d2e896dbd8efb81ba14e17d
            random: A2dEx
    data:
        name: user1
        password: 123456
    response:
        status_code: 201
        headers:
            Content-Type: application/json
        body:
            success: true
            msg: user created successfully.
            uuid: JsdfwerL

该测试用例可以在ApiTestEngine中正常运行,我们也可以采用同样的方式,对系统的所有接口编写测试用例,以此实现项目的接口自动化测试覆盖。

但问题在于,每个接口通常会对应多条测试用例,差异只是在于请求的数据会略有不同,而测试用例量越大,我们人工去准备测试数据的工作量也就越大。更令人抓狂的是,我们的系统接口不是一直不变的,有时候会根据业务需求的变化进行一些调整,相应地,我们的测试数据也需要进行同步更新,这样一来,所有相关的测试用例数据就又得重新计算一遍(任意字段数据产生变化,签名值就会大不相同)。

可以看出,如果是采用这种方式编写维护接口测试用例,人力和时间成本都会非常高,最终的结果必然是接口自动化测试难以在实际项目中得以开展。

理想的用例描述方式

在上面案例中,编写接口测试用例时之所以会很繁琐,主要是因为接口存在签名校验机制,导致我们在准备测试数据时耗费了太多时间在这上面。

然而,对于测试人员来说,接口的业务功能才是需要关注的,至于接口采用什么签名校验机制这类技术细节,的确不应耗费过多时间和精力。所以,我们的接口测试框架应该设法将接口的技术细节实现和业务参数进行拆分,并能自动处理与技术细节相关的部分,从而让业务测试人员只需要关注业务参数部分。

那要怎么实现呢?

在开始实现之前,我们不妨借鉴BDD(行为驱动开发)的思想,先想下如何编写接口测试用例的体验最友好,换句话说,就是让业务测试人员写用例写得最爽。

还是上面案例的接口测试用例,可以看出,最耗时的地方主要是计算签名校验值部分。按理说,签名校验算法我们是已知的,要是可以在测试用例中直接调用签名算法函数就好了。

事实上,这也是各种模板语言普遍采用的方式,例如Jinja2模板语言,可以在{% %}中执行函数语句,在{{ }}中可以调用变量参数。之前我在设计[AppiumBooster][AppiumBooster]时也采用了类似的思想,可以通过${config.TestEnvAccount.UserName}的方式在测试用例中引用预定义的全局变量。

基于该思路,假设我们已经实现了gen_random_string这样一个生成指定位数的随机字符串的函数,以及gen_md5这样一个计算签名校验值的函数,那么我们就可以尝试采用如下方式来描述我们的测试用例:

- test:
    name: create user which does not exist
    variable_binds:
        - TOKEN: debugtalk
        - random: ${gen_random_string(5)}
        - json: {"name": "user", "password": "123456"}
        - authorization: ${gen_md5($TOKEN, $json, $random)}
    request:
        url: http://127.0.0.1:5000/api/users/1000
        method: POST
        headers:
            Content-Type: application/json
            authorization: $authorization
            random: $random
        json: $json
    extract_binds:
        user_uuid: content.uuid
    validators:
        - {"check": "status_code", "comparator": "eq", "expected": 201}
        - {"check": "content.success", "comparator": "eq", "expected": true}

在如上用例中,用到了两种转义符:

  • $作为变量转义符,$var将不再代表的是普遍的字符串,而是var变量的值;
  • ${}作为函数的转义符,${}内可以直接填写函数名称及调用参数,甚至可以包含变量。

为什么会选择采用这种描述方式?(Why?

其实这也是我经过大量思考和实践之后,才最终确定的描述方式。如果真要讲述这个思路历程。。。还是不细说了,此处可省下一万字。(主要的思路无非就是要实现转义的效果,并且表达要简洁清晰,因此必然会用到特殊字符;而特殊字符在YAML中大多都已经有了特定的含义,排除掉不可用的之后,剩下的真没几个了,然后再借鉴其它框架常用的符号,所以说最终选择$${}也算是必然。)

可以确定的是,这种描述方式的好处非常明显,不仅可以实现复杂计算逻辑的函数调用,还可以实现变量的定义和引用。

除了转义符,由于接口测试中经常需要对结果中的特定字段进行提取,作为后续接口请求的参数,因此我们实现了extract_binds这样一个结果提取器,只要返回结果是JSON类型,就可以将其中的任意字段进行提取,并保存到一个变量中,方便后续接口请求进行引用。

另外,为了更好地实现对接口响应结果的校验,我们废弃了先前的方式,实现了独立的结果校验器validators。这是因为,很多时候在比较响应结果时,并不能简单地按照字段值是否相等来进行校验,除此之外,我们可能还需要检查某个字段的长度是否为指定位数,元素列表个数是否大于某个数值,甚至某个字符串是否满足正则匹配等等。

相信你们肯定会想,以上这些描述方式的确是很简洁,但更多地感觉是在臆想,就像开始说的gen_random_stringgen_md5函数,我们只是假设已经定义好了。就算描述得再优雅再完美,终究也还只是YAML/JSON文本格式而已,要怎样才能转换为执行的代码呢?

这就要解决How?的问题了。

嗯,这就是用例模板引擎的核心了,也算是ApiTestEngine最核心的功能特性。

更具体的,从技术实现角度,主要分为三大块:

  • 如何在用例描述(YAML/JSON)中实现函数的定义和调用
  • 如何在用例描述中实现参数的定义和引用,包括用例内部和用例集之间
  • 如何在用例描述中实现预期结果的描述和测试结果的校验

这三大块内容涉及到较多的技术实现细节,我们将在后续的文章中结合代码逐个深入进行讲解。

阅读更多

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 65 条回复
1楼 已删除
104 seveniruby 将本帖设为了精华贴 07月08日 07:47
6504

的确,关于参数签名,加密的确不方便,我现在是直接在代码里去处理,期望后续的更新

2c8322

缩减版robotframework

6109
debugtalk · #5 · 2017年07月08日 作者
6504lose 回复

已经写得差不多了,只是还要疏通下逻辑

6109
debugtalk · #6 · 2017年07月08日 作者
2c8322xatm092 回复

roboframework能实现压测么?先剧透下,APITestEngine可以很好的实现哦,而且是直接基于同一份用例。
例如:

#coding: utf-8
import zmq
import os
from locust import HttpLocust, TaskSet, task
from ate import utils, runner

class WebPageTasks(TaskSet):
    def on_start(self):
        self.test_runner = runner.Runner(self.client)
        self.testset = self.locust.testset

    @task(weight=10)
    def test_login(self):
        results = self.test_runner.run_testset(self.testset)
        assert results[0] == (True, [])

class WebPageUser(HttpLocust):
    host = 'http://www.skypixel.com'
    task_set = WebPageTasks
    min_wait = 1000
    max_wait = 5000

    testcase_file_path = os.path.join(
        os.getcwd(), 'testcases/skypixel/open_app.yml')
    testsets = utils.load_testcases_by_path(testcase_file_path)
    testset = testsets[0]
6109
debugtalk · #7 · 2017年07月08日 作者
6109debugtalk 回复

而且再仔细看这个压测脚本实现方式,会发现模板性很强,所以这完全可以自动生成。也就是说,只需要维护一套YAML/JSON格式的用例,就可以同时实现接口自动化和性能压测了。

6504

压测这里打算集成Locus?

6109
debugtalk · #9 · 2017年07月08日 作者
6504lose 回复

是的,直接复用的,现在最新的代码已经有了这个特性了,只是还没写文档

3c6eb3

你好 我在看你共享的源码 有个问题 我没看明白 下面几个文件有啥区别

demo_import_functions.yml demo_template_separate.yml demo_template_sets.yml
设计初衷是什么 多谢

6109
debugtalk · #11 · 2017年07月08日 作者
3c6eb3SunnyFong 回复

这些只是用来测试框架的数据而已。分别对应着不同的特性,例如单个测试用例,测试用例集。另外,由于代码比文档超前很多,很多特性还没来得及介绍。

Ee76af

板凳搬好,等更新

6109
debugtalk · #13 · 2017年07月09日 作者
Ee76afkasi 回复

卡斯大叔起得好早啊,膜拜

14楼 已删除
14381

如果是基于依赖的接口应该怎么弄呢?

6109
debugtalk · #16 · 2017年07月10日 作者
14381tester6636865 回复

你说的依赖接口指的是这种情况么?B接口的参数需要A接口返回的值。
针对这种情况,我的实现方式是,在用例配置的时候可以选择将接口返回结果的特性字段提取出来,绑定到一个变量中,然后后续接口请求就可以直接引用了。

14381
6109debugtalk 回复

那就类似例中填写A接口的左右边界值,后台提取后用在B接口中是吧,那放在B接口的哪个位置呢?这个是不是还是用左右边界值来确定呢?另外还想问,以数据驱动的话,用例应该写在哪里呢?Excel?还是Cucumber框架,亦或是有什么其他办法?

6109
debugtalk · #18 · 2017年07月10日 作者
14381tester6636865 回复

数据提取方式这部分,我借鉴的是pyresttest框架的方式,你可以看下代码的单元测试中用到的例子。

def test_extract_response_json(self):
    resp = requests.post(
        url="http://127.0.0.1:5000/customize-response",
        json={
            'headers': {
                'Content-Type': "application/json"
            },
            'body': {
                'success': False,
                "person": {
                    "name": {
                        "first_name": "Leo",
                        "last_name": "Lee",
                    },
                    "age": 29,
                    "cities": ["Guangzhou", "Shenzhen"]
                }
            }
        }
    )

    extract_binds = {
        "resp_status_code": "status_code",
        "resp_headers_content_type": "headers.content-type",
        "resp_content_body_success": "body.success",
        "resp_content_content_success": "content.success",
        "resp_content_text_success": "text.success",
        "resp_content_person_first_name": "content.person.name.first_name",
        "resp_content_cities_1": "content.person.cities.1"
    }
    resp_obj = response.ResponseObject(resp)
    extract_binds_dict = resp_obj.extract_response(extract_binds)

    self.assertEqual(
        extract_binds_dict["resp_status_code"],
        200
    )
    self.assertEqual(
        extract_binds_dict["resp_headers_content_type"],
        "application/json"
    )
    self.assertEqual(
        extract_binds_dict["resp_content_content_success"],
        False
    )
    self.assertEqual(
        extract_binds_dict["resp_content_text_success"],
        False
    )
    self.assertEqual(
        extract_binds_dict["resp_content_person_first_name"],
        "Leo"
    )
    self.assertEqual(
        extract_binds_dict["resp_content_cities_1"],
        "Shenzhen"
    )
6109
debugtalk · #19 · 2017年07月10日 作者
14381tester6636865 回复

用例编写方式,推荐的是YAML/JSON的格式。写好后放置到任意文件夹中,然后通过如下方式执行即可。

$ python main.py --testcase-path TESTCASE_FOLDER

具体的使用方法部分我还在写。

14381
6109debugtalk 回复

了解,谢谢~!

115

到wiki建个合集吧😀

6109
debugtalk · #22 · 2017年07月10日 作者
115oscarxie 回复

我看了下,貌似没有权限

115
6109debugtalk 回复

好了,可以试试

6109
debugtalk · #24 · 2017年07月10日 作者
115oscarxie 回复

好了,已经添加了

3c6eb3

测试数据是如何存放的,与编写的用例一起存放于同一份yaml文件中吗

6109
debugtalk · #26 · 2017年07月10日 作者
3c6eb3SunnyFong 回复

当前是这样的,测试数据与测试用例是一起的

3c6eb3
6109debugtalk 回复

OK,了解了,还有个问题:
假设要遍历测试某个参数不同值(如password),你这边是如何实现的?

6109
debugtalk · #28 · 2017年07月10日 作者
3c6eb3SunnyFong 回复

我明白你的意思,是要同一个测试用例,对应多个不同的测试数据对吧?
这块儿暂时还没有特殊处理,暂时还是以一个测试用例对应一种测试数据组合的形式。
后面的想法有考虑通过函数调用来获取测试数据,例如在特定范围内获取随机值,或者从数据库表中获取特定字段。

12235

怎么获取上一个用例里生成的值用在下一个用例里?怎么从数据库查询值用在用例里?
感觉没有这两样简直做不下去,n多接口有数据依赖

6109
debugtalk · #30 · 2017年07月14日 作者
122359688e 回复

第一个问题,参考这个 issue
第二个问题,可以将数据库查询封装到函数调用里面,然后参考《ApiTestEngine (3)》

3c6eb3

执行文件test_apiserver_v2.py,运行至启动flask mock server时,报如下错误,能帮忙看下是哪里的问题吗?

Traceback (most recent call last):
File "", line 1, in
File "E:\Program Files\python3\lib\multiprocessing\spawn.py", line 105, in spawn_main
exitcode = main(fd)
File "E:\Program Files\python3\lib\multiprocessing\spawn.py", line 115, in _main
self = reduction.pickle.load(from_parent)
EOFError: Ran out of input
Failure
Traceback (most recent call last):
File "E:\Program Files\python3\lib\unittest\suite.py", line 163, in _handleClassSetUp
setUpClass()
File "E:\Program Files\JetBrains\PyCharm Community Edition 2017.1.1\project\ApiTestEngine-master\test\base.py", line 21, in setUpClass
cls.api_server_process.start()
File "E:\Program Files\python3\lib\multiprocessing\process.py", line 105, in start
self._popen = self._Popen(self)
File "E:\Program Files\python3\lib\multiprocessing\context.py", line 223, in _Popen
return _default_context.get_context().Process._Popen(process_obj)
File "E:\Program Files\python3\lib\multiprocessing\context.py", line 322, in _Popen
return Popen(process_obj)
File "E:\Program Files\python3\lib\multiprocessing\popen_spawn_win32.py", line 65, in __init
_
reduction.dump(process_obj, to_child)
File "E:\Program Files\python3\lib\multiprocessing\reduction.py", line 60, in dump
ForkingPickler(file, protocol).dump(obj)
TypeError: can't pickle _thread.lock objects

6109
debugtalk · #32 · 2017年07月14日 作者
3c6eb3SunnyFong 回复

是采用这种方式运行的么?
python -m unittest test.test_apiserver_v2.py

3c6eb3
6109debugtalk 回复

对的

6109
debugtalk · #34 · 2017年07月15日 作者
3c6eb3SunnyFong 回复

看你贴的堆栈日志,报的错都不是框架部分的,不确定你那边的环境是否正常。
不过我也在Mac和Linux系统上测试过,后面也找台Windows试下是不是有兼容性问题。

5819

挺好的,期待完结

6109
debugtalk · #36 · 2017年07月21日 作者
5819cyj86 回复

后面还是要先写使用说明文档了,实现细节关注的人好少

16120

请问下,为啥不把token等加密信息封装在代码里面,而是放在了测试用例上呢?这样做会不会导致加密信息容易泄露?封装在代码上的话,判断需要带上签名的接口再调用这个加密算法即可,不知道这个方法对不对,还请指教下~🙋 🙋

6109
debugtalk · #38 · 2017年07月24日 作者
16120Ningxw 回复

这里的token表述的确不准确;我已经修改了案例,你可以直接看最新的代码,在README中的案例:https://github.com/debugtalk/ApiTestEngine

16120
6109debugtalk 回复

好的,我去下载看看~期待文章的下一个系列~

E39152

准备仿造您的文章,自己再造一遍轮子。
不过我只会java,看不太懂python代码。

现在暂时还卡在这篇文章这里的,最近每天晚上都会先打开这篇文章看看。
现在用例基本是按照你的这个模式来的,不过是写的json。

现在我们接口的自动化已经做了一部分了,TestNG+reportNG+jenkins+testlink+restassured,不过是那种全靠写代码的。需要一个新的用例,就自己去加一个@Test

现在想造一遍别人可能做过很多次的轮子。
加上测试用例的管理,然后加一个web来作为测试用例新增、修改、执行的后台。
太多纠结的地方了,太多需要权衡的地方了。稍稍考虑下灵活性,就会复杂很多倍。

6109
debugtalk · #41 · 2017年08月21日 作者
E39152itsgoodtobebad 回复

不用这么跟自己过不去啊,Java和Python的语言特性差异本来就比较大,如果照搬Python实现的特性再采用Java来实现,可能会非常费事。个人建议,如果用Java,那么就尽量发挥Java的特长,采用Java的方式去做就好啦。

E39152
6109debugtalk 回复

并不是说要把python改写为java。
只是我也刚学这个,这是我现在唯一看到,讲得清楚,又有源代码的项目。
所以有疑惑的时候就过来研究,看看你是怎么处理的:)

如果有java的开源代码可以学习当然更好了,目前还没找到。

6109
debugtalk · #43 · 2017年08月21日 作者
E39152itsgoodtobebad 回复

好的,多多交流,你也可以加我微信 leolee023

E39152
6109debugtalk 回复

多谢。
我现在写了一部分解析测试用例的代码了。

我的sign比较复杂,需要用到实时产生的timestamp、传入的appkey、消息体body本身(或者query string排序)。
timestamp可以作为variable,但是body(或者query string排序)就不好办了。

总感觉需要多次扫描测试用例,或者按照特定的顺序来读取测试用例。 —— 我是计算了函数后将结果替换回去,再来扫描的

反正就是特别不爽,感觉这个sign要特殊处理。

E39152
E39152itsgoodtobebad 回复

昨天晚上想了又想,发现是我太纠结要通用了。想着换一套接口也能用。

本身能力有限,却想着做出来要尽量通用,或者看到别人是怎么怎么样的,难免患得患失。
其实我现在应该尽量少的扩展,将已有的底层功能一一进行封装,然后再根据情况考虑别的事情。
准备回头去修改代码去了:)

6385

locust能复用是因为你们接口框架用的是requests吧,这个方案我们两年多前就用了。但locust性能很差,真心不推荐

6109
debugtalk · #47 · 2017年08月24日 作者
6385jacexh 回复

性能再差也比LR/Jmeter强,一般的业务系统也上不了多大的并发,再不济还可以分布式,所以在压测工具的性能和易用性方面,我优先考虑后者。

6385
6109debugtalk 回复

我不知道你觉得locust性能强于jmeter这种结论是哪来的,你可以去搜下locust issue,质疑其性能的有很多,如:
https://github.com/locustio/locust/issues/586

根据我很早之前的测试,locust单节点QPS上限约在600 QPS左右,而且response time明显异常
也就是说在一台4核的PC机上,跑4个节点的测试,至多产生2400 QPS
而jmeter却可以产生22K QPS

当然jmeter基于java线程的并发也不算优秀,ab/wrk/golang能达到50K+QPS

6109
debugtalk · #49 · 2017年08月24日 作者
6385jacexh 回复

在你给的issue链接里面,原作者也回答了,and the optimization is unnecessary in most cases.。就大多数系统的并发性能之低,还用不着压测工具拼性能。

10693

这边实测的结果也是现在被人看好的gatling和locust真的没法跟被鄙视的jmeter比极限性能

jmeter从2.x开始每个小版本都在优化性能和采用更合理的默认配置,现在3.2版简单调下JVM参数,测部署在配置一般的服务器上的hello world或nginx_stub,不比ab/wrk差多少

我测的结果是ab/wrk 34k左右,jmeter 26k-29k,号称高性能的gatling 14-22k,locust + zeromq + 8个slave才2-3k……

但!是!jmeter太旧了,诞生的时候NIO还没出来,收发请求是同步的,被测程序响应慢就会导致一堆线程堵塞,没有足够的压力到服务器,工具本身再快也没用。我们很少会去测静态页或从redis读数据直接返回的接口,多数时候它的表现都不能让人满意


gatling最近在用,本来想全面转向它的,越用越不对劲,before()after()里不支持gatling的DSL,要用原生scala,这会导致同一个接口在准备数据和跑测试时要以不同的方式写2次,复用起来也很麻烦

被requests惯坏了的人看到scala里的各种http库的api就倒胃口,碰都不想碰,估计只有java程序员忍得了(逃

它的分布式也相当简单粗暴:在不同机器开几个,跑完把log文件拼起来

另外今天才发现它没法做到真的并行测多个接口,比如已知4个RPS差不多,互相无依赖的接口放进去,结果 A >> B >> C >> D,后面每个都只有前一个的一半左右,issue在这:https://github.com/gatling/gatling/issues/2336

最后就是我也不懂scala,暂时弃坑╮(╯▽╰)╭


locust,很恶心也很舒服……官方文档没多详细,例子极少,必须要看源码和踩过坑的人写的教程才能入门……

它只有一个骨架,几乎什么都不提供要自己实现,好听点自由,不好听寒碜……

但是python + requests顺手啊!╮(╯▽╰)╭

开发效率实在高太多了,尤其像我这样主要测单个接口、单个模块/微服务而不是一长串乱七八糟的业务的,可以一堆不相关接口放一块运行,RPS跟gatling测单个接口没什么区别,节省大量时间

谁叫绝大多数程序的效率比测试工具低得多呢╮(╯▽╰)╭

对于目标是在开发过程中尽早暴露问题而不是验收的人来说,locust的缺点可以接受,之后基本就用它了

期待有一天ApiTestEngine的热度超过locust本身 XD

6109
debugtalk · #51 · 2017年08月25日 作者
10693keithmork 回复

看到最后一句不禁泪流满面,我会努力的。
其实用Locust最爽的的确就是 Python + Requests 了,实现各种业务逻辑都很方便,但是当脚本多了以后,会发现也存在挺多重复的,所以我在ApiTestEngine中采用YAML来描述用例,尽量消除了不必要的重复,同时也复用了Requests的强大功能,在跑的时候采用 locusts 命令,直接就能跑压测了,感觉应该是挺不错的特性的😅

0bd0c4

mark先。辛苦了。

6385
10693keithmork 回复

我觉得最大的问题不是locust产生的压力小,而是其response time明显偏高,特殊情况偏高数十倍
结果很难信任

6109
debugtalk · #55 · 2017年09月09日 作者
6385jacexh 回复

后面我也做了benchmark对比看下。

7446
10693keithmork 回复

方便留下qq吗?交流交流

7446
6385jacexh 回复

那服务器用什么搭啊?

6385
7446kaniki 回复

nginx即可

location /benchmark {
    return 200 'pong!'
}
7446
6385jacexh 回复

哈哈哈 谢谢 方便加qq吗?

A24049

楼主,拜读了你的源码,发现就是和fitness一样类似,建议你看看fitness,在页面上写,和你的东西惊人的相似,而且也可以多线程,只需要java后台代码,原理就是web端通过关键字调用后台代码!

C40934
A24049yinquanwang 回复

fitness 代码网址给个,我也去看看

A24049
C40934Jackychen 回复

网上搜下就知道了,我们公司就用的这个,也可以和jenkins持续集成

6109
debugtalk · #63 · 2017年09月29日 作者
A24049yinquanwang 回复

你说的是这个? http://fitnesse.org/
我大概看了下,差异还是挺大的。
后面你能写篇文章介绍下你们的实践方式么?期待

A24049
6109debugtalk 回复

和作者提供的其实是一样的,通过一系列关键字去定义接口脚本,也是定义数据,调用方法,具体用法看后台代码设计了,感觉你写的和我们公司用的太像了!不过用例最好打个标签吧!一个suite如果有些不想执行的话你这个就有限制了

10693
6385jacexh 回复

今天遇到了,看见locust里每个接口的平均响应时间都很整齐的1秒多,拿nginx_status页面试了一下,其他工具报告平均4ms,locust竟然报300ms,结果完全没法信了

总之先提个issue……,也给准备入坑的人看看这夸张的数字……
https://github.com/locustio/locust/issues/663


再来个真正的接口的例子,jmeter 200线程 vs locust 200协程

之前用locust做了两个项目都没有这么离谱,返回的数字都是比较合理的,今天不清楚什么情况……


6385
10693keithmork 回复

我的经验是:压比较快的接口(<=10ms),locust完全不可信

10693
6385jacexh 回复

大概知道上面是怎么回事了,似乎跟自己发请求自己监控有关,虽然电脑明明还有不少资源

用了master/slave就正常多了,对很快的请求这是不能接受的,10倍时间……好在那种接口通常ab或wrk就够用

有空再翻翻源码看看怎么回事,另外那个hatch rate和number of user的设置跟实际的上限和增长率的关系也是个迷……

6385
10693keithmork 回复

我觉得可能是gevent的锅,但我对这玩意不太感冒,没继续研究下去
在我们这边,locust已经承担不了压测任务了,我们现在改用golang去做

10693
6385jacexh 回复

你们是用现成的工具/框架还是自己造轮子?

换工具这种事超级烦,之前的脚本不通用: (
项目里已经用过jmeter、gatling、locust,没一个满意,浪费不少时间,被老大骂很多次了……应付临时任务还是jmeter最适合,无论效率还是性能

有空准备看看taurus,如果它的yaml用例格式比较合理就模仿一下,以后让新工具支持解析这种文件就不用那么烦了……

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册