一、关联
在某些请求中,需要携带之前从Server端返回的参数,因此在构造请求时需要先从之前的Response中提取出所需的参数。
下面的示例代码中,用户登录后服务器返回的httptoken的值作为下个任务接口请求的请求头参数,实现了前后接口的关联。
from locust import HttpUser, task, SequentialTaskSet import hashlib import queue class UserBehavior(SequentialTaskSet): token = None def on_start(self): print("--on_start--") def on_stop(self): print("--on_stop--") @task(1) def login(self): url1 = "http://xx" url2 = "http://xx" headers1 = {'Content-Type': 'application/json;charset=UTF-8','ptype': '1'} headers2 = {'Content-Type': 'application/x-www-form-urlencoded'} # request_params = {"codeVerificationType": "1", "phoneNumber": '12345678988'} # res_get = requests.get(url, params=request_params, headers=headers) # print(res_get.text) try: # 从队列中取出数据 data = eval(self.user.user_data_queue.get_nowait()) print("测试数据:%s" % data) payload = { "codeVerificationType": "1", "phoneNumber": data['phoneNumber'] } res = self.client.get(url1, params=payload, headers=headers1) print(res.text) hl = hashlib.md5() hl.update(str(data['phoneNumber']).encode(encoding='utf-8')) code = hl.hexdigest() print("手机号加密后:%s" % code) par_data = {"channel": 'cocos', "deviceId": None, "deviceType": None, "uniqueId": None, "loginType": 1, "phoneType": 1, "userIdentity": code, "userType": 1, "verificationCode": '123456', "version": '0.9', "versionName": '0.9', "accessToken": None, "token": None } res_post = self.client.post(url2, data=par_data, headers=headers2) self.token = res_post.json()["result"]["httpToken"] print("token:",self.token) # print(code) print(res_post.text) # return self.token # 使用get_nowait()方法,取不到数据,就进入这里 except queue.Empty: print("测试数据已用完,结束!") exit(0) # print("获取的token是:", token) # return token @task(1) def join_room(self): headers = {'Content-Type': 'application/json;charset=UTF-8', 'ptype': '1','token': self.token} url = "http://xx" res_post = self.client.post(url, headers=headers) print("加入房间成功:",res_post.text) print("*" * 50) class WebsiteUser(HttpUser): host = "http://xx" tasks = [UserBehavior] # 创建队列,先进先出 user_data_queue = queue.Queue() # 循环加入队列 for index in range(16000000020, 16000000025): data = '''{ "phoneNumber":"%d" }'''% index user_data_queue.put_nowait(data.replace(' ', '').replace('\n', '')) max_wait = 3000 min_wait = 1000 if __name__ == '__main__': import os os.system("locust -f locustlogin.py")
二、参数化
方式1:循环取数据,数据可重复使用
所有并发虚拟用户共享同一份测试数据,各虚拟用户在数据列表中循环取值。
例如,模拟10个用户并发请求网页,总共有4个URL地址,每个虚拟用户都会依次循环加载这4个URL地址。
from locust import SequentialTaskSet, task, HttpUser,events from gevent._semaphore import Semaphore all_locusts_spawned = Semaphore() all_locusts_spawned.acquire() def on_hatch_complete(**kwargs): all_locusts_spawned.release() # events.on_hatch_complete += on_hatch_complete events.spawning_complete.add_listener(on_hatch_complete) class UserBehavior(SequentialTaskSet): index = 0 # def on_start(self): all_locusts_spawned.wait() @task def test_visit(self): all_locusts_spawned.wait() url = self.user.share_data[self.index] self.index = (self.index + 1) % len(self.user.share_data) with self.client.get(url,catch_response=True) as response: if response.status_code == 200: response.success() else: response.failure("查询失败") print('visit url: %s' % url) class WebsiteUser(HttpUser): host = "https://www.baidu.com" tasks = [UserBehavior] share_data = ['https://www.baidu.com/', 'https://www.baidu.com/s?wd=1', 'https://www.baidu.com/s?wd=2', 'https://www.baidu.com/s?wd=3', 'https://www.baidu.com/s?wd=4'] min_wait = 100 max_wait = 300 if __name__ == '__main__': import os os.system("locust -f parameterized.py")
方式2:保证并发测试数据唯一性,不循环取数据,使用队列的方法。
所有并发虚拟用户共享同一份测试数据,并且保证虚拟用户使用的数据不重复。
例如,模拟10用户并发注册账号,总共有100个账号,要求注册账号不重复,注册完毕后自动结束测试。
from locust import TaskSet, task, HttpUser import queue class UserBehavior(TaskSet): @task def test_register(self): try: data = self.user.user_data_queue.get() except queue.Empty: print('account data run out, test ended.') exit(0) print('register with user: {}, pwd: {}'\ .format(data['username'], data['password'])) payload = { 'username': data['username'], 'password': data['password'] } res = self.client.post('/register', data=payload) print("用户%s:" % data['username'] + res.text) class WebsiteUser(HttpUser): host = 'https://debugtalk.com' tasks = [UserBehavior] user_data_queue = queue.Queue() for index in range(100): data = { "username": "test%04d" % index, "password": "pwd%04d" % index, "email": "test%04d@debugtalk.test" % index, "phone": "186%08d" % index, } user_data_queue.put_nowait(data) min_wait = 100 max_wait = 300 if __name__ == '__main__': import os os.system("locust -f parameterized1.py")
方法3:保证并发数据的唯一性,循环取数据,使用队列方法,每次使用完数据后再放入到队列中。
所有并发虚拟用户共享同一份测试数据,保证并发虚拟用户使用的数据不重复,并且数据可循环重复使用。
例如,模拟10用户并发登录账号,总共有100个账号,要求并发登录账号不相同,但数据可循环使用。
该种场景的实现方式与方法2场景基本相同,唯一的差异在于,每次使用完数据后,需要再将数据放入队列中。
from locust import TaskSet, task, HttpUser import queue class UserBehavior(TaskSet): @task def test_register(self): try: data = self.user.user_data_queue.get() except queue.Empty: print('account data run out, test ended.') exit(0) print('register with user: {}, pwd: {}'\ .format(data['username'], data['password'])) payload = { 'username': data['username'], 'password': data['password'] } self.client.post('/register', data=payload) self.user.user_data_queue.put_nowait(data) class WebsiteUser(HttpUser): host = 'https://debugtalk.com' tasks = [UserBehavior] user_data_queue = queue.Queue() for index in range(100): data = { "username": "test%04d" % index, "password": "pwd%04d" % index, "email": "test%04d@debugtalk.test" % index, "phone": "186%08d" % index, } user_data_queue.put_nowait(data) min_wait = 100 max_wait = 300 if __name__ == '__main__': import os os.system("locust -f paramterized2.py")
三、检查点
假设接口响应状态码正常结果是200,具体响应结果判断可根据实际返回结果自定义,以下示例以状态码判断为例。
locust 在请求方法中设置 catch_response = True
调用 response.success() 或 response.failure() 在结果中标注成功或失败
from locust import SequentialTaskSet, task, HttpUser,events from gevent._semaphore import Semaphore all_locusts_spawned = Semaphore() all_locusts_spawned.acquire() def on_hatch_complete(**kwargs): all_locusts_spawned.release() # events.on_hatch_complete += on_hatch_complete events.spawning_complete.add_listener(on_hatch_complete) class UserBehavior(SequentialTaskSet): index = 0 # def on_start(self): all_locusts_spawned.wait() @task def test_visit(self): all_locusts_spawned.wait() url = self.user.share_data[self.index] self.index = (self.index + 1) % len(self.user.share_data) with self.client.get(url,catch_response=True) as response: if response.status_code == 200: response.success() else: response.failure("查询失败") print('visit url: %s' % url) class WebsiteUser(HttpUser): host = "https://www.baidu.com" tasks = [UserBehavior] share_data = ['https://www.baidu.com/', 'https://www.baidu.com/s?wd=1', 'https://www.baidu.com/s?wd=2', 'https://www.baidu.com/s?wd=3', 'https://www.baidu.com/s?wd=4'] min_wait = 100 max_wait = 300 if __name__ == '__main__': import os os.system("locust -f parameterized.py")
四、集合点
from locust import SequentialTaskSet, task, HttpUser,events from gevent._semaphore import Semaphore all_locusts_spawned = Semaphore() all_locusts_spawned.acquire() def on_hatch_complete(**kwargs): all_locusts_spawned.release() # 创建钩子方法 # events.on_hatch_complete += on_hatch_complete # 挂在到locust钩子函数(所有的Locust示例产生完成时触发) events.spawning_complete.add_listener(on_hatch_complete) class UserBehavior(SequentialTaskSet): index = 0 # 限制在所有用户准备完成前处于等待状态 def on_start(self): all_locusts_spawned.wait() @task def test_visit(self): all_locusts_spawned.wait() # 限制在所有用户准备完成前处于等待状态 url = self.user.share_data[self.index] self.index = (self.index + 1) % len(self.user.share_data) with self.client.get(url,catch_response=True) as response: if response.status_code == 200: response.success() else: response.failure("查询失败") print('visit url: %s' % url) class WebsiteUser(HttpUser): host = "https://www.baidu.com" tasks = [UserBehavior] share_data = ['https://www.baidu.com/', 'https://www.baidu.com/s?wd=1', 'https://www.baidu.com/s?wd=2', 'https://www.baidu.com/s?wd=3', 'https://www.baidu.com/s?wd=4'] min_wait = 100 max_wait = 300 if __name__ == '__main__': import os os.system("locust -f parameterized.py")
浙公网安备 33010602011771号