MacのPhpStormでLaravelプロジェクトでPHPUnitをできるようにする記録

  • 環境
    • macOS Mojave バージョン10.14.4
    • Composer version 1.8.0

以前、Laravelのプロジェクトを作ったことがありましたが、今回はPhpStormを使ってやります。

ponsuke-tarou.hatenablog.com

PhpStormをインストールします。

  1. JetBeanのPHPStormのサイトからダウンロードします。
  2. ダウンロードしたPhpStorm-yyyy.x.x.dmgをクリックします。
  3. f:id:ponsuke_tarou:20190424223044p:plain
  4. f:id:ponsuke_tarou:20190424223107p:plain
  5. f:id:ponsuke_tarou:20190424223120p:plain
  6. f:id:ponsuke_tarou:20190424223133p:plain
  7. f:id:ponsuke_tarou:20190424223145p:plain
  8. f:id:ponsuke_tarou:20190424223205p:plain
    好きな色を選びます
  9. f:id:ponsuke_tarou:20190424223227p:plain
  10. f:id:ponsuke_tarou:20190424223239p:plain

新規にComposerプロジェクトを作成します。

# 事前にインストールしてあるComposerの場所を確認
$ which composer
/usr/local/bin/composer
  1. f:id:ponsuke_tarou:20190424223355p:plain
  2. f:id:ponsuke_tarou:20190424230607p:plain
    プロジェクトを作成するディレクトリとComposerの場所とpackageでLaravelを選択して作ります
  3. f:id:ponsuke_tarou:20190424234741p:plain
    プロジェクト作成中です。
  4. f:id:ponsuke_tarou:20190424235113p:plain
    プロジェクトが作成されました。

qiita.com

Composerがインストールされていない場合はPhpStormのサイトを参照して作成します。

以下サイトの[新しいComposerプロジェクトを作成するには] > [2. ダイアログで、プロジェクトのパラメータを指定します。] > [b. Composerコマンドの実行方法を選択します。]を参照
pleiades.io

Command line parameterで指定した「–prefer-dist」はLaravelをZIPでダウンロードするということです。

# こんなコマンドが動きます。
/usr/local/bin/composer create-project laravel/laravel /path/to/project/directory/tryPhp/composer --prefer-dist

kin29.info
getcomposer.org

Laravelのバージョンを確認します。

# ウィンドウの下にある[Terminal]またはMacのターミナルで確認します。
$ php artisan -V
Laravel Framework 5.8.14

LaravelにくっついているサーバでLaravelの初期画面を確認します。

# サーバを起動します
$ php artisan serve
Laravel development server started: <http://127.0.0.1:8000>
# 表示されたURLにブラウザでアクセスします。
[Wed Apr 24 23:58:16 2019] 127.0.0.1:51584 [200]: /favicon.ico

f:id:ponsuke_tarou:20190425000004p:plain
表示されました。

Xdebugをインストールします。

qiita.com

プロジェクトを設定します。

PHP language levelを使っているPHPのバージョンに合わせます。

# [PHP language level]用に使っているPHPのバージョンを確認します
$ php --version
PHP 7.3.1 (cli) (built: Jan 10 2019 13:15:37) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.3.1, Copyright (c) 1998-2018 Zend Technologies
    with Xdebug v2.7.2, Copyright (c) 2002-2019, by Derick Rethans
    with Zend OPcache v7.3.1, Copyright (c) 1999-2018, by Zend Technologies

# [CLI Interpreter]用に使っているPHPのディレクトリを確認します
$ which php
/usr/local/bin/php
$ ls -la /usr/local/bin/ | grep php
# 省略
lrwxr-xr-x    1 mana  admin       27  1 20 14:09 php -> ../Cellar/php/7.3.1/bin/php
# 省略
  1. [PhpStorm] > [Preferences...] > [Languages & Frameworks] > [PHP]を選択します。
  2. [PHP language level]を使っているPHPのバージョンへ変更します。
  3. [CLI Interpreter]の[...]ボタンでダイアログを開き、使っているPHPの場所を設定します。
    • f:id:ponsuke_tarou:20190507231840p:plain
      XdebugがDebuggerとして設定されます。
PHP language levelが非活性で変更できない場合の対応方法

qiita.com

PHPUnitは、プロジェクトを作ったときに配置されています。

プロジェクトの直下にあるcomposer.jsonを確認するとデフォルトでインストールされています。
venderディレクトリにもphpunitディレクトリが配置されています。

    "require-dev": {
        "beyondcode/laravel-dump-server": "^1.0",
        "filp/whoops": "^2.0",
        "fzaninotto/faker": "^1.4",
        "mockery/mockery": "^1.0",
        "nunomaduro/collision": "^2.0",
        "phpunit/phpunit": "^7.5"
    },
$ ls -l vendor/ | grep phpunit
drwxr-xr-x@  8 mana  staff  256  4 24 23:47 phpunit

f:id:ponsuke_tarou:20190506130945p:plain
venderディレクトリにもphpunitディレクトリが配置されています。

PHPUnitの設定をします。

(Macのターミナルを使う場合)PHPUnitのパスを通します。

# .bash_profileにphpunitのパスを書いて
$ echo 'export PATH="/Path/To/vendor/phpunit/phpunit:$PATH"' >> ~/.bash_profile
# 反映して
$ source ~/.bash_profile
# 確認します。
$ phpunit --version
PHPUnit 7.5.9 by Sebastian Bergmann and contributors.
パスはbin配下ではなくphpunit配下を指定します。

以前PHPStormを使わなかった時は、PHPUnitのPathにbin配下を指定しました。

$ echo 'export PATH="/Path/To/vendor/bin/phpunit:$PATH"' >> ~/.bash_profile

PHPUnitを使えるようにする。 - Qiita

今回はうまくいきませんでした。

$ echo 'export PATH="/Path/To/vendor/bin/phpunit:$PATH"' >> ~/.bash_profile
$ source ~/.bash_profile
$ phpunit --version
-bash: phpunit: command not found

シンボリックリンクが貼ってあるけどだめなのですね。

$ ls -la vendor/bin/
total 32
drwxr-xr-x@  6 mana  staff   192  5  6 14:50 .
drwxr-xr-x@ 47 mana  staff  1504  5  6 14:43 ..
-rwxr-xr-x@  1 mana  staff  6028  2 17 05:54 php-parse
lrwxr-xr-x   1 mana  staff    26  5  6 14:43 phpunit -> ../phpunit/phpunit/phpunit
-rwxr-xr-x@  1 mana  staff  4305 10 14  2018 psysh
lrwxr-xr-x   1 mana  staff    51  5  6 14:43 var-dump-server -> ../symfony/var-dumper/Resources/bin/var-dump-server

composer.jsonにautoloadの定義があることを確認します。

定義がなければ環境に合わせて定義します。

    "autoload": {
        "psr-4": {
            "App\\": "app/"
        },
        "classmap": [
            "database/seeds",
            "database/factories"
        ]
    },
    "autoload-dev": {
        "psr-4": {
            "Tests\\": "tests/"
        }
    },

対応付けのためのクラスマップ生成を行います。

# クラスマップ生成
$ composer dump-autoload
Generating optimized autoload files> Illuminate\Foundation\ComposerScripts::postAutoloadDump
> @php artisan package:discover --ansi
Discovered Package: beyondcode/laravel-dump-server
Discovered Package: fideloper/proxy
Discovered Package: laravel/nexmo-notification-channel
Discovered Package: laravel/slack-notification-channel
Discovered Package: laravel/tinker
Discovered Package: nesbot/carbon
Discovered Package: nunomaduro/collision
Package manifest generated successfully.                                                                                                           Generated optimized autoload files containing 3759 classes

# 確認します
$ cat vendor/composer/autoload_psr4.php
<?php

// autoload_psr4.php @generated by Composer

$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);

return array(
    'phpDocumentor\\Reflection\\' => array($vendorDir . '/phpdocumentor/reflection-common/src', $vendorDir . '/phpdocumentor/reflection-docblock/src', $vendorDir . '/phpdocumentor/type-resolver/src'),
    'Zend\\Diactoros\\' => array($vendorDir . '/zendframework/zend-diactoros/src'),
    'XdgBaseDir\\' => array($vendorDir . '/dnoegel/php-xdg-base-dir/src'),
    'Whoops\\' => array($vendorDir . '/filp/whoops/src/Whoops'),
    'Webmozart\\Assert\\' => array($vendorDir . '/webmozart/assert/src'),
    'TijsVerkoyen\\CssToInlineStyles\\' => array($vendorDir . '/tijsverkoyen/css-to-inline-styles/src'),
    'Tests\\' => array($baseDir . '/tests'),
    'Symfony\\Polyfill\\Php72\\' => array($vendorDir . '/symfony/polyfill-php72'),
    'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'),
    'Symfony\\Polyfill\\Intl\\Idn\\' => array($vendorDir . '/symfony/polyfill-intl-idn'),
    'Symfony\\Polyfill\\Iconv\\' => array($vendorDir . '/symfony/polyfill-iconv'),
    'Symfony\\Polyfill\\Ctype\\' => array($vendorDir . '/symfony/polyfill-ctype'),
    'Symfony\\Contracts\\' => array($vendorDir . '/symfony/contracts'),
    'Symfony\\Component\\VarDumper\\' => array($vendorDir . '/symfony/var-dumper'),
    'Symfony\\Component\\Translation\\' => array($vendorDir . '/symfony/translation'),
    'Symfony\\Component\\Routing\\' => array($vendorDir . '/symfony/routing'),
    'Symfony\\Component\\Process\\' => array($vendorDir . '/symfony/process'),
    'Symfony\\Component\\HttpKernel\\' => array($vendorDir . '/symfony/http-kernel'),
    'Symfony\\Component\\HttpFoundation\\' => array($vendorDir . '/symfony/http-foundation'),
    'Symfony\\Component\\Finder\\' => array($vendorDir . '/symfony/finder'),
    'Symfony\\Component\\EventDispatcher\\' => array($vendorDir . '/symfony/event-dispatcher'),
    'Symfony\\Component\\Debug\\' => array($vendorDir . '/symfony/debug'),
    'Symfony\\Component\\CssSelector\\' => array($vendorDir . '/symfony/css-selector'),
    'Symfony\\Component\\Console\\' => array($vendorDir . '/symfony/console'),
    'Ramsey\\Uuid\\' => array($vendorDir . '/ramsey/uuid/src'),
    'Psy\\' => array($vendorDir . '/psy/psysh/src'),
    'Psr\\SimpleCache\\' => array($vendorDir . '/psr/simple-cache/src'),
    'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'),
    'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src'),
    'Psr\\Container\\' => array($vendorDir . '/psr/container/src'),
    'PhpParser\\' => array($vendorDir . '/nikic/php-parser/lib/PhpParser'),
    'Opis\\Closure\\' => array($vendorDir . '/opis/closure/src'),
    'NunoMaduro\\Collision\\' => array($vendorDir . '/nunomaduro/collision/src'),
    'Nexmo\\' => array($vendorDir . '/nexmo/client/src'),
    'Monolog\\' => array($vendorDir . '/monolog/monolog/src/Monolog'),
    'League\\Flysystem\\' => array($vendorDir . '/league/flysystem/src'),
    'Lcobucci\\JWT\\' => array($vendorDir . '/lcobucci/jwt/src'),
    'Laravel\\Tinker\\' => array($vendorDir . '/laravel/tinker/src'),
    'JakubOnderka\\PhpConsoleHighlighter\\' => array($vendorDir . '/jakub-onderka/php-console-highlighter/src'),
    'JakubOnderka\\PhpConsoleColor\\' => array($vendorDir . '/jakub-onderka/php-console-color/src'),
    'Illuminate\\Notifications\\' => array($vendorDir . '/laravel/nexmo-notification-channel/src', $vendorDir . '/laravel/slack-notification-channel/src'),
    'Illuminate\\' => array($vendorDir . '/laravel/framework/src/Illuminate'),
    'Http\\Promise\\' => array($vendorDir . '/php-http/promise/src'),
    'Http\\Client\\' => array($vendorDir . '/php-http/httplug/src'),
    'Http\\Adapter\\Guzzle6\\' => array($vendorDir . '/php-http/guzzle6-adapter/src'),
    'GuzzleHttp\\Psr7\\' => array($vendorDir . '/guzzlehttp/psr7/src'),
    'GuzzleHttp\\Promise\\' => array($vendorDir . '/guzzlehttp/promises/src'),
    'GuzzleHttp\\' => array($vendorDir . '/guzzlehttp/guzzle/src'),
    'Fideloper\\Proxy\\' => array($vendorDir . '/fideloper/proxy/src'),
    'Faker\\' => array($vendorDir . '/fzaninotto/faker/src/Faker'),
    'Egulias\\EmailValidator\\' => array($vendorDir . '/egulias/email-validator/EmailValidator'),
    'Dotenv\\' => array($vendorDir . '/vlucas/phpdotenv/src'),
    'Doctrine\\Instantiator\\' => array($vendorDir . '/doctrine/instantiator/src/Doctrine/Instantiator'),
    'Doctrine\\Common\\Inflector\\' => array($vendorDir . '/doctrine/inflector/lib/Doctrine/Common/Inflector'),
    'DeepCopy\\' => array($vendorDir . '/myclabs/deep-copy/src/DeepCopy'),
    'Cron\\' => array($vendorDir . '/dragonmantank/cron-expression/src/Cron'),
    'BeyondCode\\DumpServer\\' => array($vendorDir . '/beyondcode/laravel-dump-server/src'),
    'App\\' => array($baseDir . '/app'),
    '' => array($vendorDir . '/nesbot/carbon/src'),
);

PHPUnitの設定ファイルとなるphpunit.xmlを確認します。

自動でできるなんて素敵ですね。

<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
         backupStaticAttributes="false"
         bootstrap="vendor/autoload.php"
         colors="true"
         convertErrorsToExceptions="true"
         convertNoticesToExceptions="true"
         convertWarningsToExceptions="true"
         processIsolation="false"
         stopOnFailure="false">
    <testsuites>
        <testsuite name="Unit">
            <directory suffix="Test.php">./tests/Unit</directory>
        </testsuite>

        <testsuite name="Feature">
            <directory suffix="Test.php">./tests/Feature</directory>
        </testsuite>
    </testsuites>
    <filter>
        <whitelist processUncoveredFilesFromWhitelist="true">
            <directory suffix=".php">./app</directory>
        </whitelist>
    </filter>
    <php>
        <env name="APP_ENV" value="testing"/>
        <env name="BCRYPT_ROUNDS" value="4"/>
        <env name="CACHE_DRIVER" value="array"/>
        <env name="MAIL_DRIVER" value="array"/>
        <env name="QUEUE_CONNECTION" value="sync"/>
        <env name="SESSION_DRIVER" value="array"/>
    </php>
</phpunit>

PhpStormにPHPUnitを設定します。

  1. [PhpStorm] > [Preferences...] > [Languages & Frameworks] > [PHP] > [Test Frameworks]を選択します。
  2. リストに「Local」がない場合は[+]ボタンで追加します。
  3. [PHPUnit library]で「Use Composer autoloder」を選択してvenderディレクトリ配下のautoloder.phpへのパスを設定します。
  4. [Test Runner]で[Default configuration file:]にチェックを入れてphpunit.xmlへのパスを設定します。

テストコードを作る

以前作ったファイルを流用します。なのでLaravelのディレクトリ構成とかからとっても外れています。
qiita.com

テスト対象ソースのエディタ上からテストクラスを生成します。

  1. f:id:ponsuke_tarou:20190506154955p:plain
    テスト対象ソース上でコンテキストメニュー > [Go To] > [Test]を選択します。
  2. f:id:ponsuke_tarou:20190506155122p:plain
    [Create New Test...]を選択します。既にテストペアが存在する場合はそのテストソースも表示されます。
  3. f:id:ponsuke_tarou:20190506155343p:plain
    テスト対象のメソッドを[Geneate test method for:]から選択して[OK]ボタンを押下します。
  4. f:id:ponsuke_tarou:20190506155514p:plain
    テストクラスが生成されました。

継承しているクラスをPHPUnit\Framework\TestCaseへ変更します。

自動生成時はHPUnit_Framework_TestCaseとなっていますがエラーとなるため変更します。
tomomik.hatenablog.com

Macのデフォルトではバックスラッシュが円マークになる事があるので以下のサイトを見てバックスラッシュで記載します。
Macにおけるバックスラッシュ(\)の入力方法 - Qiita

<?php

namespace App;

use PHPUnit\Framework\TestCase;

class controlStdClassArrayTest extends TestCase
{
// 省略

自動生成されたメソッドへテストコードを記載して保存します。

<?php

namespace App;

use PHPUnit\Framework\TestCase;

class controlStdClassArrayTest extends TestCase
{

    public function testCreateStdClassArrayNew()
    {
        $target = new controlStdClassArray();
        $stdList = $target->createStdClassArrayNew();

        foreach ($stdList as $std) {
            $this->assertInstanceOf(\stdClass::class, $std);
        }
    }
// 省略

作ったテストコード用に設定を追加します。

  1. f:id:ponsuke_tarou:20190507233810p:plain
    上部にある[Add Configurations...]を押下します。
  2. f:id:ponsuke_tarou:20190507234037p:plain
    [+]ボタンで表示されるテンプレートから[PHPUnit]を選択します。
  3. f:id:ponsuke_tarou:20190507235136p:plain
    各項目を入力して設定を追加します。

PHPUnitでテストコードを実行します。

f:id:ponsuke_tarou:20190507235717p:plain
[▶]ボタンで実行できます。

デバックしてブレークポイントで止めることもできます。

f:id:ponsuke_tarou:20190508000342p:plain
受話器マークをクリック後に虫ボタンでデバック実行してブレークポイントで止めることができます。