本文写作时,使用当时最新版本的0.14.5,现在已更新到2.x,可能有误,请谨慎参考。


学习笔记1中主要是介绍了Locust的线程(虚拟用户)数的创建和执行策略,这里主要介绍用locust发起http请求

1.基本http请求

为了做各种场景测试,需要本地启动服务端模拟程序,先发一个无参的get请求和post请求(本地测试服务可以参照github搭建: https://github.com/wang153723482/http_world/tree/master/hello-springboot

这里不再继承Locust这个类,而是继承了HttpLocust,这样在TaskSet的子类中,可以直接使用self.client这个属性

from locust import HttpLocust, TaskSet, task
from locust.wait_time import constant
class Task(TaskSet):
    @task
    def index(self):
        self.client.get('/hello')

    @task
    def project(self):
        self.client.post('/hello_post')


class MyHttpLocust(HttpLocust):
    host = 'http://127.0.0.1:8080'
    task_set = Task
    wait_time = constant(1)

2.进阶

HttpLocust 有一个参数为client,在实例初始化的会创建一个session,用于访问http。session继承了requests.Session
HttpLocust继承了Locust

def __init__(self):
    super(HttpLocust, self).__init__()
    if self.host is None:
        raise LocustError("You must specify the base host. Either in the host attribute in the Locust class, or on the command line using the --host option.")

    session = HttpSession(base_url=self.host)
    session.trust_env = self.trust_env
    self.client = session

在TaskSet中,使用的client参数为self.locust.client,相关源码如下

@property
def client(self):
    """
    Reference to the :py:attr:`client <locust.core.Locust.client>` attribute of the root
    Locust instance.
    """
    return self.locust.client

总之,就是locust可以用你保持session,通过下面的测试可以证实

from locust import HttpLocust, TaskSet, task
from locust.wait_time import constant


class Task(TaskSet):

    @task
    def hello_session(self):
        self.client.get('/helloSession')


class MyHttpLocust(HttpLocust):
    host = 'http://127.0.0.1:8080'
    task_set = Task
    wait_time = constant(1)

启动以后,可以在测试服务的控制台看到打印的sessionid,对于多次请求,sessionid是相同的值,但是对于不同的虚拟用户,sessionid是不同的值

locust -f http_request.py --no-web -c 3 -t 10    乌鸦:这条命令启动了3个虚拟用户,session_id会有3个不同的值

对于jmeter的HttpRequest来说,需要添加HTTP Cookie Manager才能保持默认情况下的http session

除了常用的get和post,其他的method也是支持的。

get, post, put, delete, head, patch and options

获取请求的返回结果

response = self.client.get("/about")
print("Response status code:", response.status_code)
print("Response content:", response.text)

提交键值对类型的参数

para = {"name": "zhangsan", "age": "88"}
response = self.client.post('/helloPara', para)

提交body参数

body = "asdf"
response = self.client.post('/hello_body',data=body)

设置http header

headers={'Content-Type':'application/json','myheader':'heiheihaha'}
response = self.client.post('/helloHeader',headers=headers)

3.断言

默认情况下,locust会认为response_code<400的为成功
使用 catch_response=True 可以捕获响应,然后进一步判断来断定成功还是失败

with self.client.get("/", catch_response=True) as response:
    if response.content != b"Success":
        response.failure("Got wrong response")

或者

with self.client.get("/does_not_exist/", catch_response=True) as response:
    if response.status_code == 404:
        response.success()

都是官网文档上的例子

其他的断言也类似,获取相应结果以后,解析结果,如果符合预期,则 执行 response.success(),否则执行 response.failure("error msg")

4.其他

官网文档上还有一个例子,重命名分组,对于locust的报表来说,是根据url进行分组的,使用name=''这个参数,可以将url不同的请求重新分组
乌鸦:在locust中,get请求传递键值对参数,只允许在url中拼接。如果是post可以将参数拼接在url中,也可以使用字典传值

# Statistics for these requests will be grouped under: /blog/?id=[id]
for i in range(10):
    self.client.get("/blog?id=%i" % i, name="/blog?id=[id]")

安全模式
如果请求连接错误、超时或其他类似的错误,不会报异常退出,而是返回一个None,status_code返回0,并且会统计到错误报告中
下面这个错误就是在执行测试过程,手动关闭了目标服务器

POST /helloHeader:
'ConnectionError(MaxRetryError("HTTPConnectionPool(host='127.0.0.1',port=8080): Max retries exceeded with url: /helloHeader (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x04796A00>: Failed to establish a new connection: Errno 10061 由于目标计算机积极拒绝,无法连接。'))"))'

参考:
https://docs.locust.io/en/0.14.5/writing-a-locustfile.html#making-http-requests
https://docs.locust.io/en/0.14.5/api.html#httpsession-class