【Laravel】Dusk実行時のホスト名

Laravel

前回の記事でLaravel Duskを使ったブラウザテストを実行していますが、

テスト対象のWEBアプリはVue.jsでDB連携をする際にfetch()関数を使っていました。

その際にAPIのURL指定時にホスト名でハマったのでこの記事を書いています。

もし同様のことでお困りの方の助けになればと思います。

この記事のゴール

  • Laravel Dusk実行時のホスト名がどうなっているのか理解できる。
  • 当ケースでのホスト名の問題点がわかる。
  • Javascriptでの問題回避策がわかる。

結論から言うと

プロジェクトトップに docker-compose.yml というファイルがあります。

Laravelプロジェクト作成時にDockerが参照する設定ファイルです。

このファイル内の 「services」の子要素名が

各コンテナ間通信の際のホスト名の代わりになります。

※厳密に言うとホスト名ではなく、Docker Engine側で、

※このサービス名で名前解決してくれるようです。

筆者の環境では、次のような内容になっています。

version: '3'
services:
    laravel.test:
        build:
            context: ./vendor/laravel/sail/runtimes/8.2
            dockerfile: Dockerfile
            args:
                WWWGROUP: '${WWWGROUP}'
        image: sail-8.2/app
        extra_hosts:
            - 'host.docker.internal:host-gateway'
        ports:
            - '${APP_PORT:-80}:80'
            - '${VITE_PORT:-5173}:${VITE_PORT:-5173}'
        environment:
            WWWUSER: '${WWWUSER}'
            LARAVEL_SAIL: 1
            XDEBUG_MODE: '${SAIL_XDEBUG_MODE:-off}'
            XDEBUG_CONFIG: '${SAIL_XDEBUG_CONFIG:-client_host=host.docker.internal}'
        volumes:
            - '.:/var/www/html'
        networks:
            - sail
        depends_on:
            - mariadb
            - mailpit
            - selenium
    mariadb:
        image: 'mariadb:10'
        ports:
            - '${FORWARD_DB_PORT:-3306}:3306'
        environment:
            MYSQL_ROOT_PASSWORD: '${DB_PASSWORD}'
            MYSQL_ROOT_HOST: '%'
            MYSQL_DATABASE: '${DB_DATABASE}'
            MYSQL_USER: '${DB_USERNAME}'
            MYSQL_PASSWORD: '${DB_PASSWORD}'
            MYSQL_ALLOW_EMPTY_PASSWORD: 'yes'
        volumes:
            - 'sail-mariadb:/var/lib/mysql'
            - './vendor/laravel/sail/database/mysql/create-testing-database.sh:/docker-entrypoint-initdb.d/10-create-testing-database.sh'
        networks:
            - sail
        healthcheck:
            test:
                - CMD
                - mysqladmin
                - ping
                - '-p${DB_PASSWORD}'
            retries: 3
            timeout: 5s
    mailpit:
        image: 'axllent/mailpit:latest'
        ports:
            - '${FORWARD_MAILPIT_PORT:-1025}:1025'
            - '${FORWARD_MAILPIT_DASHBOARD_PORT:-8025}:8025'
        networks:
            - sail
    selenium:
        image: selenium/standalone-chrome
        ports:
            - 4444:4444
            - 5900:5900
        extra_hosts:
            - 'host.docker.internal:host-gateway'
        volumes:
            - '/dev/shm:/dev/shm'
        networks:
            - sail
networks:
    sail:
        driver: bridge
volumes:
    sail-mariadb:
        driver: local

ここで作成されるコンテナは次の4つです。

  • laravel.test :WEBサーバ
  • mariadb :DBサーバ
  • mailpit :開発環境専用メールサーバ
  • selenium :Selenium Grid Standalone with Chrome(ここでブラウザ操作)

これらが、お互いのコンテナ同士でアクセスする際のホスト名になります。

お互いのホスト間の関係をテキストで書くと次のようになります。

ホスト[localhost] (sail実行)

▼Dusk実行命令  ▲実行結果出力

ホスト[laravel.test](Dusk実行)

▼操作命令    ▲操作結果出力

ホスト[selenium](ブラウザ操作)

▼WEBアクセス  ▲WEBコンテンツ出力

ホスト[laravel.test](WEBコンテンツ生成)

▼SQL      ▲結果出力

ホスト[mariadb](DB管理)

何が問題なのか

筆者はVue.jsのアプリをCDN版で作成していました。

つまり、SSRでない通常のJavascriptと同様に、

コードのコンパイルと実行はクライアント側で行われます。

WEBサーバに設置してあるAPIへのアクセスURLが

Vue.js のコード内で次のように指定されていました。

`{{ env('APP_URL') }}/ajax/todo/update/`+ todo.id

{{ env(‘APP_URL’) }} の部分が、Bladeテンプレートのマスタッシュとして、

.env ファイル内で定義されている

APP_URL=http://localhost

で置き換えられます。

つまり、URLの完成形としては、

http://localhost/ajax/todo/update/1

のようなものになります。

さて、これの何が問題なのでしょうか?

このURLを解釈するのが ホスト[selenium] だということです。

[selenium] から見た [localhost] は自分自身 [selenium] です。

これではWEBサーバ [laravel.test] へアクセスできません。

[selenium] の80番ポートも443番ポートも

インバウンドアクセスはブロックされてしまいます。

当然、APIへのアクセスはブロックされてデータ取得できません。

下手すると接続待ちになってフリーズ同然になります。

道理でDuskがテスト失敗するわけです。

Javascriptにおける解決策

APIのホスト名を、開発環境においては [laravel.test] 、

本番環境においては本番サーバのホスト名を、

コードの書き換え無しに指定できるようにしなければなりません。

「http」や「https」等のプロトコルの指定も同様です。

Javascriptの場合は次の方法で解決できます。

location.protocol + '//' + location.host + '/ajax/todo/update/' + todo.id

「location.protocol」は、現在ブラウザで表示しているサイトのプロトコルを返します。

「http」や「https」のことです。

「location.host」は、現在ブラウザで表示しているサイトのホスト名を返します。

「localhost」、「www.hoge.hoge」、「github.com」などです。

Dusk実行した際のスクリーンショットを見てみましょう。

画面の上から3行目に「location.host」を表示していますが、

「laravel.test」になっていることがわかります。

コメント

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