PuPHPeteerでE2Eテスト(3) Jpostalで郵便番号から住所を自動入力

前回Google検索で作ったクラスを利用して、Jpostalで郵便番号から住所を自動入力するテストプログラムを作ります。

PuPHPeteerとは

WebアプリケーションのE2Eテストフレームワークの一つに、Puppeteer(パペッティア)があります。Puppeteerはブラウザを起動し、ブラウザを操作してE2Eテストをします。

PuPHPeteerは、PHPからPuppeteerを使うためのライブラリです。

前回

npm installやcomposer init、Google検索するテストプログラムを作りました。

郵便番号から住所を自動入力する

Jpostalをテストするテストプログラムを作ります。Jpostalは郵便番号から住所を自動入力するjqueryプラグインです。Jpostalは弊社が公開しています。

J-POSTAL

まず、手で操作します。

  • 住所入力フォーム https://jpostal-1006.appspot.com/sample_1.html を表示します。
  • 郵便番号の最初の3桁に「100」を入力します。
  • 都道府県が「東京都」になるはずです。
  • 市区町村が「千代田区」になるはずです。
  • 郵便番号の次の4桁に「0001」を入力します。
  • 町域が「千代田」になるはずです。

最初のテスト

まず、必ず合格する単純なテストを書いて、テストが動くことを確認します。

study_puphpeteer
├── composer.json
├── node_modules
├── package.json
├── phpunit.sh
├── phpunit.xml
├── tests
│   ├── BasePuppeteerTestCase.php
│   ├── GoogleSearchTest.php
│   └── Jpostal100Test.php   // ★コレ
└── vendor
Code language: plaintext (plaintext)

tests/Jpostal100Test.php

tests/Jpostal100Test.phpを新規作成します。必ず合格する単純なテストを書きます。

<?php

namespace Ninton\Test;

use PHPUnit\Framework\TestCase;

class Jpostal100Test extends TestCase
{
    public function testSmoke(): void
    {
        $this->assertTrue(true);
    }
}Code language: PHP (php)

テスト実行

ターミナルでテスト実行します。最後にグリーンで「OK (1 test, 1 assertion)」と表示されたら、テスト合格です!

$ ./phpunit.sh tests/Jpostal100Test.php 
+ ./vendor/bin/phpunit --configuration=phpunit.xml tests/Jpostal100Test.php
PHPUnit 8.5.14 by Sebastian Bergmann and contributors.

.                                                                   1 / 1 (100%)

Time: 121 ms, Memory: 4.00 MB

OK (1 test, 1 assertion)Code language: Bash (bash)

tests/BasePuppeteerTestCase.php

前回、共通部分をリファクタして、tests/BasePuppeteerTestCase.phpを作りました。このクラスの$pageプロパティ、スクリーンショットを保存するscreenShotoメソッド、次のページ表示を待つwaitForPageLoadメソッドを使います。

study_puphpeteer/tests/BasePuppeteerTestCase.php at main · ninton/study_puphpeteer
study PuPHPeteer. Contribute to ninton/study_puphpeteer development by creating an account on GitHub.
プロパティ、メソッド説明
$pagePuPHPeteerのPageオブジェクト
setUp()ブラウザを起動します
tearDown()ブラウザを終了します
waitForPageLoad()次のページの表示を待ちます
screenShot()スクリーンショットを保存します

tests/Jpostal100Test.phpを編集します。BasePuppeteerTestCaseをextendsします。

<?php

namespace Ninton\Test;

use PHPUnit\Framework\TestCase;

class Jpostal100Test extends BasePuppeteerTestCase
{
    public function testSmoke(): void
    {
        $this->assertTrue(true);
    }
}Code language: PHP (php)

テスト実行

この段階で、ターミナルでテスト実行します。数秒かかってブラウザが起動し、すぐに閉じるはずです。

最後にグリーンで「OK (1 test, 1 assertion)」と表示されたら、テスト合格です!

$ ./phpunit.sh tests/Jpostal100Test.php 
+ ./vendor/bin/phpunit --configuration=phpunit.xml tests/Jpostal100Test.php
PHPUnit 8.5.14 by Sebastian Bergmann and contributors.

.                                                                   1 / 1 (100%)

Time: 5.03 seconds, Memory: 6.00 MB

OK (1 test, 1 assertion)Code language: Bash (bash)

郵便番号から住所を自動入力する

testSmokeメソッドは削除します。コメントで残しておいてもかまいません。

新しく、次のメソッドを追加します。

    public function test_1000001は東京都千代田区千代田(): void
    {
    }Code language: PHP (php)

手で操作した手順にしたがって、プログラムを書いていきます。

  • 住所入力フォーム https://jpostal-1006.appspot.com/sample_1.html を表示します。
  • 郵便番号の上3桁に「100」を入力します。
  • 都道府県が「東京都」になるはずです。
  • 市区町村が「千代田区」になるはずです。
  • 郵便番号の下4桁に「0001」を入力します。
  • 町域が「千代田」になるはずです。

住所入力フォーム https://jpostal-1006.appspot.com/sample_1.html を表示します。

Pageクラスのgotoメソッドを使います。

        $this->page->goto('https://jpostal-1006.appspot.com/sample_1.html');
Code language: PHP (php)

郵便番号の上3桁に「100」を入力します。

Pageクラスのtypeメソッドを使います。

上3桁のINPUTタグのHTMLソースを見ると、

<input id="postcode1" name="postcode1" maxlength="3">Code language: HTML, XML (xml)

なので、DOM idは、"postcode1"です。CSSセレクターに '#postcode1' を指定します。

Jpostalは上3桁を入力したときに、ajaxでデータをダウンロードして、住所欄に自動入力します。多くの場合、0.5秒もかかりませんが、ねんのため2秒待つことにしました。

        $this->page->type('#postcode1', '100');
        sleep(2);
        $this->screenShot();
Code language: PHP (php)

ここで、テストを実行してみます。

スクリーンショットを見ると、"100"が自動入力されて、東京都、千代田区になるはずですが...なりましたね!

3桁を入力した時点で、Ajaxでデータをダウンロードしているんだね

仮に、onblur(フォーカスが離れた)イベントで、自動入力していたとするね、

としたら、3桁を入力した時点では、onblurイベントは発生しないよね?どうすればいいのかな?

タブ「\t」をtypeしてみたけど、次の入力欄にフォーカス移動しなかったわ

別の入力欄をクリックしたら、onblurイベントが発生しわ

あとね、決め打ちで2秒待つより、Ajax通信を待ったほうがスマートだよね?

本家のPuppeteerでは、await Promise.allでpage.typeとpage.waitForResponseを待つみたいだけど、同じことをPHPやPuPHPeteerでどうやるのかわからなかったわ

都道府県が「東京都」になるはずです。

PageクラスのquerySelectorメソッドや、ElementHandleのgetPropertyメソッドを使って、value属性を取得します。

都道府県のSELECTタグのHTMLソースを見ると、

<span>都道府県</span>
<select id="address1" name="address1">Code language: HTML, XML (xml)

なので、DOM idは、"address1"です。CSSセレクターに '#address1' を指定します。

        $el = $this->page->querySelector('#address1');
        $value = $el->getProperty('value')->jsonValue();
        $this->assertEquals('東京都', $value);
Code language: PHP (php)

市区町村が「千代田区」になるはずです。

PageクラスのquerySelectorメソッドや、ElementHandleのgetPropertyメソッドを使って、value属性を取得します。

市区町村のINPUTタグのHTMLソースを見ると、

<span>市区町村</span>
<input id="address2" name="address2">Code language: HTML, XML (xml)

なので、DOM idは、"address2"です。CSSセレクターに '#address2' を指定します。

        $el = $this->page->querySelector('#address2');
        $value = $el->getProperty('value')->jsonValue();
        $this->assertEquals('千代田区', $value);
Code language: PHP (php)

郵便番号の下4桁に「0001」を入力します。

Pageクラスのtypeメソッドを使います。

下4桁のINPUTタグのHTMLソースを見ると、

<input id="postcode2" name="postcode2" maxlength="4">Code language: HTML, XML (xml)

なので、DOM idは、"postcode2"です。CSSセレクターに '#postcode2' です。

上3桁下4桁を入力した時点で、ダウンロード済みのデータから検索して、再度、住所欄に自動入力します。すでにデータはダウンロード済みなので、0.1秒も待てば充分です。

        $this->page->type('#postcode2', '0001');
        usleep(100000);
        $this->screenShot();
Code language: PHP (php)

ブラウザ上のJavaScript処理は0.1秒待てば充分ってこと?

Jpostalは0.1秒でもいいけど、

テスト対象のページのJavaScript処理によって調整してね

町域が「千代田」になるはずです。

PageクラスのquerySelectorメソッドや、ElementHandleのgetPropertyメソッドを使って、value属性を取得します。

町域のINPUTタグのHTMLソースを見ると、

<span>町域</span>
<input id="address3" name="address3">Code language: HTML, XML (xml)

なので、DOM idは、"address3"です。CSSセレクターに '#address3' を指定します。

        $el = $this->page->querySelector('#address3');
        $value = $el->getProperty('value')->jsonValue();
        $this->assertEquals('千代田区', $value);
Code language: PHP (php)

すでに入力欄に表示されているとき

さきほどのテストに続けて、下4桁に"0002"を入力して、町域が「皇居外苑」になることをテストしてみましょう。

        $this->page->type('#postcode2', '0002');
        usleep(100000);
        $this->screenShot();

        $el = $this->page->querySelector('#address3');
        $value = $el->getProperty('value')->jsonValue();
        $this->assertEquals('皇居外苑', $value);
Code language: PHP (php)

テストを実行すると、失敗してしまいました。

$ ./phpunit.sh tests/Jpostal100Test.php 
+ ./vendor/bin/phpunit --configuration=phpunit.xml tests/Jpostal100Test.php
PHPUnit 8.5.14 by Sebastian Bergmann and contributors.

F                                                                   1 / 1 (100%)

Time: 10.24 seconds, Memory: 6.00 MB

There was 1 failure:

1) Ninton\Test\Jpostal100Test::test_1000001は東京都千代田区千代田
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'皇居外苑'
+'千代田'

/home/aoki/projects/personals/study/study_puphpeteer/tests/Jpostal100Test.php:39

FAILURES!
Code language: Bash (bash)

メソッド終了前に sleep(5); などを入れて、ブラウザを見て確認したり、スクリーンショットを確認します。

下4桁が「0001」のままですね。

Pageクラスのtypeメソッドは、すでに入力済みの内容をクリアせずに「追記」します。

手でキーボードから入力するとき、すでに入力済みの内容があるときは、BackSpaceキーやDeleteキーで削除します。

Puppeteerの場合も、すでに入力済みの内容があるときは、クリアしてからタイプします。

ASCIコードの0x08をtypeしてみたけど、削除できなかったわ

残念ながら、Puppeteerには、入力欄をクリアするAPIは用意されていません。開発者の提案する方法は、次のJavaScriptコードです。

await page.$eval(selector, element => element.value = '');

// または

await page.evaluate(selector => document.querySelector(selector).value = '', selector);Code language: JavaScript (javascript)

このコードをPuPHPeteer用に置き換えて、BasePuppeteerTestCase.phpに追加します。

メソッド説明
pageType(string $selector, string $text)入力欄をクリアしてから、入力する
pageTypeClear(string $selector)入力欄をクリアする
use Nesk\Rialto\Data\JsFunction;

class BasePuppeteerTestCase extends TestCase
{
...

    /**
     * @param string $selector
     */
    protected function pageTypeClear(string $selector): void
    {
        $jsFunc = JsFunction::createWithBody("document.querySelector('${selector}').value = '';");
        $this->page->evaluate($jsFunc);
        usleep(100000);
    }

    /**
     * @param string $selector
     * @param string $text
     */
    protected function pageType(string $selector, string $text): void
    {
        $this->pageTypeClear($selector);
        $this->page->type($selector, $text);
    }
}
Code language: PHP (php)

この pageTypeメソッドを使って、"0002"を入力します。

        $this->pageType('#postcode2', '0002');
        usleep(100000);
        $this->screenShot();

        $el = $this->page->querySelector('#address3');
        $value = $el->getProperty('value')->jsonValue();
        $this->assertEquals('皇居外苑', $value);

Code language: PHP (php)

テストを実行すると、無事テストが通りました。

$ ./phpunit.sh tests/Jpostal100Test.php 
+ ./vendor/bin/phpunit --configuration=phpunit.xml tests/Jpostal100Test.php
PHPUnit 8.5.14 by Sebastian Bergmann and contributors.

.                                                                   1 / 1 (100%)

Time: 14.18 seconds, Memory: 6.00 MB

OK (1 test, 4 assertions)
Code language: Bash (bash)
feat: add page.clear method by yanivefraim · Pull Request #3180 · puppeteer/puppeteer
I wasn't sure about implementation (if to use setSelection + backspace or just to clear the value). Setting element.valu...

今回のまとめ

Jpostal100Test.php

study_puphpeteer/tests/Jpostal100Test.php at main · ninton/study_puphpeteer
study PuPHPeteer. Contribute to ninton/study_puphpeteer development by creating an account on GitHub.

BasePuppeteerTestCase.php

study_puphpeteer/tests/BasePuppeteerTestCase.php at main · ninton/study_puphpeteer
study PuPHPeteer. Contribute to ninton/study_puphpeteer development by creating an account on GitHub.

つづく

タイトルとURLをコピーしました