locust是一个相对容易上手的一个分布式用户负载测试工具,支持Web UI实时监控以及无头模式运行。
安装也非常简单,需要先确定自己的python在3.6以上版本,然后再安装Locust,执行安装命令pip install locust即可。
如何编写locust的python脚本?下面是locust的一些类和属性的介绍
1. HttpUser 类是用于模拟 HTTP 请求的基本用户行为类,首先需要定义一个继承自 HttpUser 的类,并在其中定义用户的操作,其中的一些属性参数
client 属性,类似于requests中请求方式支持以下几种方式
- client.get(url, **kwargs):发送 GET 请求。
- client.post(url, data=None, json=None, **kwargs):发送 POST 请求。
- client.put(url, data=None, **kwargs):发送 PUT 请求。
- client.delete(url, **kwargs):发送 DELETE 请求。
- client.head(url, **kwargs):发送 HEAD 请求。
- client.options(url, **kwargs):发送 OPTIONS 请求
拿我们常用的post请求举例子
from locust import HttpUser, task
class MyUser(HttpUser):
@task
def login(self):
self.client.post("/login", {"username": "test", "password": "test"})
除了基本的请求方法之外,client 还支持传递额外的参数,如 headers、params 等。
1.2 wait_time 属性
wait_time 属性用于设置虚拟用户在执行完一次任务后等待的时间。这可以通过 between(min_wait, max_wait) 方法来设置一个随机的等待时间范围,如 wait_time = between(5, 15)
1.3 host 属性
HttpUser 类默认使用 host 属性来发送请求。如果未指定 host,则需要在每个请求中明确指定 URL.host属性,一般在HttpUser 类中设置 host 属性
示例:class MyTest(HttpUser):
host = "http://127.0.0.00:10001"
2 .TaskSet 类是一个容器类,用于定义一组任务,并且可以指定这些任务的执行频率。TaskSet 允许任务以随机顺序执行,并且可以为每个任务指定权重,权重较高的任务将更频繁地被执行
示例
from locust import task, TaskSet
class XnTest(TaskSet):
@task(4)
def more_page(self):
self.client.get("/one")
@task(1)
def litle_page(self):
self.client.get("/two")
@task(4) 和 @task(1):分别为两个任务指定了权重,就是@task(4)的more_page执行大概占比80%,@task(1)的litle_page占比为20%
3 .SequentialTaskSet 类与 TaskSet 类相似,但它的任务将按照定义的顺序依次执行,而不是随机执行。这意味着任务将按照你在脚本中定义的顺序依次执行,而不会被打乱
from locust import task, SequentialTaskSet
class UserTest(SequentialTaskSet):
@task
def index_page(self):
self.client.get("/")
@task
def profile_page(self):
self.client.get("/profile")
@task:定义了两个任务,它们将按照定义的顺序依次执行
4. 一个简单的SequentialTaskSet 类与 TaskSet类,HttpUser类一起结合使用
from locust import task, between,TaskSet, SequentialTaskSet,HttpUser
class LoginSequence(SequentialTaskSet):
@task
def login(self):
self.client.post("/login", {"username": "test", "password": "test"})
@task
def logout(self):
self.client.get("/logout")
class UserBer(TaskSet):
tasks = {LoginSequence: 2} # 指定顺序执行的任务
@task(4)
def index_page(self):
self.client.get("/one")
@task(1)
def profile_page(self):
self.client.get("/two")
class MyWebUser(HttpUser):
wait_time = between(5, 15)
host = "http:/xxxxxxxx.com"
tasks = [UserBer]
通过 TaskSet 和 SequentialTaskSet 类结合使用,我们可以简单定义模拟较为真实的用户行为,具体还是要看业务场景灵活使用
5. on_start和on_stop
就是每个用户实列化前会执行一次,oe_stop则是实例化后销毁一次,可以模拟用户的登录登出场景。
6. 对于多接口测试,在每个方法上加上task装饰器即可,task装饰器上可添加权重参数,把之前所用到的组合起来,并且加上with断言更贴近我们实际的测试场景。
from locust import HttpUser, task, between,TaskSet
class MyUser(TaskSet):
def on_start(self):
cs_url = "/login/xxxx/xxxx/xxx" # 替换为你的地址
# 模拟用户登录并获取 token
data_zero = {"username": "testuser", "password": "testpassword"}
response = self.client.post(cs_url, data=data_zero)
token = response.json()["token"]
self.token = token
@task(4)
def test_one(self):
url_one = "/xxxx/xxxx/xxxx/xxx"
data_one = {
"token": self.token, # 调用on_start中获取的token
"type": 1,
"size": "1",
"id": 12
}
with self.client.get(url_one, data=data_one, catch_response=True) as response:
if response.status_code == 200:
restext = {"name":"test_name", "id": 2}
textlist = response.json()["data"]
if restext in textlist:
response.success()
else:
response.failure("购物车返回的数据不正确")
else:
response.failure("请求失败状态码不正确,实际为".format(response.status_code))
@task(1) # 权重占比为1
def test_two(self):
cs_url = "/xxxx/xxxx/xxxx/xxx"
cs_data = {"token": self.token,
"id": 14,
"size": 1
}
with self.client.get(cs_url, data=cs_data,catch_response=True) as response:
if response.status_code == 200:
restext_1 = {"name": "test_name", "id": 2}
textlist_1 = response.json()["data"]
if restext_1 in textlist_1:
response.success()
else:
response.failure("返回的数据不正确")
else:
response.failure("请求失败状态码不正确,实际为".format(response.status_code))
class OP_login(HttpUser):
tasks = [MyUser]
host = "http://localhost:1001"
wait_time = between(1, 3)
在locust中我们一般用with进行自定义断言,with 语句结合 catch_response=True 参数可以用来获取和自定义请求的响应。这允许我们在请求失败时手动标记为成功或失败,并且可以添加自定义的失败原因,这让我们更好的去控制测试结果。
7. 在实际场景中不同时间段,客户用户数是不同的,为了更贴近实际的场景,可以这样去编写
from locust import HttpUser, task, between ,LoadTestShape
class MyWebUser(HttpUser):
wait_time = between(5, 15)
host = "http://example.com"
@task
def index_page(self):
self.client.get("/")
@task
def profile_page(self):
self.client.get("/profile")
class TestShape(LoadTestShape):
"""
自定义负载测试,模拟网站流量不同时间的流量。
"""
max_users = 500 # 最大用户数
start_rate = 50 # 每秒启动的用户数
def tick(self, run_time):
if run_time < 60: # 前 1 分钟内逐渐增加用户数
return (round(run_time * (self.max_users / 60)), self.start_rate)
elif run_time < 120: # 接下来的 1 分钟内保持最大用户数
return (self.max_users, self.spawn_rate)
elif run_time < 180: # 接下来的 1 分钟内逐渐减少用户数
return (round((180 - run_time) * (self.max_users / 60)), self.start_rate)
else: # 3 分钟后停止测试
return None
根据自己的实际情况来编写脚本结合使用,合理编写贴近自己场景的测试脚本,以上只是本人的一些分享,如有错误的地方欢迎指正。