マルチリモート
WebdriverIOでは、1つのテストで複数の自動セッションを実行できます。これは、複数のユーザーを必要とする機能(例えば、チャットやWebRTCアプリケーション)をテストする際に便利です。
各インスタンスでnewSessionやurlのような共通のコマンドを実行する必要がある複数のリモートインスタンスを作成する代わりに、単純にmultiremoteインスタンスを作成し、すべてのブラウザを同時に制御できます。
そのためには、multiremote()関数を使用し、値にcapabilitiesをキーとする名前付きのオブジェクトを渡します。各機能に名前を付けることで、単一のインスタンスでコマンドを実行するときに、その単一のインスタンスを簡単に選択してアクセスできます。
マルチリモートは、すべてのテストを並行して実行するためのものではありません。特別な統合テスト(例えば、チャットアプリケーション)のために、複数のブラウザやモバイルデバイスを調整するのを支援することを目的としています。
すべてのマルチリモートインスタンスは、結果の配列を返します。最初の結果は機能オブジェクトで最初に定義された機能を表し、2番目の結果は2番目の機能などを表します。
スタンドアロンモードの使用
以下は、スタンドアロンモードでマルチリモートインスタンスを作成する方法の例です。
import { multiremote } from 'webdriverio'
(async () => {
    const browser = await multiremote({
        myChromeBrowser: {
            capabilities: {
                browserName: 'chrome'
            }
        },
        myFirefoxBrowser: {
            capabilities: {
                browserName: 'firefox'
            }
        }
    })
    // open url with both browser at the same time
    await browser.url('http://json.org')
    // call commands at the same time
    const title = await browser.getTitle()
    expect(title).toEqual(['JSON', 'JSON'])
    // click on an element at the same time
    const elem = await browser.$('#someElem')
    await elem.click()
    // only click with one browser (Firefox)
    await elem.getInstance('myFirefoxBrowser').click()
})()
WDIOテストランナーの使用
WDIOテストランナーでマルチリモートを使用するには、wdio.conf.jsのcapabilitiesオブジェクトを(機能のリストではなく)ブラウザ名をキーとするオブジェクトとして定義するだけです。
export const config = {
    // ...
    capabilities: {
        myChromeBrowser: {
            capabilities: {
                browserName: 'chrome'
            }
        },
        myFirefoxBrowser: {
            capabilities: {
                browserName: 'firefox'
            }
        }
    }
    // ...
}
これにより、ChromeとFirefoxで2つのWebDriverセッションが作成されます。ChromeとFirefoxだけでなく、Appiumを使用して2つのモバイルデバイス、または1つのモバイルデバイスと1つのブラウザを起動することもできます。
ブラウザの機能オブジェクトを配列に入れることで、マルチリモートを並行して実行することもできます。各モードを区別する方法であるため、各ブラウザにcapabilitiesフィールドが含まれていることを確認してください。
export const config = {
    // ...
    capabilities: [{
        myChromeBrowser0: {
            capabilities: {
                browserName: 'chrome'
            }
        },
        myFirefoxBrowser0: {
            capabilities: {
                browserName: 'firefox'
            }
        }
    }, {
        myChromeBrowser1: {
            capabilities: {
                browserName: 'chrome'
            }
        },
        myFirefoxBrowser1: {
            capabilities: {
                browserName: 'firefox'
            }
        }
    }]
    // ...
}
クラウドサービスバックエンドの1つを、ローカルのWebdriver/Appium、またはSeleniumスタンドアロンインスタンスと共に起動することもできます。ブラウザの機能でbstack:options(Browserstack)、sauce:options(SauceLabs)、またはtb:options(TestingBot)のいずれかを指定した場合、WebdriverIOはクラウドバックエンド機能を自動的に検出します。
export const config = {
    // ...
    user: process.env.BROWSERSTACK_USERNAME,
    key: process.env.BROWSERSTACK_ACCESS_KEY,
    capabilities: {
        myChromeBrowser: {
            capabilities: {
                browserName: 'chrome'
            }
        },
        myBrowserStackFirefoxBrowser: {
            capabilities: {
                browserName: 'firefox',
                'bstack:options': {
                    // ...
                }
            }
        }
    },
    services: [
        ['browserstack', 'selenium-standalone']
    ],
    // ...
}
あらゆる種類のOS/ブラウザの組み合わせが可能です(モバイルおよびデスクトップブラウザを含む)。テストがbrowser変数を通じて呼び出すすべてのコマンドは、各インスタンスで並行して実行されます。これにより、統合テストが合理化され、実行が高速化されます。
例えば、URLを開くと
browser.url('https://socketio-chat-h9jt.herokuapp.com/')
各コマンドの結果は、ブラウザ名をキーとし、コマンドの結果を値とするオブジェクトになります。以下のようなものです。
// wdio testrunner example
await browser.url('https://www.whatismybrowser.com')
const elem = await $('.string-major')
const result = await elem.getText()
console.log(result[0]) // returns: 'Chrome 40 on Mac OS X (Yosemite)'
console.log(result[1]) // returns: 'Firefox 35 on Mac OS X (Yosemite)'
各コマンドは1つずつ実行されることに注意してください。つまり、すべてのブラウザがそれを実行すると、コマンドは終了します。これは、ブラウザのアクションが同期されるため、現在何が起こっているかを理解するのに役立ちます。
何かをテストするために、各ブラウザで異なることを行う必要がある場合があります。たとえば、チャットアプリケーションをテストする場合、テキストメッセージを送信するブラウザが1つ、受信を待つ別のブラウザが1つ必要であり、受信したテキストメッセージに対してアサーションを実行する必要があります。
WDIOテストランナーを使用すると、グローバルスコープにインスタンスを持つブラウザ名が登録されます。
const myChromeBrowser = browser.getInstance('myChromeBrowser')
await myChromeBrowser.$('#message').setValue('Hi, I am Chrome')
await myChromeBrowser.$('#send').click()
// wait until messages arrive
await $('.messages').waitForExist()
// check if one of the messages contain the Chrome message
assert.true(
    (
        await $$('.messages').map((m) => m.getText())
    ).includes('Hi, I am Chrome')
)
この例では、myChromeBrowserインスタンスが#sendボタンをクリックすると、myFirefoxBrowserインスタンスはメッセージの待機を開始します。
マルチリモートを使用すると、並行して同じことをしたい場合でも、連携して異なることをしたい場合でも、複数のブラウザを簡単かつ便利に制御できます。
browserオブジェクト経由で文字列を使用してブラウザインスタンスにアクセスする
グローバル変数(例えば、myChromeBrowser、myFirefoxBrowser)を介してブラウザインスタンスにアクセスすることに加えて、browserオブジェクト(例えば、browser["myChromeBrowser"]またはbrowser["myFirefoxBrowser"])を介してアクセスすることもできます。browser.instancesを使用して、すべてのインスタンスのリストを取得できます。これは、どちらのブラウザでも実行できる再利用可能なテストステップを作成する場合に特に役立ちます。例えば、
wdio.conf.js
    capabilities: {
        userA: {
            capabilities: {
                browserName: 'chrome'
            }
        },
        userB: {
            capabilities: {
                browserName: 'chrome'
            }
        }
    }
Cucumberファイル
When User A types a message into the chat
ステップ定義ファイル
When(/^User (.) types a message into the chat/, async (userId) => {
    await browser.getInstance(`user${userId}`).$('#message').setValue('Hi, I am Chrome')
    await browser.getInstance(`user${userId}`).$('#send').click()
})
TypeScript型を拡張する
TypeScriptを使用しており、マルチリモートオブジェクトから直接ドライバーインスタンスにアクセスしたい場合は、マルチリモート型を拡張してそれを行うこともできます。例えば、次の機能が与えられたとします。
export const config: WebdriverIO.MultiremoteConfig = {
    // ...
    capabilities: {
        myAppiumDriver: {
            // ...
        },
        myChromeDriver: {
            // ...
        }
    }
    // ...
}
カスタムドライバー名を追加して、マルチリモートインスタンスを拡張できます。例えば、
declare namespace WebdriverIO {
    interface MultiRemoteBrowser {
        myAppiumDriver: WebdriverIO.Browser
        myChromeDriver: WebdriverIO.Browser
    }
}
これで、例えば次のようにしてドライバーに直接アクセスできます。
multiremotebrowser.myAppiumDriver.$$(...)
multiremotebrowser.myChromeDriver.$(...)