Cypress とは、ブラウザをクライアントとする web アプリケーションの E2E テスト【End to End Test】を実現するためのテストツールです。
E2E テストに必要なものが網羅されており、テストコードを記述するだけでシステム全体を通した結合テストを容易に実施することができます。
CLI からの実行も可能であり、テストを自動化したい場合にも最適です。
Cypress は Node.js を利用したアプリケーションであり、開発言語は JavaScript、対応ブラウザは chrome、chromium、edge、electron、firefox です。
参考: Webシステムにおける結合テスト
Cypress の公式サイト
https://www.cypress.io/
Cypress のインストール
Cypress は Node.js のパッケージなので、npm でインストールします。
今回は npm のローカルインストールにて例示します。
>cd C:\NodeJS\local # インストール先ディレクトリ
>npm init # パッケージ情報を初期化する場合
>npm install cypress
カレントディレクトリのnode_modules
に実体がコピーされ、package.json
、package-lock.json
ファイルにパッケージ情報が追記されます。
これでインストールは完了です。
参考: Node.js のパッケージ管理ツール npm とは
npmjs.com
https://www.npmjs.com/package/cypress
プロジェクトの作成
プロジェクトとは、テストセットの固まり、いわゆるテストスイートです。
まず、Cypress のインストールディレクトリへ移動します。
npx で Cypress を起動すると、カレントディレクトリにcypress
というディレクトリとcypress.json
というファイルが作成され、コンソール画面が立ち上がります。
>cd c:\NodeJS\local
>npx cypress open
OK をクリックすると、cypress/integration/examples
配下にあるテストプログラムの一覧が表示されます。
テストプログラムの作成
テストコードはcypress/integration
配下に配置します。
このディレクトリは構成ファイル(cypress.json
)の設定integrationFolder
で変更できます。
配置されたテストコードは Cypress のコンソール画面上に現れ、右上の Run all specs をクリックするだけですべてのテストコードを実行できます。
Cypress の言語仕様は、Javascript のテストフレームワークである Mocha の BDD スタイルがベースとなっています。
describe("テストセットタイトル", function(){
it("テストタイトル", function(){
// ここにテスト内容を記述します。
});
before(function(){
// describe() の最初に実行されます。
// 前処理系を記述します。
});
after(function(){
// describe() の最後に実行されます。
// 後処理系を記述します。
});
beforeEach(function(){
// 各 it() の前に実行されます。
// it() 実行のたびに行いたい前処理系を記述します。
});
afterEach(function(){
// 各 it() の後に実行されます。
// it() 実行のたびに行いたい後処理系を記述します。
});
});
describe()
テストセットの宣言です。
テストセットのタイトルとコールバックメソッドが引数になります。describe()
はネスト可能です。
it()
実行するテストコードです。
テストタイトルとコールバックメソッドが引数になり、コールバックの中にテストコードを記述します。
1つのdescribe()
の中に複数のit()
を含めることができます。
before()
describe()
実行時に最初に1回だけ実行されます。
前提条件や前処理を記述します。
after()
describe()
終了時に1回だけ実行されます。
後処理を記述します。
beforeEach()
各it()
の実行時に1回だけ実行されます。
afterEach()
各it()
終了時に1回だけ実行されます。
以下、テストコードの例です。
describe("Test Suite", function(){
it("Test 01", function(){
// ページ移動
cy.visit("https://dev.softwarenote.info/test/test_exp01.php");
// 要素の取得にはjQueryセレクタが使えます
cy.get("#id");
cy.get(".class");
// フォームの操作
cy.get('input[type="text"][name="post_textbox"]').type("cypress.io"); // テキストボックス
cy.get('textarea[name="post_textarea"]').type("cypress.io"); // テキストエリア
cy.get('input[name="post_checkbox"]').check(); // チェックボックス
cy.get('input[name="post_radio"]').check("val2"); // ラジオボタン value="val2"を選択する
cy.get('select[name="post_select"]').select("val2"); // プルダウン value="val2"を選択する
// メソッドチェーンによる実装
cy.get('textarea[name="post_textarea"]')
.type("cypress.io") // 「cypress.io」と入力し
.type("{enter}") // エンター
.type("cypress.io"); // 「cypress.io」と入力
// ラジオボタン、チェックボックス
cy.get('input[type="radio"]').first().check(); // ラジオボタン 最初の項目をチェックする
cy.get('input[type="checkbox"]') // チェックボックス
.each(function($el, index, $list){
if($el.prop("checked")){
$el.prop("checked", false); // チェックをはずす
}else{
$el.prop("checked", true); // チェックする
}
});
// ボタンのクリックなど
cy.get('[type="button"]').click(); // ボタンをクリックする
cy.contains("送信").click(); // 文字列から要素を取得しクリック
cy.get('form[name="form_test"]').submit(); // POSTする
// アサーション(Chai BDD TDD Assertions)
cy.get('[name="post_textbox"]').should("have.value", "cypress.io"); // TDD アサーション
cy.get('[name="post_textbox"]').then(($el) => {
expect($el).to.have.value("cypress.io"); // BDD アサーション
});
cy.get("#result_text").should("have.text", "結果");
cy.get("#result_text").then(($el) => {
expect($el).to.have.text("結果");
});
// その他の操作
cy.screenshot(); // スクリーンショット
cy.reload(); // ページのリロード
cy.log("test log"); // ログ出力
cy.setCookie("ses_id", "dabde"); // クッキーの設定
cy.getCookie("ses_id"); // クッキーの取得
cy.clearCookie("ses_id"); // クッキーの削除
});
});
Cypress – Writing Your First Test
https://docs.cypress.io/guides/getting-started/writing-your-first-test.html
Cypress – Best Practices
https://docs.cypress.io/guides/references/best-practices.html
Cypress – アサーション
https://docs.cypress.io/guides/references/assertions.html
テストプログラムの保守性を高めるために
プロジェクトの保守期間が長くなると、しだいにテストセットは肥大化し、管理が難しくなっていきます。
なるべくテストプログラムも作りっぱなしにせず、共通処理の整理やモジュール化、リファクタリングを行いましょう。
例えばcypress/support/commands.js
には、どのテストプログラムからでも利用できるメソッドを定義することができます。
Cypress.Commands.add("login", function(uid, passwd){
cy.visit("/login");
cy.get('input[name="uid"]').type(uid);
cy.get('input[name="password"]').type(passwd);
cy.get('input[type="submit"]').click();
});
describe("Test1", function(){
it("Commands Test", function(){
cy.login("userId", "passWord");
});
});
class のインポートも可能です。
export default class Login{
login(uid, passwd){
cy.visit("/login")
cy.get('input[name="uid"]').type(uid);
cy.get('input[name="password"]').type(passwd);
cy.get('input[type="submit"]').click();
}
logout(){
cy.visit("/logout")
}
}
import Login from "../classes/Login"
describe("Test2", function(){
it("Import Test", function(){
const lg = new Login();
lg.login("userId", "passWord");
});
});
class の定義もできます。
class Login{
login(){
cy.visit("/login")
cy.get('input[name="uid"]').type("userId");
cy.get('input[name="password"]').type("passWord");
cy.get('input[type="submit"]').click();
}
}
describe("Test3", function(){
it("Class Test", function(){
const lg = new Login();
lg.login();
});
});
サンプルデータは、cypress/fixture
を利用しましょう。
{
"userid": "userId",
"password": "passWord";
}
class Login{
login(){
cy.fixture("profile").then(function(Profiles){
let uid = Profiles.userid;
let passwd = Profiles.password;
}
}
ファイルを直接読み込むこともできます。
describe("Test4", function(){
it("readFile Test", function(){
cy.readFile("profile.json").its("loginid").should("eq", "loginId");
});
});
テストの実行
コマンドラインからの実行は以下の通りです。
>cd c:\NodeJS\local
>npx cypress open # GUIを起動する場合
>npx cypress run # すべてのテストが自動実行されます
>npx cypress run --spec (-s) cypress/integration/test1.js # テストを選択して実行
>npx cypress run -s path/to/*/test*.js # テストの選択にはワイルドカードが使用可能
何も指定しないとcypress/integration
配下のすべてのテストプログラムが実行されます。--spec, -s
オプションで実行するテストプログラムを指定します。
ワイルドカードによって複数のプログラムを自動的に実行できます。
–browser, -b {ブラウザ名}
ブラウザを指定します。chrome
、chromium
、edge
、electron
、firefox
が指定可能です。
デフォルトは chrome です。
–config-file, -C {構成ファイル名}
構成ファイルを読み込みます。
デフォルトはカレントディレクトリにあるcypress.json
を読みに行きます。
–headed
ブラウザを表示してテストを実施します。
–headless
ブラウザが非表示の状態でテストを実施します。
–record
テストの実行結果を記録します。cypress/videos
ディレクトリにブラウザの動作が録画された動画が保存されます。
エラーが発生した場合は、cypress/screenshots
にブラウザのキャプチャ画像が保存されます。
Cypress – コマンドライン
https://docs.cypress.io/guides/guides/command-line.html
構成ファイル
Cypress はテスト実行時にカレントディレクトリにある構成ファイル(cypress.json
)を読み込み、そこに設定された構成値によって動作を制御します。
構成ファイルは、コマンドオプション--config-file
, -C
で切り替えることができます。
以下は主な構成値を記述した構成ファイルの設定例です。
{
"baseUrl": "softwarenote.info", // cy.visit() または cy.request() の URL プレフィックス
"numTestsKeptInMemory": 50, // メモリに保持されるテスト数。使用メモリ量が高い場合はこの数を減らす
"pageLoadTimeout": 60000, // cy.visit() や cy.go()、cy.reload() のタイムアウト値 [ms]
"responseTimeout": 30000, // cy.request() などが応答するまでのタイムアウト値 [ms]
"integrationFolder": "cypress/integration", // テストプログラムの参照場所
"screenshotsFolder": "cypress/screenshots", // cy.screenshot() やテスト失敗時のキャプチャ画像の保存場所
"testFiles": "*/*.*", // 実行するテストファイルのフィルタ
"videoFolder": "cypress/videos", // ビデオの保存場所
"screenshotOnRunFailure": true, // テスト失敗時にキャプチャ画像を撮るかどうか
"video": true, // ビデオを録画するかどうか
"trashAssetsBeforeRuns": true, // cypress run 実行時に保存されている画像やビデオファイルを消去
"userAgent": null, // ユーザエージェントの変更
"viewportWidth": 1000, // ブラウザのビューポートの幅
"viewportHeight": 660 // ブラウザのビューポートの高さ
}
Cypress – 構成オプション
https://docs.cypress.io/guides/references/configuration.html
環境変数
構成ファイルでは、オプションenv
を利用してテストプログラムに設定値を渡すことができます。
{
"env": {
"user_env1": 1,
"user_env2": 2
}
}
Cypress.env("user_env1"); // =1
Cypress.env("user_env2"); // =2
コメント