CucumberJS(BDD)によるREST APIテスト


導入


BDDは非技術的で技術的な人々のための非常に強力なツールです.
この記事では、残りのAPIをテストするために、どのように設定してキュウリを実行する方法を示します.

何が本当にBDDですか?


BDDは行動駆動型開発のための短い
BDDはソフトウェアチームにとって、ビジネスと技術者の間のギャップをクローズする方法です
  • は、役割の向こう側の協同を奨励して、解決される問題の共有理解を構築するために
  • フィードバックを増加するために急速な、小さい反復において、働いている
  • および値
  • のフロー
  • システムの動作に対して自動的にチェックされるシステムドキュメントの作成
    我々は、具体的な、現実世界の例の周りの共同作業を集中することによってこれを行います.これらの例を使って、コンセプトから実装まで導く.

    きゅうりは何ですか。


    キュウリは、行動駆動開発(BDD)をサポートするツールです.キュウリは、プレーンテキストで書かれた実行可能な仕様を読み込み、ソフトウェアがこれらの仕様が何を言うかを検証します.仕様は、複数の例、またはシナリオから成ります.例えば、
    Scenario Outline: create a contact
        Given A contact <request>
        When I send POST request to /directory
        Then I get response code 201
    
    (このシナリオはgherkin文法で書かれています)
    各シナリオは、キュウリの作業のための手順のリストです.キュウリは、ソフトウェアが仕様に準拠していることを検証し、レポートを示す✅ 成功❌ 各シナリオの失敗.

    何がgherkinですか?


    gherkinは、プレーンテキストを十分にキュウリの理解するために構造化された文法規則のセットです.gherkinドキュメントが格納されます.機能のテキストファイルは、通常、ソースのコントロールでは、ソフトウェアと一緒にバージョン管理されます.

    どのようにgherkinのです。あなたのコードに機能ファイル接着剤?


    Gerkinの機能ファイルから各ステップのステップ定義を記述します.ステップ定義はGerkinの手順をプログラミングコードに接続します.ステップ定義は、ステップによって実行されるべき動作を実行する.したがって、ステップ定義は、仕様を実装にハードワイヤーします.

    機能


    特徴は、関連するシナリオのグループです.このように、アプリケーションで多くの関連するテストをテストします.理想的には、gherkinファイルの機能は密接にアプリケーションの機能にマップされます-したがって、名前
    それから、シナリオは特定の方法で注文されます.
    与えられたこれらのステップは、テストを行う前に初期状態を設定するために使用されます
    これらのステップは実行されるべき実際のテストです
    それから、これらのステップはテストの結果をアサートするために使用されます


    私はディレクトリを管理する簡単なREST APIを作成しました.私は連絡先を作成することができます変更、それを読んで、連絡先を削除します.私はBDDテストをすべての機能が設計通りに動作するように書かれている.

    セットアッププロジェクト


    npm init
    

    依存関係をインストールする


     "dependencies": {
        "axios": "^0.20.0",
      },
      "devDependencies": {
        "cucumber": "^6.0.5",
        "cucumber-html-reporter": "^5.2.0"
      }
    

    ディレクトリを作る。src /機能の特徴ファイル


    @directory-service
    Feature: Directory Service
      In order to manage directory
      As a developer
      I want to make sure CRUD operations through REST API works fine
    
      Scenario Outline: create a contact
        Given A contact <request>
        When I send POST request to /directory
        Then I get response code 201
    
        Examples:
          | request                                                                                          
          | {"id":99,"name":"Dwayne Klocko","email":"[email protected]","phoneNumber":"1-876-420-9890"}          |
          | {"id":7,"name":"Ian Weimann DVM","email":"[email protected]","phoneNumber":"(297) 962-1879"} |
    
      Scenario Outline: modify contact
        Given The contact with <id> exist
        When I send PATCH request with a <secondaryPhoneNumber> to /directory
        Then I get response code 200
    
        Examples:
          | id | secondaryPhoneNumber                       |
          | 99 | {"secondaryPhoneNumber": "(914) 249-3519"} |
          | 7  | {"secondaryPhoneNumber": "788.323.7782"}   |
    
      Scenario Outline: get contact
        Given The contact with <id> exist
        When I send GET request to /directory
        Then I receive <response>
    
        Examples:
          | id | response                                      |
          | 99 | {"id":99,"name":"Dwayne Klocko","email":"[email protected]","phoneNumber":"1-876-420-9890","secondaryPhoneNumber": "(914) 249-3519"}         |
          | 7  | {"id":7,"name":"Ian Weimann DVM","email":"[email protected]","phoneNumber":"(297) 962-1879", "secondaryPhoneNumber": "788.323.7782"} |
    
      Scenario Outline: delete contact
        Given The contact with <id> exist
        When I send DELETE request to /directory
        Then I get response code 200
    
        Examples:
          | id |
          | 99 |
          | 7  |
    

    ディレクトリを作る。Src /ステップのJS


    const {Given, When, Then, AfterAll, After} = require('cucumber');
    const assert = require('assert').strict
    const restHelper = require('./../util/restHelper');
    
    Given('A contact {}', function (request) {
        this.context['request'] = JSON.parse(request);
    });
    
    When('I send POST request to {}', async function (path) {
        this.context['response'] = await restHelper.postData(`${process.env.SERVICE_URL}${path}`, this.context['request']);
    })
    
    Then('I get response code {int}', async function (code) {
        assert.equal(this.context['response'].status, code);
    });
    
    When('I send PATCH request with a {} to {}', async function (phoneNumberPayload, path) {
        const response = await restHelper.patchData(`${process.env.SERVICE_URL}${path}/${this.context['id']}`, JSON.parse(phoneNumberPayload));
        this.context['response'] = response;
    })
    
    Given('The contact with {int} exist', async function (id) {
        this.context['id'] = id;
    })
    
    When('I send GET request to {}', async function (path) {
        const response = await restHelper.getData(`${process.env.SERVICE_URL}${path}/${this.context['id']}`);
        this.context['response'] = response;
    })
    
    Then(/^I receive (.*)$/, async function (expectedResponse) {
        assert.deepEqual(this.context['response'].data, JSON.parse(expectedResponse));
    })
    
    When('I send DELETE request to {}', async function (path) {
        const response = await restHelper.deleteData(`${process.env.SERVICE_URL}${path}/${this.context['id']}`);
        this.context['response'] = response;
    })
    
    

    実際の残りの呼び出しを行うサービスを作成する


    任意のHTTPクライアントを使用することができます、私はAxiosを使用します.

    テストを実行し、レポートを生成するには


    npm i
    "./node_modules/.bin/cucumber-js -f json:cucumber.json src/features/ -r src/steps/ --tags '@directory-service'"
    
    このコマンドでは、並行して3つのシナリオを同時に実行します.
    以上です.私は、それがキュウリとgherkinでbddの要点であることを意味します.
    ここではサンプルキュウリレポートです.

    ステップ間のデータの共有


    ほとんどの場合、ステップ間のデータを共有する必要があります.キュウリは、このように、世界として知られているフックと手順にさらされ、各シナリオの孤立したコンテキストを提供します.デフォルトのコンストラクタは以下の通りです:
    function World({ attach, log, parameters }) {
      this.attach = attach
      this.log = log
      this.parameters = parameters
    }
    
    注意:ステップを使用したい場合は、手順で匿名関数を使用してはいけません.
    const {setWorldConstructor} = require("cucumber");
    
    if (!process.env.DIRECTORY_SERVICE_URL) {
        require('dotenv-flow').config();
    }
    
    class CustomWorld {
        constructor({parameters}) {
            this.context = {};
        }
    }
    setWorldConstructor(CustomWorld);
    
    以下はこのデモの間に使った便利なライブラリです.

    . envファイル


    私は環境変数を格納するためにdotenv flow npmを使いました.
    参考:https://github.com/kerimdzhanov/dotenv-flow

    セットアップモックレストAPI


    私はJSONサーバNPMを使用してセットアップモックレストAPIを持っている.
    参考:https://github.com/typicode/json-server
    キュウリのために

    https://github.com/cucumber/cucumber-js ソースコード-


    要約では、BDDはすべてのステークホルダーからのコラボレーションのためのグラウンドを設定します.タグを使用すると、dev、sit、uatのためのBDDのスーツの別のセットを実行することができますもビルドパイプラインをprod.このセットアップは、CI/CDの練習で本当に効果的である可能性があります、それは開発と展開サイクルをスピードアップすることができますが、基本的な品質チェックを適切に維持します.