编写 locust file
Locust 文件是一个普通的 python 文件。唯一的要求是它必须声明至少一个从 User 类继承的类。
User 用户类
一个用户类代表一个用户。Locust 将为每个被模拟的用户产生一个 User 类的实例。User 类可以定义一些常见的属性。
wait_time 属性
User 类的 wait _ time
方法是一个可选属性,用于确定模拟用户在执行任务之间应该等待多长时间。如果没有指定等待时间,则一个新任务完成后将立即执行。
有三个内置的等待时间函数:
-
constant
固定时间 -
between
在最小值和最大值之间的随机时间 -
constant_pacing
一个自适应时间,确保任务每 x 秒运行一次
例如,让每个用户在每次任务执行之间等待0.5到10秒:
from locust import User, task, between
class MyUser(User):
@task
def my_task(self):
print("executing my_task")
wait_time = between(0.5, 10)
也可以直接在类上声明自己的 wait _ time 方法。例如,下面的 User 类将休眠一秒钟,然后两秒钟,然后三秒钟,等等。
class MyUser(User):
last_wait_time = 0
def wait_time(self):
self.last_wait_time += 1
return self.last_wait_time
...
weight 属性
如果文件中存在多个用户类,可以通过命令行参数来指定在同一个 locustfile 中使用哪个用户类:
$ locust -f locust_file.py WebUser MobileUser
如果希望模拟某种类型的更多用户,可以在这些用户类上设置一个 weight 属性。例如,WebUser 比 MobileUser 的可能性高三倍:
class WebUser(User):
weight = 3
...
class MobileUser(User):
weight = 1
...
host 属性
Host 属性是目标系统的 URL 前缀(比如“ http://google.com””)。通常,这是在 Locust 的 web UI 或命令行中指定的。
如果在 User 类中声明了一个 host 属性,那么在命令行或 web 请求中未指定 host 的情况下将使用该host。
environment 属性
对用户运行environment
的引用。使用它来与环境或其中包含的runner
进行交互。例如,从某个task方法中停止runner
:
self.environment.runner.quit()
如果运行一个独立的Locust 实例,这将停止整个运行。如果在worker节点上运行,它将停止该节点。
on_start 和 on_stop 方法
User(和 TaskSet)可以声明 on _ start 方法、 on _ stop 方法。用户在开始运行时调用 on _ start 方法,在停止运行时调用 on _ stop 方法。对于 TaskSet,当模拟用户开始执行 TaskSet 时调用 on _ start 方法,当模拟用户停止执行 TaskSet 时调用 on _ stop。
Tasks 任务
当负载测试启动时,将为每个模拟用户创建一个 User 类的实例,在自己的协程中运行。当这些用户运行时,他们会一直重复“选择待执行任务执行-等待-再次选择待执行任务执行”这样的过程。
@task 装饰器
使用@task
装饰器为 User 添加任务。
from locust import User, task, constant
class MyUser(User):
wait_time = constant(1)
@task
def my_task(self):
print("User instance (%r) executing my_task" % self)
@task
接受一个可选的 weight 参数,该参数可用于指定任务的执行权重。在下面的例子中,task2被选中的几率是 task1的两倍:
from locust import User, task, between
class MyUser(User):
wait_time = between(5, 15)
@task(3)
def task1(self):
pass
@task(6)
def task2(self):
pass
tasks 属性
另一种定义 User 任务的方法是设置 tasks 属性。
tasks 属性是 Tasks 的列表,或者是一个 < Task: int > 字典,其中 Task 是 python 可调用的类或 TaskSet 类。
from locust import User, constant
def my_task(user):
pass
class MyUser(User):
tasks = [my_task]
wait_time = constant(1)
如果将 tasks 属性指定为列表,则每次执行任务时,都将从 tasks 属性中随机选择任务。但是,如果任务是 字典——使用 callables 作为键,使用整型 int 作为值——则将随机选择要执行的任务,但使用 int 作为权重。如下所示:
{my_task: 3, another_task: 1}
my_task 被执行的可能性是 another_task 的3倍。
@ tag 装饰器
通过使用@tag 装饰器标记任务,您可以使用 -- tags 和 -- exclude-tags 参数来挑选测试期间执行的任务。例子:
from locust import User, constant, task, tag
class MyUser(User):
wait_time = constant(1)
@tag('tag1')
@task
def task1(self):
pass
@tag('tag1', 'tag2')
@task
def task2(self):
pass
@tag('tag3')
@task
def task3(self):
pass
@task
def task4(self):
pass
如果您使用 -- tag1开始这个测试,那么在测试期间只会执行 task1和 task2。如果以 -- tag2 tag3开始,那么只执行 task2和 task3。
--exclude-tags
则相反,是做排除选择。因此,如果以 --exclude-tags tag3
开始测试,则只执行 task1、 task2和 task4。排除
总是优先于包含
,因此如果一个任务包含一个您已经包含的标记和一个您已经排除的标记,那么它将不会被执行。
Events
如果您想运行一些设置代码作为测试的一部分,通常只需将其放在 locustfile 的模块级别,但有时您需要在运行过程中的特定时间做一些事情。为此,Locust 提供了events hooks 钩子。
test_start 和 test_stop
如果您需要在负载测试的开始或停止时运行一些代码(注意区别里user task级别的 on_start on_stop),则应该使用 test_start
和 test_stop
事件。您可以在Locust 文件的模块级设置这些事件的侦听器:
from locust import events
@events.test_start.add_listener
def on_test_start(environment, **kwargs):
print("A new test is starting")
@events.test_stop.add_listener
def on_test_stop(environment, **kwargs):
print("A new test is ending")
当 Locust 分布式(master-worker)运行时,test _ start 和 test _ stop 事件将只在master节点中触发。
init 初始化
init
事件在每个 Locust 进程开始时触发。这在分布式模式中特别有用,因为每个worker(不是每个用户)都需要一次机会来进行一些初始化。例如:
from locust import events
from locust.runners import MasterRunner
@events.init.add_listener
def on_locust_init(environment, **kwargs):
if isinstance(environment.runner, MasterRunner):
print("I'm on master node")
else:
print("I'm on a worker or standalone node")
other events
... ...
HttpUser 类
HttpUser
是最常用的User
,它添加了一个用于发出 HTTP 请求的client
属性。
from locust import HttpUser, task, between
class MyUser(HttpUser):
wait_time = between(5, 15)
@task(4)
def index(self):
self.client.get("/")
@task(1)
def about(self):
self.client.get("/about/")
client 属性 / HttpSession
client
是 HttpSession 的一个实例。HttpSession 是requests.Session
的子类/包装器。因此如果熟悉requests的小伙伴就会对此很熟悉。
client 包含所有 HTTP 方法: get、 post、 put、 ..。
就像requests.Session
一样。它在请求之间会保存 cookie,所以它可以很容易地用于需要登录的使用场景。
发出一个 POST 请求,查看响应并在第二个请求时使用已经获得的 session cookie
response = self.client.post("/login", {"username":"testuser", "password":"secret"})
print("Response status code:", response.status_code)
print("Response text:", response.text)
response = self.client.get("/my-profile")
捕获任何请求。Session 抛出的 RequestException (由连接错误、超时或类似情况引起) ,而是返回一个虚拟的 Response 对象,其 status _ code 设置为0,内容设置为 None。
断言响应
如果 HTTP 响应代码是 OK 的(< 400) ,那么请求被认为是成功的,但是对响应进行一些额外的验证通常是有用的。
通过使用 catch_response 参数、 with-statement 和对 response.failure ()的调用,可以将请求标记为 failed
with self.client.get("/", catch_response=True) as response:
if response.text != "Success":
response.failure("Got wrong response")
elif response.elapsed.total_seconds() > 0.5:
response.failure("Request took too long")
你也可以将一个请求标记为成功,即使响应代码不正确:
with self.client.get("/does_not_exist/", catch_response=True) as response:
if response.status_code == 404:
response.success()
您甚至可以通过抛出异常,然后在 with-block 外捕获异常,从而完全避免记录请求。或者你可以抛出一个 Locust 异常,就像下面的例子一样,然后让 Locust 捕捉它。
from locust.exception import RescheduleTask
...
with self.client.get("/does_not_exist/", catch_response=True) as response:
if response.status_code == 404:
raise RescheduleTask()
使用动态参数对 URL 请求进行分组
比如一些网站网址包含某种动态参数,可以通过向 HttpSession 的request方法传递一个 name 参数来实现请求分组。
例子:
# 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]")
代理设置
... ...
Tasksets
Tasksets 是一种结构化等级网站/系统测试的方法。
如何组织测试代码
对于小型测试,可以将所有测试代码保存在一个 locustfile.py ,但是对于较大的测试套件,您可能希望将代码分割成多个文件和目录。
如何构造测试源代码当然完全取决于您,但是我们建议您遵循 Python 最佳实践。下面是一个想象中Locust 项目的文件结构示例:
项目根目录
│ locustfile.py
│ requirements.txt # 外部 Python 依赖项通常保存在 requirements.txt 中
│
└─common
auth.py
config.py
__init__.py
有多个不同Locust 文件的项目也可以将它们保存在一个单独的子目录中:
项目根目录
│ requirements.txt
│
├─common
│ auth.py
│ config.py
│ __init__.py
│
└─locustfiles
api.py
website.py