Sock Shop全体へ負荷をかけるテストシナリオ
import base64
from locust import HttpUser, task
import random
import time
import string
def randomname(n):
randlst = [random.choice(string.ascii_letters + string.digits) for i in range(n)]
return ''.join(randlst)
class WebUser(HttpUser):
@task
def load(self):
headers = {'Content-Type': 'application/json'}
payload = {
"Username": randomname(10), # ユーザネーム
"First name": "tarou", #ファーストネーム
"Last name": "kouka", #ラストネーム
"Email": "abc@abc", # メアド
"Password": "password123" # パスワード
}
payload_address = {
"number": "1404-1",
"street": "katakuramachi",
"city": "hachiouji",
"postcode": "1920982",
"country": "japan"
}
payload_payment = {
"longNum": "1234567890123456",
"expires": "12/24",
"ccv": "123"
}
catalogueList = [
"3395a43e-2d88-40de-b95f-e00e1502085b",
"510a0d7e-8e83-4193-b483-e27e09ddc34d",
"808a2de1-1aaa-4c25-a9b9-6612e8f29a38",
"819e1fbf-8b7e-4f6d-811f-693534916a8b",
"837ab141-399e-4c1f-9abc-bace40296bac",
"a0a4f044-b040-410d-8ead-4de0446aec7e",
"d3588630-ad8e-49df-bbd7-3167f7efb246",
"zzz4f044-b040-410d-8ead-4de0446aec7e"
]
item_id = random.choice(catalogueList)
self.client.get("/")
#self.client.get("/login", headers={"Authorization":"Basic %s" % base64string})
self.client.post("/register", json=payload, headers=headers)
self.client.get("/category.html")
self.client.get("/detail.html?id={}".format(item_id))
self.client.delete("/cart")
self.client.post("/cart", json={"id": item_id, "quantity": 1})
time.sleep(3)
self.client.post("/addresses", json=payload_address, headers=headers)
self.client.post("/cards", json=payload_payment, headers=headers)
self.client.get("/basket.html")
self.client.post("/orders")
公式のテストシナリオが使えないT_T
Sock ShopにLocustで負荷試験を行うために公式のLocustシナリオを使用しようとしたところ,以下のようなエラーが出る.
File "/home/c0a20120/.local/lib/python3.10/site-packages/locust/main.py", line 75, in main
docstring, _user_classes, shape_class = load_locustfile(_locustfile)
File "/home/c0a20120/.local/lib/python3.10/site-packages/locust/util/load_locustfile.py", line 58, in load_locustfile
imported = source.load_module()
File "<frozen importlib._bootstrap_external>", line 548, in _check_name_wrapper
File "<frozen importlib._bootstrap_external>", line 1063, in load_module
File "<frozen importlib._bootstrap_external>", line 888, in load_module
File "<frozen importlib._bootstrap>", line 290, in _load_module_shim
File "<frozen importlib._bootstrap>", line 719, in _load
File "<frozen importlib._bootstrap>", line 688, in _load_unlocked
File "<frozen importlib._bootstrap_external>", line 883, in exec_module
File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
File "/home/c0a20120/locust/locustfile.py", line 27, in <module>
class Web(HttpLocust):
File "/home/c0a20120/.local/lib/python3.10/site-packages/locust/util/deprecation.py", line 27, in __new__
raise ImportError(deprecation_message)
ImportError: The HttpLocust class has been renamed to HttpUser in version 1.0. For more info see: https://docs.locust.io/en/latest/changelog.html#changelog-1-0
エラーの内容によると,バージョン1.0からHttpLocustクラスがHttpUserという名前に変更されていた.現在,私の環境のLocustのバージョンは2.15.1であったため,クラスを変更して再度実行してみる.
GUIにアクセスできたので,対象のSock Shopのアドレスを入力し,実際に負荷試験を実行してみた.
無事にリクエストを送信できた.しかし,failuresの欄に以下のようなエラーが発生...
/orderへのPOSTリクエストが送信できていないT_T.
これは商品を注文する動作なので,カートにモノがない状態でこのURLにアクセスしようとするとこのようなエラーが発生してしまう.
また,
理由として考えられるのは,Locustでは複数のユーザでリクエストを送信しているけど,テストシナリオでは1つのユーザしか使っていないため,試験中にユーザを共有してる可能性がある.
orderの対策
orderへのPOSTリクエストがきちんと送信されるために,テストシナリオの最初にユーザを登録する.Sock Shopのユーザ登録画面はこれ
登録する際にはUsername,First name,Last name,Email,Passwordの5つの要素が必要.
すべて同じ内容で登録しようとしてもエラーが発生してしまうが,Usernameだけ変更すれば別のユーザとして登録することができる.
そのため,locustfile.pyに下記のようなクラスを追加した.
def randomname(n):
randlst = [random.choice(string.ascii_letters + string.digits) for i in range(n)]
return ''.join(randlst)
headers = {'Content-Type': 'application/json'}
payload = {
"Username": randomname(10), # ユーザネーム
"First name": "tarou", #ファーストネーム
"Last name": "kouka", #ラストネーム
"Email": "abc@abc", # メアド
"Password": "password123" # パスワード
}
randomnameクラスにより,Usernameが毎回異なる文字列となるようにした.また,その他の要素は固定している.
テストシナリオ内でログインしている部分の変更を行う.
変更前
self.client.get("/login", headers={"Authorization":"Basic %s" % base64string})
変更後
self.client.post("/register", json=payload, headers=headers)
これで,テストシナリオが実行されるたびに,新たなユーザが作成されるようになった.
でも,ユーザを新たに登録して注文を行う際に,住所とカードの情報が必要.
だから,それぞれの情報をPOSTリクエストで入力していく.
payload_address = {
"number": "1404-1",
"street": "katakuramachi",
"city": "hachiouji",
"postcode": "1920982",
"country": "japan"
}
payload_payment = {
"longNum": "1234567890123456",
"expires": "12/24",
"ccv": "123"
}
これらの情報は異なるユーザで同じものを使用しても大丈夫なためこれで固定した.
orderでエラーが起きることなくSock Shopの全体に負荷試験を行うことができた.
ただ,RPSが増加するとカートにモノを入れてから注文するまでの期間が短くorderでエラーが出ちゃう.
self.client.post("/cart", json={"id": item_id, "quantity": 1})
time.sleep(3)
このようにカートにモノを入れた後に期間を開けるとエラーが出なくなる.