Selenium4のCDP APIでネットワークを色々シミュレートしてみる

みなさんこんにちは、自動テストアーキテクトの森川です。

Selenium4リリースまつりということで、前回に引き続きCDP(Chrome Devloper Protocol)機能を触ってみました。

Selenium4に搭載されたCDPのAPIを叩いてブラウザの位置情報を上書きしてみた|SHIFT Group 技術ブログ|note


ネットワークシュミレート機能

Chrome DevToolsにはネットワークをシュミレートする機能があり、Selenium4のCDPでもこれを扱うことができます。

Inspect network activity - Chrome Developers

Chrome DevTools Protocol - Network domain

サンプルコードはあちらこちらで公開されており、比較的簡単にAPIを叩けます。

出典: Selenium 4 APIs for Chrome DevTools Protocol - Applitools

DevTools devTools = driver.getDevTools();
devTools.createSession();
devTools.send(Network.enable(Optional.empty(), Optional.empty(), Optional.empty()));
devTools.send(Network.emulateNetworkConditions(
        false,  // オフラインですか?
        500000, // ダウンロードスループット
        200000, // アップロードスループット
        50,     // レイテンシー
        Optional.of(ConnectionType.CELLULAR2G)
));

単純なAPIとはいえ、パラメータをいちいち値で指定するのは、面倒なので簡単にインターフェイスを作ってみました。

パラメータの値をJsonに落として

Conditions.json

{
  "conditions":[
    {
      "name": "SLOW",
      "download": 20,
      "upload": 10,
      "rtt": 500
    },
    {
      "name": "Regular 2G",
      "download": 250,
      "upload": 50,
      "rtt": 300
    },
...
    {
      "name": "Regular 4G",
      "download": 4000,
      "upload": 3000,
      "rtt": 20
    },
    {
      "name": "Wifi",
      "download": 30000,
      "upload": 15000,
      "rtt": 2
    }
  ]
}

インターフェイスを書いて

class NetworkEmulator {
    private Map<String, Object> conditionsMap = new HashMap<>();

    public NetworkEmulator(String jsonPath) throws IOException, ParseException {
        parseConditionsJson(jsonPath);
    }

    /**
     * simulate network speed by specify condition name
     * @param devTools DevTool instance
     * @param name conditoin name
     */
    public void emulate(DevTools devTools, String name) {
        devTools.send(Network.enable(Optional.empty(), Optional.empty(), Optional.empty()));
        Map<String, String> condition = (HashMap) conditionsMap.get(name);
        devTools.send(Network.emulateNetworkConditions(
                false,
                Integer.parseInt(condition.get("rtt")),
                Integer.parseInt(condition.get("download")) * 1000,
                Integer.parseInt(condition.get("upload")) * 1000,
                Optional.of(ConnectionType.OTHER)
        ));
    }

    private void parseConditionsJson(String path) throws IOException, ParseException {
        JSONParser parser = new JSONParser();
        JSONObject jsonObject = (JSONObject) parser.parse(new FileReader(path));
        JSONArray conditions = (JSONArray) jsonObject.get("conditions");
        for (JSONObject condition : (Iterable<JSONObject>) conditions) {
            Map<String, String> map = new HashMap<>();
            String name = condition.get("name").toString();
            map.put("name", name);
            map.put("download", condition.get("download").toString());
            map.put("upload", condition.get("upload").toString());
            map.put("rtt", condition.get("rtt").toString());
            conditionsMap.put(name, map);
        }
    }
}

テストを書く(一部抜粋です)

@BeforeAll
void setup() {
    networkEmulator = new NetworkEmulator(JSON_PATH);
    System.setProperty("path/chromedriver.exe");
    driver = new ChromeDriver();
    wait = new WebDriverWait(driver, Duration.ofSeconds(30));
    devTools = ((ChromeDriver) driver).getDevTools();
    devTools.createSession();
    driver.get("about:blank");
}

@ParameterizedTest
@MethodSource("conditions")
@Story("network name:{name}")
void networkSimulationTest(String name) {
    String TEST_URL = "https://www.selenium.dev/";
    networkEmulator.emulate(devTools,name); // おしゃれにさらりと1行で
    Allure.step("access url",() -> {
        driver.get(TEST_URL);
        wait.until(visibilityOfAllElementsLocatedBy(locatorPageBottom));
        });
    });
}

Stream<Arguments> conditions() {
    return Stream.of(
            arguments("SLOW"),
            arguments("GPRS"),
            arguments("Regular 3G"),
            arguments("Wifi")
    );
}

@AfterAll
void tearDown() {
    driver.quit();
}

やってみた

実行結果はこちら

サンプルページだと3GとWifiの違いがそれほど出ていないですが

"SLOW"でしっかり激遅になっていますので妥当なところだと思います。

職場のネット速度がこんなだったら絶叫しそうです。

YouTubeでやってみた

YouTubeは回線パフォーマンスによって配信ビットレート設定を自動的に振り分けてくれますよね(経験則として)

これを利用させていただいて、シミュレートするネットワーク速度によってアプリ側が期待値のビットレートになるか検証してみました。

回線速度によってふるまいが変わるアプリケーションの動作検証というユースケースと思っていただければ幸いです。

■実行結果

回線速度は"SLOW"と"Wifi"の2つでそれぞれのビットレートの期待値を144、720としました。

・SLOW / 期待値 144p

・Wifi / 期待値 720p

期待値は満たしてくれたようです(実行時間が長いのは少し気になりますが)

テストコード(一部抜粋)

@ParameterizedTest
@MethodSource("conditions")
@Story("network name:{name},{expected}")
void youtubeNetworkSimulateTest(String name, String expected) throws IOException {
    String SPEED_TEST_URL = "https://www.youtube.com/watch?v=t705r8ICkRw";
    String result;
    String expectedText = String.format(EXPECTED_TEMPLATE,expected);
    networkEmulator.emulate(devTools,name);
    step("access url",() -> {
        driver.get(SPEED_TEST_URL);
        try {
            wait.until(elementToBeClickable(locatorSkipAd));
            driver.findElement(locatorSkipAd).click();
        } catch (Exception ignored) {
        }
    step("show network config",() -> {
        driver.findElement(config).click();
        wait.until(elementToBeClickable(locatorNetworkConfig));
        result = result + driver.findElement(locatorNetworkConfig).getText();
    });
    Allure.step(result);
    Assert.assertEquals(expectedText,result);
}
         
Stream<Arguments> conditions() {
    return Stream.of(
            arguments("SLOW","144"),
            arguments("Wifi","720")
    );
}

まとめ

インターフェイスさえ書いてやれば、汎用的なスクリプト作成も可能になると思いますし、ブラウザを立ち上げたまま設定だけを変えてグリグリと回せるところもお手軽で良いですね。

ユースケースとしては、ネットワーク速度によるアプリ性能の確認や、ネットワーク性能によってふるまいが変わるアプリの動作確認などがあげられるでしょうか。

設定速度によってはテストの実行に恐ろしく時間がかかるので(当たり前ですが)、テストツール側で設定している待機上限に達してしまう可能性があります。そのあたりはチューニングが必要になるでしょう。

専用ツールを使った非機能テストとは別に、E2Eテストに盛り込んでビルド時にさっと回すというような使い方ができれば十分有用だと感じました。


__________________________________

執筆者プロフィール:森川 知雄
中堅SIerでテスト管理と業務ツール、テスト自動化ツール開発を12年経験。
SHIFTでは、GUIテストの自動化ツールRacine(ラシーヌ)の開発を担当。
GUIテストに限らず、なんでも自動化することを好むが、ルンバが掃除しているところを眺めるのは好まないタイプ。
さまざま案件で自動化、効率化によるお客様への価値創出を日々模索している。2021年からは技術イベントSHIFT EVOLVEの運営を担当。

お問合せはお気軽に
https://service.shiftinc.jp/contact/

SHIFTについて(コーポレートサイト)
https://www.shiftinc.jp/

SHIFTのサービスについて(サービスサイト)
https://service.shiftinc.jp/

SHIFTの導入事例
https://service.shiftinc.jp/case/

お役立ち資料はこちら
https://service.shiftinc.jp/resources/

SHIFTの採用情報はこちら
https://recruit.shiftinc.jp/career/