📝

テストコードを書いたことないコーダーがCypressをいじってみた話

2023/01/28に公開

概要

HTML・CSSを主に書いているコーダーが、テスト自動化を体験するためにCypressを使ってみました。

結論

  • 変更が起きにくい状況で保守性の高いテストコードを書けば、テストの手間が減るかもしれない
  • テスト自動化の過程で、テストの客観性が上がりそう

目的:テスト、およびテスト自動化を体験してみる

現在私はQAエンジニアを志望して活動しているのですが、テストコードを書いた経験がありません。テスト自動化のメリットや勘所を知るために、Cypressを使ってみます。

Cypressを選んだ理由:知ってるツールの中ではとっつきやすい気がしたから

テスト自動化ツールというと他にも、

などありますが、今回はCypressを選びました。理由は要素の指定方法がjQueryに似ていて、とっつきやすいと思ったからです。

ちなみにJavaScriptのテストツールの人気度でいうと、2022年時点ではCypressは4位です。

https://2022.stateofjs.com/ja-JP/libraries/testing/

やったこと

今回やったことは以下です。

  1. テスト対象の仕様とコードを用意する
  2. Cypressをインストールする
  3. テストコードを書く
  4. 書いたテストコードを実行する

テスト対象のコードを用意する

input要素とbutton要素を用意して、button要素が押されたときに、以下の条件で処理するようなコードを書きました。

  • input要素が空欄じゃなかったら「入力された文字列:${入力された文字列}」と表示
  • input要素が空欄なら「文字を入力してください」と表示

実際に書いたコードは以下です。

<body>
    <h1>Cypressのテスト</h1>
    <label for="input">文字列</label>
    <input id="input" type="text">
    <button id="button">送信</button>

    <script>
        const createElementWithHTMLString = (HTMLString) => {
            const newDiv = document.createElement('div');
            newDiv.innerHTML = HTMLString;
            return newDiv.firstElementChild;
        }

        const showElement = () => {
            const html = '<p id="text"></p>';
            const targetNewElement = createElementWithHTMLString(html);
            document.body.append(targetNewElement);
        }

        const changeText = () => {
            const text = document.getElementById('input').value;
            let target = document.getElementById('text');

            //p#textが存在するかを判定、なかったらshowElementを呼ぶ
            if (target === null) {
                showElement();
            }

            //targetがない場合にエラーが起きるので、targetを取得し直す
            target = document.getElementById('text');
            target.textContent = (!text) ? '文字を入力してください': `入力された文字:${text}`;
        }

        document.getElementById('button').addEventListener('click', changeText);
    </script>
</body>

Cypressのインストール

前提として、nodeをインストールしておく必要があります。

適当なディレクトリに移動して、以下コマンドでnpmのパッケージをインストールします。

npm init -y
npm install cypress

Cypressを開く

以下のコマンドで、Cypressが立ち上がります。

./node_modules/.bin/cypress open

他にもいくつかのコマンドで立ち上げることができるようです。

https://docs.cypress.io/guides/getting-started/opening-the-app#cypress-open

E2E Testingを選択

選択肢として「E2E Testing」と「Component Testing」が表示されるので、「E2E Testing」を選択します。

何も選択せずに、Continueをクリック

生成されるファイルを選択できますが、全部チェックされた状態で「Continue」をクリックします。

ブラウザを選択

テストしたいブラウザを選択します。すると、Cypressによってブラウザが立ち上げられます。

Create new specを選択

選択肢として「Scaffold example specs」と「Create new spec」が表示されるので、「Create new spec」を選択します。

ちなみにexampleからはCypressが用意したコードをインストールできます。

ファイル名を設定

ファイル名を設定します。選択したファイル(今回の場合、cypress/e2e/spec.cy.js)が自動で作成されます。

テストを実行する

ファイルが生成されると「Okey, run the spec」というボタンが表示されるので、それを押します。そうすると、書かれた内容のテストが実行されます。

テスト用のコードを書く

まずはユーザーの動作を日本語で定義して、それをコードに変換します。

ユーザーがする行動を日本語で書き出してみる

コードを書くために、ユーザーがする行動を日本語で、かつ1ステップで書き出してみます。今回の場合、

  • ユーザーがテキストを入力する場合
  • ユーザーがテキストを入力しない場合

の2パターン書き出してみます。

テキストを入力する場合
  1. 「指定したURL」にアクセスする
  2. inputに文字を入力する
  3. ボタンをクリック
  4. 期待する結果:指定した文字が表示される
テキストを入力しない場合
  1. 「指定したURL」にアクセスする
  2. ボタンをクリック
  3. 期待する結果:指定した文字が表示される

実際に書いたコードは以下です。

describe(
  'inputテスト',
  () => {
    it('inputに値を入力',
      () => {
        //サイトに接続
        cy.visit('指定したURL')
        //文字を入力する
        cy.get('#input').type('Hello, World')
        //ボタンを押す
        cy.get('#button').click()
        //文字の表示を確認
        cy.get('#text').should('have.text',`入力された文字:Hello, World`)
      }
    )

    it('inputに値を入力しない',
      () => {
        //サイトに接続
        cy.visit('指定したURL')
        //ボタンを押す
        cy.get('#button').click()
        //文字の表示を確認
        cy.get('#text').should('have.text',`文字を入力してください`)
      }
    )
  }
)

各メソッドの役割は以下のような感じです。

  • get():対象を取得
  • type()とclick():ユーザーがする操作
  • should():期待する結果

テストする

コード修正を編集すると、自動でテストが実行されます。パスしたテストには、チェックマークが表示されます。

わざと、テストに失敗してみる

「入力(にゅうりょく)された文字:」を「人力(じんりき)された文字:」にしてテストを実行してみます。

    it('inputに値を入力しない', //テストの小項目名?
      () => {
        //実際にCypress、ユーザーがやる動作
        //サイトに接続
        cy.visit('指定したURL')
        //ボタンを押す
        cy.get('#button').click()
        //文字の表示を確認
        cy.get('#text').should('have.text',`文字を入力してください`)
      }
    )

すると以下のように、エラーが指摘されます。

Timed out retrying after 4000ms: expected '<p#text>' to have text '入力された文字:Hello, World', but the text was '人力された文字:Hello, World'

ちなみに該当するテストコードは表示されますが、テストされる側のコードの場所は表示されません。

思ったこと

感想を

  • テスト自動化について思ったこと
  • Cypressについて思ったこと

に分けて書きます。

テスト自動化について思ったこと

テストの客観性が上がりそう

テストコードを書くためには、手動で無意識的にやってる動作を、ツールが理解できるレベルで分解する必要があります。そのプロセスでテスト実装者の主観が排除される、つまり客観性が上がる気がします。

手動だと時間がかかる作業も短時間でやってくれるので楽

例みたいな動作を手動でやると、テストのたびにテキストを入力したりボタンを押したりする必要があります。Cypressだとそれが一瞬で終わるので楽です(実行環境やテストの量にもよると思いますが)。

(しっかりしたテストコードなら)早めに入れておけばおくほど、開発が楽になりそう

リグレッションのチェックを自動でやってくれるので、それにかける労力を別のところに当てられるはずです。もちろん保守性の高いテストコードを書いている場合に限りますが……。

コードの編集が頻繁に発生するものには向かないのかもしれない

Cypressの場合、自動化のためにはコードを書く必要があります。したがってコードや仕様の変更が頻繁に発生する状況だと、結局手動でコードを毎回変える羽目になって自動化のメリットが小さくなると思いました。

Cypressについて思ったこと:jQuery的に使える

公式でも触れられているのですが、要素の取得方法はjQueryに近い気がしました。

//#hogehogeを選択する
//jQuery
$('#hogehoge')
//Cypress
cy.get('#hogehoge')

従ってjQueryを触ったことがある方がテスト自動化を体験してみたいときに、Cypressを使ってみるのはいいと思います。

今後やってみたいこと:コードをわかりやすくする

https://www.jasst.jp/symposium/jasst22tokyo/pdf/B3-1-2.pdf によると、わかりやすいコードを書くコツは以下だそうです。

  1. ユーザー⽬線の表記を⼼がける
  2. あいまいな部分を減らす
  3. 「何をテストしているのか」と「どうテストするのか」を分ける
    引用元: 「60分で学ぶ実践E2Eテスト(テスト実装編)」60ページ( https://www.jasst.jp/symposium/jasst22tokyo/pdf/B3-1-2.pdf )の内容を一部省略

現状だとこれらの点をクリアできていないと思うので、今後やってみたいと思います。

参考資料

https://docs.cypress.io/guides/overview/why-cypress

https://qiita.com/oh_rusty_nail/items/58dcec335d67e81816dd

https://www.jasst.jp/symposium/jasst22tokyo/pdf/B3-1-1.pdf

https://www.jasst.jp/symposium/jasst22tokyo/pdf/B3-1-2.pdf

Discussion