見出し画像

mswを使ってフロントエンド開発の生産性を上げていこう

こんにちは!スペースマーケットのフロントエンドエンジニアの和山です。

先日うちのわんたろう(雑種・♂)が1歳になりました!
近頃はペットOKの焼肉店というところがありまして、そちらで一緒にお肉を食べながらお祝いしたり、おうちでおいしいちゅーるタワーを食べたりと贅沢しております。
子供の成長はあっという間と言いますが、犬の成長もあっという間でした。
この1年たくさんいろいろな経験ができたと思うので、今年も一緒におでかけしようと思います。

ということで、1年のうちすでに4分の1が過ぎ去りました。
時が経つのが早い!!!
四半期が終わったということで、新年に立てた目標の進捗はいかがでしょうか?

僕は四半期の目標に「フロントエンドのテストカバレッジ向上」という目標がありました。
その中でmswの導入に関わることができたので、mswを使う利点だったり、利用方法についてご紹介させていただこうと思います。

mswって何?

そもそもmswとは何かの説明をしていないため、mswについてご紹介します。
公式サイトは以下です。

簡潔にいうと、ブラウザ側からのリクエストをインターセプトして任意のレスポンスを返却できるようにするものです。
これの何がいいかというと特にテストを書いたことがある方はご経験があるかと思いますが、以下のように考えていただけると良いかと思います。

例:fetch関数の戻り値をモック化したレスポンスにする場合
・fetchそのものをモック化する場合
 - fetch関数の内部構造が無視される
・mswを利用して通信をモック化する場合
 - fetch関数の内部構造はそのまま

つまり、内部的な作りについては一切手を加えることがなくAPIのレスポンスのみを変更するような形になります。
その為、fetch関数の仕組みはそのまま使えるのでかなり自然な形でモック化することができます。

mswはブラウザ+node.js両方で動作することが大きな利点です。
これは通常の開発時、jestでのテストコードの実装時、Storybookなど多くの場面で利用できることを意味しています。
現在スペースマーケットでは主にテストでの利用をしています。

mswの使い方

簡単にmswの使い方をご紹介します。

yarn add -D msw

などでライブラリを追加してください。

それでは、インターセプトする際のハンドラーの処理を実装します。
インターセプトできる通信はREST または GraphQLです。

まずはREST APIの場合の書き方です。

import { rest } from 'msw'

// 配列型式で定義する
export const restHandlers = [
  // 一覧の場合
  rest.get('/items', (res, req, ctx) => {
    return res(
      ctx.status(200),
      ctx.json({ items: [
        { id: 1, name: 'りんご' },
        { id: 2, name: 'みかん' },
        { id: 3, name: 'バナナ' }
      ]})
    )
  })
  // :idで個別取得の場合
  rest.get('/item/:id', (res, req, ctx) => {
    return res(
      ctx.status(200),
      ctx.json({ id: 1, name: 'りんご', price: 100})
    )
  })
  rest.post('/item/:id', (res, req, ctx) => {
    return res(
      ctx.status(200),
      ctx.json({ message: 'success!' })
    )
  })
] 

例に記載している通り動的パスの場合は動的部分を:hogeと記載すれば動的なパスを設定可能です。
また、今回は特に記載していませんが、コールバックのreqにリクエストの情報が入ってくるので、ここの値によってレスポンスを変更することも可能です。

次にGraphQL APIの場合です

import { graphql } from 'msw'

// restと同様に配列型式で指定する
export const graphqlHandlers = [
  // 一覧の場合
  graphql.query('GetItems', (res, req, ctx) => {
    return res(
      ctx.data({ items: [
        { id: 1, name: 'りんご' },
        { id: 2, name: 'みかん' },
        { id: 3, name: 'バナナ' }
      ]})
    )
  })
  // 個別取得の場合
  graphql.query('GetItem', (res, req, ctx) => {
    return res(
      ctx.data({ id: 1, name: 'りんご', price: 100})
    )
  })
  graphql.mutation('AddItem', (res, req, ctx) => {
    return res(
      ctx.data({ message: 'success!' })
    )
  })
] 

書き方が少し違うだけでほとんど一緒です。

今回は特に異常系を想定していないのですが、エラーレスポンスを設定することも可能です。
かなり簡単に、そして直感的にモックが書けます。

それでは次にこのモックのレスポンスが動作するようにします。

モックを実行する

実行はかなり簡単です。
jestでテストコードを実装する場合はブラウザ上での動作はしないので、node.jsの実行手順に沿って記載します。

import { setupServer } from 'msw/node'
import { restHandlers, graphqlHandlers } from './handlers'

const server = setupServer(...[...restHandlers, ...graphqlHandlers])

// 起動
beforeAll(() => server.listen())
// リセット
afterEach(() => server.resetHandlers())
// 終了
afterAll(() => server.close())

describe('Hogeのテスト', () => {
  test('APIから一覧を取得し表示されることを確認する', () => {
    // テストを記載
  })
})
 

たったこれだけの手順でAPIをモックできるようになります。
あとは普段書いているようにテストコードを実装していくだけです。
jest.mockなどでモック化しないのでコード量も減り、かなり楽にテストが書けるようになったと思います。

今回別ファイルでハンドラーを用意しましたが以下の様な書き方でも動作します。

const server = setupServer(
  rest.get('/item/:id', (res, req, ctx) => {
    return res(
      ctx.status(200),
      ctx.json({ id: 1, name: 'りんご', price: 100})
    )
  })
)

またこちらブラウザで実行したい場合も同じ様にかなり簡単な記述で使えます。
ユースケースとしては通常の開発だったり、Storybookでの開発時に利用可能です。

import { setupWorker } from 'msw'
import { restHandlers, graphqlHandlers } from './handlers'

const worker = setupWorker(...handlers)
// 開始
worker.start()

// windowにストップメソッドを用意しておき
window.__mswStop = worker.stop
// コールすることで停止可能に
window.__mswStop()

停止の際にwindowにstopを用意しているのは、コンソールから楽に呼び出せる様にできるためです。
例えば特定のボタンを押したら終了したい場合などはそのまま直接worker.stop()を呼び出せば停止可能なので、使いたいユースケースに合わせて設定すると良いかと思います。

また公式サイトではenvの値をみて、開発モードでの起動の場合のみworker.start()を実行するかしないかを設定していたので、どういう時にモックを使うかなどはプロジェクトによってしっかり定めておく良いと思います。

まとめ

mswを使うことによりテストコードを新たに追加できるのはもちろんですが、バックエンドの開発を待たずにフロントエンド開発が進められるというのもまた大きな利点です。
APIの設計さえ決まっていればフロントエンド側で独自のモックを用意し機能開発ができる、そして開発が進み開発としてモックを使わなくなれば今度はテストとして同じモックが再利用できるというサイクルが作れます。
フロントエンドの開発側をさらに上げることのできるライブラリなので是非使っていただければと思います。

話は変わりますが、スペースマーケットではバックエンドエンジニアを募集中です!
気になる方は是非以下のリンクからチェックしていただけると嬉しいです。

最近「九龍ジェネリックロマンス」という漫画にどハマりしました。
コミックス1巻のラストで完全に引き込まれたと言っても過言ではありません。
この漫画の中に登場するキャラクターがこんなことを言います。

「懐かしいという感情は恋と同じ」

ということで弊社にも「懐かしいと思える、恋することができるスペースがあるはず!」ということで懐かしさを感じられるスペースを集めてみました!

このリストのスペースの他にもたくさんのスペースを弊社では掲載しております。
皆さんの懐かしいを感じられる、恋することができるスペースを見つけて、そして是非使ってみてみてください!


この記事が気に入ったらサポートをしてみませんか?