ふわふわにっき

日々の学習記録などを書いていきます

Vue router + TypeScript環境にて、CypressでE2Eテストを書く ~自作サービスで大変だったところその2~

こんにちは。フィヨルドブートキャンプ(以下FBC)卒業生のふわ(@fuwa-syugyo)です。

先日リリースさせていただいた自作サービスで、大変だったところを振り返るシリーズ第2弾です。

CypressでE2Eテストを書いたので、振り返ろうと思います。(Cypress自体については解説が豊富にありそうなので、ここでは説明を省きます)

第1弾の記事はこちら

環境

  • Cypress 12.11.0
  • TypeScript 4.9.5
  • Vue 3.2.47 (Composition API)
  • Vue-router 4.1.6
  • Vite 4.1.2

リポジトリこちら

Cypress選定の理由

主に以下の理由になります。

  • FBC生の自作サービスで比較的使われていたフレームワークだった
  • 情報が豊富そうな印象だった
  • 試してみて、書きやすそうだと感じた

「Vue E2E test」で検索するとCypressに関するページが多く出てきましたし、Vueとしても推奨しているため問題なさそうと判断しました。

どんなテストを作成したか

主に以下のテストを作成しました。

  • 楽曲or人物の検索結果と情報が正しく表示されているか
  • 楽曲情報画面で、Spotifyへのリンクが正しいか
  • 楽曲検索結果のフィルターが正しく動作するか

MusicBrainz APISpotify Web APIのレスポンスが正しいかどうかが主なテストですね。

モックの使用

上記の通り、主なテストは外部APIのレスポンスが想定通りかという内容です。

テストのたびに外部APIを叩くというのは時間がかかりますし不安定です。特に、検索結果は時間経過で変わっていくので今は良くてもいつかは落ちてしまいます。そのため、テストダブルを使うことにしました。

テストダブルの選定

今回はモックを使うことにしました。入力を考慮する必要はなく、出力が正しいかどうかだけを見れば良かったのでスタブやスパイは使わなくて良いと判断しました。

今までリクエストをモック化したことがなく、イメージを掴むのに少し苦戦しました。こちらの動画で学習しました。

Cypressで外部APIへのリクエストをモック化する

公式ドキュメントを参考に、テストコード内のMusicBrainz APISpotify Web APIへのリクエストをモック化しました。

以下は実際のE2Eテストの一部です。楽曲検索結果が想定通りかというテストです。

describe('Recording search and lookup artist', () => {
  it('Search recording', () => {
    cy.visit('http://127.0.0.1:5173/')
    cy.intercept(
      'GET',
      'https://musicbrainz.org/ws/2/recording/?query=recording:%E9%9D%92%E6%98%A5%E3%82%B3%E3%83%B3%E3%83%97%E3%83%AC%E3%83%83%E3%82%AF%E3%82%B9&offset=0&limit=100&fmt=json',
      { fixture: 'mock_seisyun_page1.json' }
    ).as('seisyun1PageRequest')
    cy.get('input[type="search"]')
      .should('be.visible')
      .type('青春コンプレックス', { force: true })
    cy.get('.search-button').click()
    cy.wait('@seisyun1PageRequest')
    cy.get('.recording-search-table > tbody').contains('青春コンプレックス')
    cy.get('.recording-search-table > tbody').contains('結束バンド')
  })
 cy.get('input[type="search"]')
      .should('be.visible')
      .type('青春コンプレックス', { force: true })
    cy.get('.search-button').click()

で検索フォームに「青春コンプレックス」と入力し、検索するという操作を行なっています。そしてhttps://musicbrainz.org/ws/2/recording/?query=recording:%E9%9D%92%E6%98%A5%E3%82%B3%E3%83%B3%E3%83%97%E3%83%AC%E3%83%83%E3%82%AF%E3%82%B9&offset=0&limit=100&fmt=jsonに遷移します。

「青春コンプレックス」と入力して検索

モック化している部分は以下です。

cy.intercept( 'GET', 'https://musicbrainz.org/ws/2/recording/?query=recording:%E9%9D%92%E6%98%A5%E3%82%B3%E3%83%B3%E3%83%97%E3%83%AC%E3%83%83%E3%82%AF%E3%82%B9&offset=0&limit=100&fmt=json', { fixture: 'mock_seisyun_page1.json' } ).as('seisyun1PageRequest')

  • HTTPリクエストでGETを指定
  • https://musicbrainz.org/~ のアクセスが対象
  • レスポンスはmock_seisyun_page1.json の内容
  • このリクエストの名前はseisyun1PageRequest

という意味です。

cy.wait('@seisyun1PageRequest')と書き、レスポンスが返ってくるまで待ってから、以降のテストに進みます。

これで検索結果の表示が想定通りかというテストが書けました!

実際にAPIを叩いてテストを行なっていたときは時間がかかりすぎてタイムアウトになってしまうことがしばしばあったのですが、モック化してからは爆速でテストが終わるようになって感動しました〜

おまけ Cypress Studioについて

Cypress Studioはブラウザを操作すると、その操作のシナリオを自動的にテストコードに落とし込んでくれます。まだ実験的な機能だそうです。

使ってみての感想ですが、そこまで実用的ではないかなという印象でした。

  • そのままだとエラーが発生してしまい、結局書き直す羽目になった
  • 指定されたセレクタがわかりにくい

セレクタについて、例えばページネーションボタンを押すという操作については自分で書いたものとCypress Studioで書いたもので以下のような違いがありました。後者では子要素の番号で指定してしまい、パッと見てどこを選んでいるのかわかりにくいと思いますね。

自分

cy.get('.pagination').contains('>').click()

Cypress Studio

cy.get(':nth-child(9) > .paginate-buttons').click();

ただし、Cypressにあまり触れたことがない場合どんな書き方をすれば良いのかざっくり掴めますし、どのようなクエリ(getやcontainsといったもの)があるのかを知ることができるので学習目的では結構役に立ったと思います。

まとめ

CypressでE2Eテストを書くにあたって、一番大変だったのはモックの部分でした。初めて触れる部分だったので少々取っ付きにくかったのですが、テストが安定化&高速化したので私のサービスでは必須だったと思います。

Cypress自体はドキュメントが豊富だったためE2Eテストにおいては大きく苦戦することはありませんでした。強いて言うなら比較的新しいツールなのでChatGPT3.5に聞いても参考になる答えが返ってこなかったことくらいでしょうか。ただしそれはE2Eテストについてのみの話です。

次回はCypressでのコンポーネントテストのお話を書こうと思います。