BDD テスト記述言語リファレンス

ビヘイビア駆動開発 (BDD) とは、DataWeave での処理用に BAT テストを記述するための構文です。BDD とは、Mocha や Jasmine などの他のテストフレームワークに類似した埋め込みドメイン固有言語 (EDSL) です。

次のステートメントが各 BAT テストに必要です。

import * from bat::BDD

このステートメントはドメイン固有言語 (DSL) のテストライブラリ BDD.dwl をインポートします。このライブラリは、describe、it、GET、POST、sleep など、記述時に頻繁に使用する言葉を定義するものです。

このステートメントは mustEqual、mustMatch、every、oneOf、assert などの一般的なマッチャーをインポートします。

次の例は、一般的なテストの記述開始を示しています。

import * from bat::BDD
import * from bat::Assertions
import * from bat::Mutable


var context = HashMap()
---
describe("the login flow") in [
  it("tries a secured endpoint") in [
    GET `http://127.0.0.1:4567/secured_by_token` with {

    } assert [
      $.response.status mustEqual 401,
    ]
  ],
  it("gets the access token") in [
    POST `http://127.0.0.1:4567/get_access_token` with {} assert [
      $.response.body.new_token mustMatch /accessToken_\d+/,
    ] execute [
      context.set('token', $.response.body.new_token)
    ] assert [
      context.get('token') mustMatch /accessToken_\d+/,
      context.get('token') mustEqual $.response.body.new_token,
      $.response.status mustEqual 200,
    ]
  ],
  it("tries to get a secured endpoint") in [
    GET `http://127.0.0.1:4567/secured_by_token/header` with {
      headers: {
        Authorization: context.get('token')
      }
    } assert [
      $.request.headers.Authorization mustEqual context.get('token'),
      $.response.status mustEqual 200
    ]
  ]
]

テストファイルについて

各機能シナリオについて .dwl ファイルを作成します。ファイルはフォルダに基づいて編成することをお勧めします。この編成は、BAT テストが生成するレポートに対応できます。次に例を示します。

tests
├── CoreServices
│   └── Sanity.dwl
└── DesignCenter
    ├── APIDesigner
    │   └── Designer.dwl
    └── MyFolder
        ├── DataSense.dwl
        └── GetProjects.dwl

テストファイルには describe 式が含まれている必要があります。

import * from bat::BDD
---
describe `Demo scenario` in [

]

テストスイートには 1 つ以上のテスト (.dwl) ファイルが含まれている必要があります。

テストブロック

テストブロックにより、テストをより理解しやすくなります。すべてのテストブロックは関数コールまたはカスタム補間値として記述できます。

テストキーワード

次のキーワードおよび次の Fowler の GivenWhenThen 構造をテストで使用します。

関数名 説明 失敗した場合に実行を停止する

describe

グループ化用。レポートのコンテキスト情報を追加するために深くネストできます

いいえ

scenario

describe と同じ

いいえ

suite

describe と同じ

いいえ

given

テストステップ。前処理に使用します

はい

when

テストステップ。通常は副次的影響を作成するために使用します

はい

it

テストステップ。検証

いいえ

should

テストステップ

いいえ

must

テストステップ

はい

it should "str"

テストステップ

いいえ

it must ""

テストステップ

はい

while

テストステップ

いいえ

until

テストステップ

いいえ

テストブロックについて

テストブロックには簡単な名前があり、ブロックが x<name> として記述されるのをスキップします。たとえば、describe のスキップされたバージョンは xdescribe です。

describe("A flow")  in [ ... ] // Describes a sequence of test blocks
xdescribe("A flow") in [ ... ] // The same, but skipped.

describe `A flow` in [ ... ]   // The same as describe, but with a custom interpolation
xdescribe `A flow` in [ ... ]  // The same, but skipped.

カスタム補間値について

カスタム補間値により、関数をコールして、補間値を 2 つのリストに分解できます。

  • 最初のリストには、文字列リテラルがすべて含まれます。

  • 2 つ目のリストには、補間された値が含まれます。

値を文字列に変換する必要はありません。カスタム補間のテキストおよび値はバッククォート (`) で囲みます。

次のブロックのすべての項目をカスタム補間値として使用でき、これらには x<name> バージョンがあります。

import * from bat::BDD         // <-----
import * from bat::Assertions
---
describe `User trades stocks` in [
  scenario `User requests a sell before close of trading` in [
    given `I have 100 shares of MSFT stock` in [
      POST `http://broker/create_stocks` with {
        body: {
          quantity: 100,
          paper: 'MSFT'
        }
      } assert [
        $.response.status == 201
      ]
    ],
    given `I have 150 shares of APPL stock` in [
      POST `http://broker/create_stocks` with {
        body: {
          quantity: 150,
          paper: 'APPL'
        }
      } assert [
        $.response.status == 201
      ]
    ],
    when `I ask to sell 20 shares of MSFT stock` in [
      POST `http://broker/sell_stocks` with {
        body: {
          quantity: 20,
          paper: 'APPL'
        }
      } assert [
        $.response.status == 201
      ]
    ],
    it should "have 80 shares of MSFT stock" in [
      GET `http://broker/get_stocks/MSFT` with {
        headers: {}
      } assert [
        $.response.status == 200,
        $.response.body.quantity == 80
      ]
    ],
    it should "have 150 shares of APPL stock" in [
      GET `http://broker/get_stocks/APPL` with {
        headers: {}
      } assert [
        $.response.status == 200,
        $.response.body.quantity == 150
      ]
    ]
  ]
]

お使いのコーディングスタイルガイドに準拠するために、カスタム補間値を使用せずにブロックを記述することもできます。これによって動作に影響が及ぶことはありません。

import * from bat::BDD         // <-----
import * from bat::Assertions
---
describe("User trades stocks") in [
  scenario("User requests a sell before close of trading") in [
    given("I have 100 shares of MSFT stock") in [
      POST `http://broker/create_stocks` with {
        body: {
          quantity: 100,
          paper: 'MSFT'
        }
      } assert [
        $.response.status == 201
      ]
    ],
    given("I have 150 shares of APPL stock") in [
      POST `http://broker/create_stocks` with {
        body: {
          quantity: 150,
          paper: 'APPL'
        }
      } assert [
        $.response.status == 201
      ]
    ],
    when("I ask to sell 20 shares of MSFT stock") in [
      POST `http://broker/sell_stocks` with {
        body: {
          quantity: 20,
          paper: 'APPL'
        }
      } assert [
        $.response.status == 201
      ]
    ],
    should("have 80 shares of MSFT stock") in [
      GET `http://broker/get_stocks/MSFT` with {
        headers: {}
      } assert [
        $.response.status == 200,
        $.response.body.quantity == 80
      ]
    ],
    should("have 150 shares of APPL stock") in [
      GET `http://broker/get_stocks/APPL` with {
        headers: {}
      } assert [
        $.response.status == 200,
        $.response.body.quantity == 150
      ]
    ]
  ]
]

実行の制御およびアセットの削除の方法

テスト中に失敗した場合、作成済みのアセットをすべて削除する必要があります。たとえば、ユーザがアセットを作成し、検証を実行してから削除するとします。 通常、その検証は失敗し、これによってテストが中断されるため、アセットは削除されず、データベースにはテストデータが蓄積され始めます。一般的に、これによって mustshould に変更することになり、実行を続けることはできますが、 失敗したアセットが残されることはありません。

正しくない例:

6 行目の名前の更新に失敗すると、実行が停止するためプロジェクトは削除されません。

describe `update project names` in [
  it must 'create a project' in [
    createProject()     // OK
  ],
  it must 'update the name' in [
    updateProjectName() // FAILS
  ],
  // Because the previous step is a `must` that failed, execution stops here and the next steps don't execute
  it must 'clean up deleting the project' in [
    deleteProject()     // CANCELLED
  ]
]

これらの予約された言葉を使用してステップを終了するか、実行を続けられるようにします。

  • should は何かが失敗する可能性があることを示しますが、テストに必須ではありません。

  • must はプロジェクトの作成など、何かの実行を続ける必要があることを示します。

正しい例:

describe `update project names` in [
  it must 'create a project' in [
    /**
     * Project creation is a MUST, because in this scenario
     * we depend on the created project to continue.
     */
    createProject()     // OK
  ],
  it should 'update the name' in [
    /**
     * Validations are should because the execution must continue
     * if the validation fails.
     */
    updateProjectName() // FAILS
  ],
  // Because the previous step is a should and it failed, continue executing.
  it must 'clean up deleting the project' in [
    deleteProject()     // OK
  ]
]

選択的なステップの実行方法

結果が失敗の場合、assuming 関数はテストをスキップします。このコマンドは次の構文になります。

[TestBlockExpression] assuming [BooleanExpression] in …​

次に例を示します。

describe `E2E Scenario` in [
  it should 'always do something' in [
    doSomething()
  ],
  it should 'do something else' in [
    doSomethingElse()
  ],
  it should 'sometimes, do something else' assuming (random() > 0.5) in [
    // This is executed randomly, based on          ^^^^^^^^^^^^^^^^ that condition
    doSomethingElse()
  ],
  it should 'do something in dev environments' assuming (config.env == 'DEV') in [
    // This is executed only when                       ^^^^^^^^^^^^^^^^^^^^^ that == true
    doSomethingElse()
  ]
]

このコードをより理解しやすくするために、この関数の 2 つの別名を使用できます。whenwhenNot です。次に例を示します。

describe `E2E Scenario` in [
  it should 'always do something' in [
    doSomething()
  ],
  it must 'do something else' when config.runSanity in [
    doSomethingElse()
  ],
  it should 'do something else' when a == b in [
    doSomethingElse()
  ],
  it should 'do something in dev environments' whenNot config.isSmokeTests in [
    doSomethingElse()
  ]
]

ループ文の実行

while 関数または until 関数は、条件が true または false になる間 (または、それまで) テストを実行します。

while 関数のシグネチャ:

  • while( sentence , condition, time per request, number of retries)

  • do { sentence } while (condition) //→ Default values: 1 second and 3 retries

until 関数のシグネチャ:

  • until( sentence , condition, time per request, number of retries)

  • do { sentence } until (condition) //→ Default values: 1 second and 3 retries

例:

dwl
import * from bat::BDD
import * from bat::Assertions
import * from bat::Types
---
suite("Example for until and while") in [
  it should 'test the while prefix' in [
    while(() -> GET `http://apimon.cloudhub.io/users` with {},
    (x: BATHttpStep) -> x.result.response.status != 200, 10000, 5)
  ],
  it should 'test the while infix' in [
    do {
      () -> GET `http://apimon.cloudhub.io/users` with {} assert [
          $.response.status mustEqual 200
        ]
    } while ($.response.status != 200)
  ],
  it should 'test the until prefix' in [
    until(() -> GET `http://apimon.cloudhub.io/users` with {},
     (x: BATHttpStep) -> x.result.response.status != 200, 10000, 5)
  ],
  it should 'test the until infix' in [
    do {
      () -> GET `http://apimon.cloudhub.io/users` with {} assert [
          $.response.status mustEqual 200
        ]
    } until ($.result.response.status != 200)
  ]
]

Was this article helpful?

💙 Thanks for your feedback!

Edit on GitHub