0
0

More than 1 year has passed since last update.

Sock ShopのLocustシナリオ書いてみた

Posted at

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であったため,クラスを変更して再度実行してみる.
image.png
GUIにアクセスできたので,対象のSock Shopのアドレスを入力し,実際に負荷試験を実行してみた.
image.png
無事にリクエストを送信できた.しかし,failuresの欄に以下のようなエラーが発生...
image.png
/orderへのPOSTリクエストが送信できていないT_T.
これは商品を注文する動作なので,カートにモノがない状態でこのURLにアクセスしようとするとこのようなエラーが発生してしまう.
また,
理由として考えられるのは,Locustでは複数のユーザでリクエストを送信しているけど,テストシナリオでは1つのユーザしか使っていないため,試験中にユーザを共有してる可能性がある.

orderの対策

orderへのPOSTリクエストがきちんと送信されるために,テストシナリオの最初にユーザを登録する.Sock Shopのユーザ登録画面はこれ
image.png
登録する際には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"
}

これらの情報は異なるユーザで同じものを使用しても大丈夫なためこれで固定した.
image.png
orderでエラーが起きることなくSock Shopの全体に負荷試験を行うことができた.
ただ,RPSが増加するとカートにモノを入れてから注文するまでの期間が短くorderでエラーが出ちゃう.

self.client.post("/cart", json={"id": item_id, "quantity": 1})
time.sleep(3)

このようにカートにモノを入れた後に期間を開けるとエラーが出なくなる.

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0