テストケースに適用するFakerの動作を動的に選択する方法はありますか?



Fakerのアクション関数とテストケースの関連付けは、Illusionオブジェクトによって行われます。通常、以下の図のようにIllusionオブジェクトは複数のFakerアクション関数を包含し、テストケースはIllusionオブジェクトを包含することによってテストケース実行時のFakerアクションを適用します。


この場合、規模の大きいプロジェクトで頻繁にfake動作が増減するとIllusionとTestcaseも追従させる必要が生じ、管理が煩雑になります。このような負担を軽減するため、v21.2LTS-patch20211026 以降のリリースから Dynamic Illusion機能 を提供しています。

Dynamic Illusion

Dynamic Illusionは、従来のIllusionが保持するfakers属性にPythonスクリプトを記述する機能です。スクリプトは、引数にTestcase情報が与えられ、それらを元に動的に適用するFakerアクション関数のマップを生成して返却することができます。これによって一つのIllusionに複数のTestcaseを関連付け、Testcase毎に適用するFakerアクションを動的に関連付けることが可能となり、テストと擬似動作の対応付けを一つのIllusionに集約して管理することが出来ます。
Dynamic Illusionによる関連付けのイメージは以下の図のようになります。

サンプルプラグイン

Scenarioプラグイン

SampleScenario

- category: DynamicIllusionTest
  name: SampleScenario
  uri: /v1/dynamicIllusionTests
  method: GET
  connect_timeout: 60
  request_timeout: 60
  commands:
    - command: script
      kwargs:
        code: |-
          faker("SampleFakerA")
          r = await callout(path="/sampleEndpointA")
          if r.error:
              raise Error(r.code)

          faker("SampleFakerB")
          r = await callout(path="/sampleEndpointB")
          if r.error:
              raise Error(r.code)

          context.session.finish()


Fakerプラグイン

SampleFakerA

category: DynamicIllusionTest
name: SampleFakerA
fakes:
  BadRequest:
    script: |-
      async def BadRequest(*args, **kwargs):
          return FakeHttpResponse(code=400, body={"status": False})
  Success:
    script: |-
      async def Success(*args, **kwargs):
          return FakeHttpResponse(body={"status": True})


SampleFakerB

category: DynamicIllusionTest
name: SampleFakerB
fakes:
  BadRequest:
    script: |-
      async def BadRequest(*args, **kwargs):
          return FakeHttpResponse(code=400, body={"status": False})
  Success:
    script: |-
      async def Success(*args, **kwargs):
          return FakeHttpResponse(body={"status": True})


Illusionプラグイン

SampleDynamicIllusion

category: DynamicIllusionTest
name: SampleDynamicIllusion
fakers: |-
  def generateFakers(testcase):
      if testcase.name=="NormalTest":
          return {"SampleFakerA": "Success", "SampleFakerB": "Success"}
      elif testcase.name=="SubnormalATest":
          return {"SampleFakerA": "BadRequest", "SampleFakerB": "Success"}
      elif testcase.name=="SubnormalBTest":
          return {"SampleFakerA": "Success", "SampleFakerB": "BadRequest"}
      else:
          return {"SampleFakerA": "BadRequest", "SampleFakerB": "BadRequest"}


Tip

本サンプルでは、テストケース名で適用するFakerマップを生成していますが、v21.2LTS-patch20211026 以降のリリースからTestcaseに任意のメタデータ辞書(metadata属性)を保持できるようになっているため、Fakerマップ生成に利用することが出来ます。またIllusionに記述するscript関数は非同期関数もサポートされているため、データベースにテストケースとFaker情報のマッピングテーブルを管理しておいて検索してくるという手法を取ることも可能です。


Testcaseプラグイン

NormalTest

category: DynamicIllusionTest
name: NormalTest
target: SampleScenario
type: testcase
illusion: SampleDynamicIllusion
input:
  method: GET
  path: /v1/dynamicIllusionTests
assertion:
  begin: |-
    async def hoge(*args, **kwargs):
        pass
  output: |-
    async def hoge(*args, **kwargs):
        assert Response.code==200, f"Invalid response code {Response.code}"
  progress: []


SubnormalATest

category: DynamicIllusionTest
name: SubnormalATest
target: SampleScenario
type: testcase
illusion: SampleDynamicIllusion
input:
  method: GET
  path: /v1/dynamicIllusionTests
assertion:
  begin: |-
    async def hoge(*args, **kwargs):
        pass
  output: |-
    async def hoge(*args, **kwargs):
        assert Response.code==400, f"Invalid response code {Response.code}"
  progress: []


SubnormalBTest

category: DynamicIllusionTest
name: SubnormalBTest
target: SampleScenario
type: testcase
illusion: SampleDynamicIllusion
input:
  method: GET
  path: /v1/dynamicIllusionTests
assertion:
  begin: |-
    async def hoge(*args, **kwargs):
        pass
  output: |-
    async def hoge(*args, **kwargs):
        assert Response.code==400, f"Invalid response code {Response.code}"
  progress: []


SubnormalAllTest

category: DynamicIllusionTest
name: SubnormalAllTest
target: SampleScenario
type: testcase
illusion: SampleDynamicIllusion
input:
  method: GET
  path: /v1/dynamicIllusionTests
assertion:
  begin: |-
    async def hoge(*args, **kwargs):
        pass
  output: |-
    async def hoge(*args, **kwargs):
        assert Response.code==400, f"Invalid response code {Response.code}"
  progress: []