TypeScript + Cypress + CucumberでE2Eテストをやる


前提

  • 今回はnode.jsのパッケージマネージャーとしてyarnを使っている
  • したがって本稿で使用しているコマンドはyarnを使っているが、使っていない方は各自npmに変換して読み進めてください

Cypressとは

  • Cypress は、テストのセットアップ、作成、実行、デバッグなどをシンプルにするブラウザテストツール
  • Seleniumと似ているがCypressは完全にテスト目的に特化しているのが特徴

目的

今回はCypressのプラグインにあるCucumberを導入して実際に簡単なテストを書いて回してみる

※最短で目的を達成するため細かい説明は多少省きますが(必要だと思った部分は一応リンク貼っておきます)、参考サイトにを挙げておきますのでそちらを確認してみてください

1. インストール

本稿ではルートのディレクトリをsample-e2eとする
初めにsample-e2eディレクトリを作成し、cypressやその依存関係をTypeScriptへトランスパイルのために必要なものをインストール

mkdir sample-e2e
cd e2e
yarn init -y
yarn add cypress webpack @cypress/webpack-preprocessor typescript ts-loader

次にCucumberのプラグインの導入に必要なやつをインストール

yarn add cypress-cucumber-preprocessor @types/cypress-cucumber-preprocessor tsify

2. セットアップ

まずはTypeScriptの設定をするためtsconfig.jsonを作成

/tsconfig.json
{
  "compilerOptions": {
    "strict": true,
    "sourceMap": true,
    "module": "commonjs",
    "target": "es5",
    "lib": [
      "dom",
      "es6"
    ],
    "jsx": "react",
    "experimentalDecorators": true
  },
  "compileOnSave": false
}

次にCypressの最初のdry runを行い、Cypressのフォルダ構造を準備(デフォルトのディレクトリの役割については後述)

yarn cypress open

  • コマンドを実行するとCypress IDEが勝手に開く
  • ウェルカムメッセージが表示されるので、確認したら一旦閉じましょう

プラグインのindex.jsを編集

/cypress/plugins/index.js
const cucumber = require("cypress-cucumber-preprocessor").default;
const browserify = require("@cypress/browserify-preprocessor");

module.exports = (on) => {
  const options = browserify.defaultOptions;

  options.browserifyOptions.plugin.unshift(['tsify']);
  // Or, if you need a custom tsconfig.json for your test files:
  // options.browserifyOptions.plugin.unshift(['tsify', {project: 'path/to/other/tsconfig.json'}]);

  on("file:preprocessor", cucumber(options));
};

Cucumberの書き方でテストを書くstepファイルを置く場所を指定(stepファイルについては後述)

/package.json
"cypress-cucumber-preprocessor": {
    "step_definitions": "cypress/support/step_definitions/"
  }
  • 注意点として説明のしやすさの都合上今回はあえてCucunberのレガシーなスタイルである/cypress/support/step_definitions以下でstepファイルを作成する方法で進めていく
  • 最近はstepファイルを/cypress/integration以下に任意のディレクトリを作成し、その中に置く方法を取っている場合が多い
  • その辺は運用次第なので各々やりやすい方法で進めてください
  • その場合は上記の代わりに以下のようにpackage.jsonを編集
/package.json
// /cypress/support/step_definitions以下でstepファイルを作成していく場合は無視してください
 "cypress-cucumber-preprocessor": {
    "nonGlobalStepDefinitions": true
 }

任意で/package.jsonにいくつかのスクリプトを追加(今回は参考サイトのスクリプトを真似したのでやらなくてもよい)

/package.json
  "scripts": {
    "cypress:open": "cypress open",
    "cypress:run": "cypress run"
  }

3. *.featureの作成

  • Cucumberでは*.featureに仕様等を定義していく
  • 今回はexampleでよくやるGoogle.comのページに行くテストをやってみようと思うので、とりあえずGoogle.featureを作成
/cypress/integration/Google.feature
Feature: Access Google
    Googleの検索ページのテスト

    Scenario: Googleの検索ページにアクセス
        Given google.comにアクセスする
        Then タイトルにGoogleがある
  • Feature、Scenario、諸々の書き方等はCucumber公式を参照
  • とりあえず大事なのはStep(例ではGivenThenだが他にもWhen等がある)でGivenWhenなどで前提の処理を記述しThenで期待する動作を記述する

4. *.step.tsの作成

  • Cucumberではstepファイルで*.featureで定義した内容に対応したテストを実装する
  • 今回はstepファイルを/cypress/support/step_definitions以下にaccessingGoogle.step.tsを作成しそこでCypressを使いつつテストを実装していく
/cypress/support/step_definitions/accessingGoogle.step.ts
/// <reference types="cypress"/>

import {Given, Then} from "cypress-cucumber-preprocessor/steps";

Given(`google.comにアクセスする`, () => {
    cy.visit("https://google.com")
});

Then(`タイトルにGoogleがある`, () => {
    cy.title().should("include", "Google")
});

注意点として以下の2点がある

  • TypeScriptでやる場合、ファイルの先頭に/// <reference types="cypress"/>を記述する
  • 各Stepの第一引数の文字列は*.featureと一致させる必要がある

2点目についてはよく正規表現を使うことが多いが今回は必要ないと思ったためやっていない

Cypress Apiについて

  • stepファイルの例でしれっとcy.visit(url)等が出てきているが、他にも色々なメソッドが用意されている
  • どのようなメソッドがあるのかはCypress公式が全てなので各自確認するといいと思います
  • とりあえず大事なのはcy.should(arg)で、これは明示的なアサーションに使う
  • この引数にはアサーションツールの「Chai」をベースにテストでアサーションを行う
  • cy.should(arg)についてもCypress公式のshouldのページに書いてあるので確認してみてください

5. テストの実行

手っ取り早くテストの実行の方法は以下の2つのやり方がある

  • 実際にブラウザが起動されてStepで実装した挙動をして、テストする
  • コマンドラインで任意のコマンドによってテストする

ローカルでテストする場合は1つ目の方法でテストし、CI上でテストする等ブラウザを使わずにテストする必要がある場合は2つ目の方法でテストする
他にもタグをつけて細かくテスト対象を変えることなどもできるので色々調べてみてください

1つ目の方法でテストする場合は以下のコマンドでウィンドウが開く(今回はscriptを用意したのでそれを使用)

yarn cypress:open

ウィンドウが開いたらGoogle.featureをクリックするとテストが走ります

2つ目の方法で実行する場合は

yarn cypress:run

ちなみにコマンドで動かした場合/cypress/videos以下にビデオが追加され、ブラウザでの挙動がビデオで確認できる
必要ない場合は設定で追加しないようにできる

参考. ディレクトリについて

最初のdry runをしてウィンドウを閉じたらデフォルトで以下のようにディレクトリが作成される

ディレクトリ構成
e2e
 └─cypress
      ├─fixtures
      │    └─examples
      ├─integration
      │    └─examples
      ├─plugins
      └─support

とりあえず大事なのはcypress/fixturecypress/integration

  • cypress/fixture : *.jsonファイルに複数のテストで使用するサンプルデータを用意しておくことができる
  • cypress/integration : 実際にテストを記述するディレクトリ
  • examplesはただの例なので安全に削除可能

fixturesについて

例 fixture/foo.json
{
    "name": "foo"
}

このように用意しておくと次のようにするとテストで呼び出せる

// テストの中で
cy.fixture('foo').then((fooJson) => {
    ...
})
  • 文法的にはcy.fixture(filePath)filePathはデフォルトでcypress/fixture
  • fooJsonfoo.jsonのデータが入る

呼び出しの方法は他にもありますが、詳細は省きます

参考にしたサイト

https://www.cypress.io/
https://docs.cypress.io/api/api/table-of-contents.html
https://github.com/TheBrainFamily/cypress-cucumber-preprocessor
https://typescript-jp.gitbook.io/deep-dive/intro-1/cypress
https://www.kaizenprogrammer.com/entry/2017/12/23/144242