メインコンテンツへスキップ

リクエストのモックとスパイ

WebdriverIOには、ネットワーク応答を修正するための組み込みサポートがあり、バックエンドやモックサーバーをセットアップすることなく、フロントエンドアプリケーションのテストに集中できます。テストでREST APIリクエストなどのWebリソースに対するカスタム応答を定義し、動的に変更できます。

情報

mockコマンドを使用するには、Chrome DevToolsプロトコルのサポートが必要であることに注意してください。そのサポートは、Chromiumベースのブラウザでローカルでテストを実行する場合、Selenium Grid v4以上を介して実行する場合、またはChrome DevToolsプロトコルのサポートがあるクラウドベンダー(SauceLabs、BrowserStack、LambdaTestなど)を介して実行する場合に提供されます。Webdriver Bidiに必要なプリミティブが実装され、それぞれのブラウザに実装されると、完全なクロスブラウザサポートが利用可能になります。

モックの作成

応答を変更する前に、まずモックを定義する必要があります。このモックは、リソースURLによって記述され、リクエストメソッドまたはヘッダーでフィルタリングできます。リソースは、minimatchによるglob式をサポートします。

// mock all resources ending with "/users/list"
const userListMock = await browser.mock('**/users/list')

// or you can specify the mock by filtering resources by headers or
// status code, only mock successful requests to json resources
const strictMock = await browser.mock('**', {
// mock all json responses
headers: { 'Content-Type': 'application/json' },
// that were successful
statusCode: 200
})

カスタム応答の指定

モックを定義したら、そのモックに対するカスタム応答を定義できます。これらのカスタム応答は、JSONに応答するオブジェクト、カスタムフィクスチャに応答するローカルファイル、または応答をインターネットからのリソースに置き換えるWebリソースのいずれかになります。

APIリクエストのモック

JSON応答が期待されるAPIリクエストをモックするには、モックオブジェクトでrespondを呼び出し、返したい任意のオブジェクトを指定するだけです。たとえば、

const mock = await browser.mock('https://todo-backend-express-knex.herokuapp.com/')

mock.respond([{
title: 'Injected (non) completed Todo',
order: null,
completed: false
}, {
title: 'Injected completed Todo',
order: null,
completed: true
}], {
headers: {
'Access-Control-Allow-Origin': '*'
},
fetchResponse: false
})

await browser.url('https://todobackend.com/client/index.html?https://todo-backend-express-knex.herokuapp.com/')

await $('#todo-list li').waitForExist()
console.log(await $$('#todo-list li').map(el => el.getText()))
// outputs: "[ 'Injected (non) completed Todo', 'Injected completed Todo' ]"

次の例のように、モック応答パラメーターを渡すことで、応答ヘッダーとステータスコードも変更できます。

mock.respond({ ... }, {
// respond with status code 404
statusCode: 404,
// merge response headers with following headers
headers: { 'x-custom-header': 'foobar' }
})

モックがバックエンドをまったく呼び出さないようにするには、fetchResponseフラグにfalseを渡すことができます。

mock.respond({ ... }, {
// do not call the actual backend
fetchResponse: false
})

カスタム応答はフィクスチャファイルに保存して、次のようにテストでrequireすることをお勧めします。

// requires Node.js v16.14.0 or higher to support JSON import assertions
import responseFixture from './__fixtures__/apiResponse.json' assert { type: 'json' }
mock.respond(responseFixture)

テキストリソースのモック

JavaScript、CSSファイル、またはその他のテキストベースのリソースなどのテキストリソースを変更する場合は、ファイルパスを渡すだけで、WebdriverIOは元のリソースを置き換えます。例:

const scriptMock = await browser.mock('**/script.min.js')
scriptMock.respond('./tests/fixtures/script.js')

// or respond with your custom JS
scriptMock.respond('alert("I am a mocked resource")')

Webリソースのリダイレクト

目的の応答がすでにWebでホストされている場合は、Webリソースを別のWebリソースに置き換えることもできます。これは、個々のページリソースとWebページ自体で機能します。例:

const pageMock = await browser.mock('https://google.com/')
await pageMock.respond('https://webdriverio.dokyumento.jp')
await browser.url('https://google.com')
console.log(await browser.getTitle()) // returns "WebdriverIO · Next-gen browser and mobile automation test framework for Node.js"

動的な応答

モック応答が元のリソース応答に依存している場合は、元の応答をパラメーターとして受け取り、戻り値に基づいてモックを設定する関数を渡すことで、リソースを動的に変更することもできます。例:

const mock = await browser.mock('https://todo-backend-express-knex.herokuapp.com/', {
method: 'get'
})

mock.respond((req) => {
// replace todo content with their list number
return req.body.map((item, i) => ({ ...item, title: i }))
})

await browser.url('https://todobackend.com/client/index.html?https://todo-backend-express-knex.herokuapp.com/')

await $('#todo-list li').waitForExist()
console.log(await $$('#todo-list li label').map((el) => el.getText()))
// returns
// [
// '0', '1', '2', '19', '20',
// '21', '3', '4', '5', '6',
// '7', '8', '9', '10', '11',
// '12', '13', '14', '15', '16',
// '17', '18', '22'
// ]

モックの中止

カスタム応答を返す代わりに、次のHTTPエラーのいずれかでリクエストを中止することもできます。

  • 失敗
  • 中止
  • タイムアウト
  • アクセス拒否
  • 接続が閉じられました
  • 接続がリセットされました
  • 接続が拒否されました
  • 接続が中止されました
  • 接続に失敗しました
  • 名前が解決されませんでした
  • インターネットが切断されました
  • アドレスに到達できません
  • クライアントによってブロックされました
  • 応答によってブロックされました

これは、機能テストに悪影響を与えるページからサードパーティスクリプトをブロックする場合に非常に役立ちます。モックは、abortまたはabortOnceを呼び出すだけで中止できます。例:

const mock = await browser.mock('https://#/**')
mock.abort('Failed')

スパイ

すべてのモックは、ブラウザがそのリソースに対して行ったリクエストの数をカウントするスパイです。モックに応答や中止理由を適用しない場合、通常受け取るデフォルトの応答が継続されます。これにより、ブラウザがリクエストを行った回数(特定のAPIエンドポイントなど)を確認できます。例:

const mock = await browser.mock('**/user', { method: 'post' })
console.log(mock.calls.length) // returns 0

// register user
await $('#username').setValue('randomUser')
await $('password').setValue('password123')
await $('password_repeat').setValue('password123')
await $('button[type="submit"]').click()

// check if API request was made
expect(mock.calls.length).toBe(1)

// assert response
expect(mock.calls[0].body).toEqual({ success: true })

ようこそ!何かお手伝いできることはありますか?

WebdriverIO AI Copilot