CircleCIでPHPUnit

# はじめに
本記事はCircleCIを利用して、PHPUnitでCIの自動化を実施することを目的とし、以下の図をワンシーケンス通す備忘録とする。
![スクリーンショット 2020-08-08 17.21.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/377612/a44f6bc3-b0f9-301d-76f3-7e7a0c424d6d.png)

GitHubのリポジトリは[こちら](https://github.com/sakamotoyuya/circleci-phpunit)

# 環境
macOS Catalina:10.15.5（19F101）
php 7.4.9
composer 1.10.10
PHPUnit 9.3.7


# 目次
- 前提条件
- CircleCIを登録する
- GitHubでCI連携用のリポジトリを作る
- composerを取得する
- composerでPHPUnitをインストールする
- PHPUnitで手動実行するテストファイルを作成する
- PHPUnitを手動実行する
- CircleCIとGitHubで作成したリポジトリを連携する
- CircleCIでPHPUnitを動作させるためにGitHubにTestコードを修正してプッシュしてみる
- おまけ・・・config.yml作成時につまづいた点について

## 前提条件
本記事の手順については、予めGitHubの登録作業が実施済であることを前提とする。また、上記の図にある通り以下技術についての知識が必要。

- git
- docker
- php
- composer
- PHPUnit
- UNIXコマンド

## CircleCIを登録する
- [CircleCI](https://circleci.com/signup/)にアクセス
![スクリーンショット 2020-08-08 22.07.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/377612/8638c07d-31d0-2def-e8cb-d4ca02839c19.png)
- 「Sign up with GitHub」をクリックする
 - GitHubで未認証の場合は、CircleCIとの認証を促されるため、GitHubアカウントで認証する。
- 以下のような画面になる。 
![スクリーンショット 2020-08-08 22.17.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/377612/b7df9ea9-0c20-5513-03bb-ad2b426975c2.png)

## GitHubでCI連携用のリポジトリを作る
- GitHubにログインする(GitHubの登録は予め実施しておく必要がある。)  
![スクリーンショット 2020-08-08 22.37.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/377612/a07b6f71-ddb4-7754-b7c9-0c096b832cb6.png)
- Newをクリックする。  
![スクリーンショット 2020-08-08 22.38.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/377612/0dd8e47c-6930-43b0-aab6-40df73875c1c.png)
- 「Repository name」に任意のリポジトリ名を入力する。今回は「circleci-phpunit」を設定。
- 「Create repository」をクリックすると以下の画面が表示される。
![スクリーンショット 2020-08-09 8.03.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/377612/c8d2d232-bf6b-c28a-1e6d-6cc0324c7b5b.png)
- ベアリポジトリ(GitHub側)とノンベアリポジトリ(ローカル側)の現在の状態毎の、GitHubとの連携方法を提示してくれる。今回は「...or create a new repository on the command line」（ローカルにノンベアリポジトリを作成してからベアリポジトリにプッシュする方法）を採用することとする。
![スクリーンショット 2020-08-08 22.50.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/377612/98bccc5e-eb84-d7af-8634-28bf6b2c45c9.png)
- 上記のコマンドを枠内の右上のアイコンをクリックしてコピーする。
- git管理するフォルダを作成する。   
  今回はリポジトリ名(circleci-phpunit)と同じ名前で作成する。
- 「terminal.app」を開き、作成した「circleci-phpunit」階層に移動して、GitHubの画面のアイコンでコピーしたものを貼り付けて実行する。
- GitHubページにコマンドで追加した「README.md」が存在すればリポジトリの操作準備完了。

# composerを取得する
composerを取得するには予めphpをmacにインストールしてパスを切っておく必要がある。ちなみにmacには最初からphpが入っているが、macOSのバージョンをあげるとphpの設定がbackupとして変わってしまうことがあるので私の環境では[xampp](https://www.apachefriends.org/jp/index.html)をダウンロードしてxamppないの「xamppfies/bin」にパスを通して置きphpコマンドをterminalから利用できるようにしている。この状態であれば以下の手順でcomposerのダウンロードが可能となる。

まずはcomposerをダウンロードしたいディレクトリに移動する。

```.sh
cd プロジェクトのフォルダ
```

その後以下を参考にcomposerをダウンロードする。

[composerの本家サイトから抜粋](https://getcomposer.org/download/)

```.sh
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php -r "if (hash_file('sha384', 'composer-setup.php') === 'e5325b19b381bfd88ce90a5ddb7823406b2a38cff6bb704b0acc289a09c8128d4a8ce2bbafcd1fcbdc38666422fe2806') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
php composer-setup.php
php -r "unlink('composer-setup.php');"
```
composer.pharをダウンロード後、拡張子を除いてダウンロードしたcomposerが動作するか確認する。※pharのままでも動作可能だが、コマンド短縮のため拡張子は省く。

```.sh
sakamotoyuya:circleci-phpunit sakamotoyuya$ mv composer.phar composer
sakamotoyuya:circleci-phpunit sakamotoyuya$ ./composer -v
   ______
  / ____/___  ____ ___  ____  ____  ________  _____
 / /   / __ \/ __ `__ \/ __ \/ __ \/ ___/ _ \/ ___/
/ /___/ /_/ / / / / / / /_/ / /_/ (__  )  __/ /
\____/\____/_/ /_/ /_/ .___/\____/____/\___/_/
                    /_/
Composer version 1.10.10 2020-08-03 11:35:19

Usage:
  command [options] [arguments]

Options:
・・・
```

# composerでPHPUnitをインストールする
- ダウンロードしたcomposerと同一階層(以後ルートディレクトリという)に「composer.json」ファイルを以下の内容で作成する。  
[PHPUnit本家サイトstartup](https://phpunit.de/getting-started/phpunit-9.html)を参考にして実施。

```composer.json
{
    "autoload": {
        "classmap": [
            "src/"
        ]
    },
    "require-dev": {
        "phpunit/phpunit": "^9"
    }
}
```

オートロード(autoload)方法には`psr-4`、`psr-0`、`classmap`、`files`とあるが、今回は規定したディレクトリ内を全て読み込む`classmap`を対象にすることとする。

オートロードの詳細は以下を参考。
[composerドキュメント - オートロード](https://getcomposer.org/doc/04-schema.md#autoload)

オートロードの指定先を`src/`として、このフォルダは以下のモジュールを自動読み込み対象ファイルとする。

`require-dev`はパッケージリンクという。
パッケージリンクの詳細は以下を参考。
[composerドキュメント - パッケージリンク](https://getcomposer.org/doc/04-schema.md#package-links)

パッケージリンクには`require`(本番用)と`require-dev`（開発用）の二つがある。
これについての使い方の詳細は以下がわかりやすいため参照。
[【Laravel】composer require-devの開発、本番での切り分け](https://qiita.com/k-kurikuri/items/8e644b3338d8759d64fd)
>```
>$ composer install --no-dev
>```
>開発用パッケージは本番環境では不要ですので、--no-devオプションを付けcomposerコマンドを実行する。
>そうするとrequire-devオプションに記載されているライブラリ自体はvendorへインストールされません

要するにコンパイルオプションを切り替えることで、パッケージの導入可否を決定する。本番用ではPHPUnitを使わないため、`require-dev`に入れて--no-devをコンパイルオプションを付与することで、導入しない動作となる。

- ルートディレクトリにsrcフォルダを作成する。
- 以下のようなフォルダ構造となる。

```
ルートディレクトリ
　|- .git　　　　　　 ・・・隠しフォルダ
　|- src　　　　　　　・・・フォルダ
　|- composer　　　　・・・ファイル
　|- composer.json　・・・ファイル
　|- README.md　　　 ・・・ファイル
```

- ルートディレクトに移動する。`cd ルートディレクトリ`
- 以下のコマンドでPHPUnitをインストールする。

```.sh
$ ./composer install
```
composerのコマンドを実行するには、実行ファイル`composer`とその実行ファイルが読み取る設定ファイル`composer.json`が必要となる。そのファイルが置いてある箇所に移動して実行する必要がある。
本手順でvendorフォルダが作成される。
vendorフォルダは以下にパッケージがインストールされていくのですが、このフォルダは大きくなったり可変することが多いので、.gitignoreファイルを作ってgit管理の対象外にする。

- 以下の内容で.gitignoreファイルを作る。

```..gitignore
vendor
```

- ディレクトリは以下のようになる。

```
ルートディレクトリ
　|- .git　　　　　　 ・・・隠しフォルダ
　|- .gitignore　　　・・・⭐️ファイル
　|- src　　　　　　　・・・フォルダ
　|- vendor　　　　　・・・⭐️composerによりインストールされたフォルダ
　|  |- bin
　|  |   |- phpunit・・・⭐️phpunitの実行ファイル
　|  ~
　|- composer　　　　・・・ファイル
　|- composer.json　・・・ファイル
　|- README.md　　　 ・・・ファイル
```

# PHPUnitで手動実行するテストファイルを作成する
- srcフォルダに運用するモジュールのHuman.phpを作成する

```Human.php
<?php
class Human{    
    public function helloString(){
        return "こんにちわ";
    }
    public function goodnightString(){
        return "おやすみ";
    }
}
```

- ルートフォルダにtestsフォルダを作成する。フォルダ構成は以下のようになる。

```
ルートディレクトリ
　|- .git　　　　　　 ・・・隠しフォルダ
　|- src　　　　　　　・・・フォルダ
　|  |- Human.php   ・・・⭐️評価対象モジュール
　|
　|- tests　　　　　　・・・⭐️フォルダ
　|- vendor　　　　　・・・composerによりインストールされたフォルダ
　|  |- bin
　|  |   |- phpunit・・・phpunitの実行ファイル
　|  ~
　|- composer　　　　・・・ファイル
　|- composer.json　・・・ファイル
　|- README.md　　　 ・・・ファイル

```

- testsフォルダにHuman.phpをテストするための、テストコードHumanTest.phpを作成する

```HumanTest.php
<?php
use PHPUnit\Framework\TestCase;
final class SakamotoTest extends TestCase{
    public function testA(){
        $obj = new Human();
        $this->assertSame("こんにちわ",$obj->helloString());
    }
    public function testB(){
        $obj = new Human();
        // $this->assertSame("おーまいっが！",$obj->goodnightString());
        $this->assertSame("おやすみ",$obj->goodnightString());
    }

    public function testD(){
        $obj = new Human();
        $this->assertSame("おやすみ",$obj->goodnightString());
    }
}

```
テストコードのファイル名には決まりがあり、`「テストしたいモジュールのファイル名+Test.php」`のようにする必要がある。今回`Human.php`をテストしたいので、ファイル名は`HumanTest.php`となる。また、`HumanTest.php`の中身のクラス名はファイル名と異なっても動作可能なようです。試しに`SakamotoTest`クラスにしてもテスト実行可能でした。そうする意味もないので、クラス名はファイル名に合わせて`HumanTest`にした方が可読性がいいですね。今回は上記の通りお試しのコードとなっているが、実際に作る際は`HumanTest`クラスとしましょう。

- このときのフォルダ構成は以下となる

```
ルートディレクトリ
　|- .git　　　　　　 ・・・隠しフォルダ
　|- src　　　　　　　・・・フォルダ
　|  |- Human.php   ・・・評価対象モジュール
　|
　|- tests　　　　　　・・・フォルダ
　|  |- HumanTest.php・・・⭐️Testファイル
　|
　|- vendor　　　　　・・・composerによりインストールされたフォルダ
　|  |- bin
　|  |   |- phpunit・・・phpunitの実行ファイル
　|  ~
　|- composer　　　　・・・ファイル
　|- composer.json　・・・ファイル
　|- README.md　　　 ・・・ファイル
```
# PHPUnitを手動実行する
- ソースコードを更新したので、オートロードを更新する

```
$ ./composer dump-autoload
```
プロジェクトのファイルを更新した場合はこのコマンドを実行する必要がある。
これを実施しないと変更前の状態での開始となっている。

- PHPUnitを手動実行する

```
$ ./vender/bin/phpunit tests
```
テストコードのあるフォルダを指定する。今回はtestsフォルダを指定する。
問題なければ以下のような結果になる。

```
sakamotoyuya:circleci-phpunit sakamotoyuya$ ./vendor/bin/phpunit tests
PHPUnit 9.3.7 by Sebastian Bergmann and contributors.


Warning:       Test case class not matching filename is deprecated
               in /Users/sakamotoyuya/Documents/GitHub/circleci-phpunit/tests/HumanTest.php
               Class name was 'SakamotoTest', expected 'HumanTest'

...                                                                 3 / 3 (100%)

Time: 00:00.016, Memory: 4.00 MB

OK (3 tests, 3 assertions)
...                                                                 3 / 3 (100%)

Time: 29 ms, Memory: 4.00 MB

OK (3 tests, 3 assertions)
```
このように結果がOKであればテストOKということです。
サンプルコード`HumanTest.php`は文字列比較しているだけなので、コメントアウト箇所を入れ替えることでNGの場合も確認できる。手動での確認はここまで。
いよいよ次からこの流れをCircleCIを用いて自動化する。

- ここまでできたら、コミットしてGitHubにプッシュする。

```.sh
git add .
git commit -m "phpunitでテスト環境を作成"
git push
```
プッシュ後のGitHubはこのようになる。
![スクリーンショット 2020-08-23 8.20.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/377612/b7abd4f7-83d6-6a19-bed6-fe7da7027046.png)

# CircleCIとGitHubで作成したリポジトリを連携する
- CircleCIにログイン > Projectsを開く
![スクリーンショット 2020-08-23 9.10.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/377612/b89cea88-0d69-af10-e1c3-3a0ea463157d.png)
CircleCI登録時にGitHubと連携しているため、GitHubで作成したリポジトリ一覧が表示される。
- `circleci-phpunit`の`Set Up Project`をクリック
![スクリーンショット 2020-08-23 9.19.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/377612/be2e5c66-ac7e-aeeb-3815-5007f3a78e50.png)
- `Use Existing Config`をクリック
![スクリーンショット 2020-08-23 9.20.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/377612/ac5ea9b6-6589-4100-a56e-edee093d2c0b.png)  

```..翻訳
config.ymlファイルを追加しましたか？  
すでに.circleci / config.ymlを追加している場合は、プロジェクトの構築を開始できる。
そうでない場合は、ビルドを開始する前に、config.ymlファイルをダウンロードして、
リポジトリのルートにある.circleciという名前の新しいフォルダーに追加する。
```
ルートディレクトリ直下に`.circleci/config.yml`の設定ファイルが必要とのことなので、これに従って設定ファイルを作っていく。

- .circleci/config.ymlを以下の内容で作成する。

```..circleci/config.yml
version: 2.1
jobs:
  build:
    environment:
      TZ: "Asia/Tokyo"
      DEBIAN_FRONTEND: noninteractive
    docker: 
      - image: ubuntu:latest # the primary container, where your job's commands are run
        environment:
          TZ: "Asia/Tokyo"
          DEBIAN_FRONTEND: noninteractive
    steps:
      - checkout # check out the code in the project directory
      - run: apt-get update && apt-get install -y tzdata
      - run: apt-get install -y wget sudo gnupg
      - run: sudo apt-get update
      - run: sudo apt-get upgrade -y
      - run: sudo apt install -y php
      - run: sudo apt-get install -y php-mbstring
      - run: sudo apt-get install -y php-xml
      - run: sudo apt-get install -y zip
      - run: sudo apt-get install -y unzip
      - run: echo "Set disable_coredump false" >> /etc/sudo.conf
      - run: php ./composer install && ./vendor/bin/phpunit tests
```
設定ファイルの詳細は後で記載する。

- 現在のフォルダ構成は以下の通りとなる

```
ルートディレクトリ
　|- .git　　　　　　 ・・・隠しフォルダ
　|- .circleci
　|  |- config.yml　・・・⭐️circleciの設定ファイル
　|
　|- src　　　　　　　・・・フォルダ
　|  |- Human.php   ・・・評価対象モジュール
　|
　|- tests　　　　　　・・・フォルダ
　|  |- HumanTest.php・・・Testファイル
　|
　|- vendor　　　　　・・・composerによりインストールされたフォルダ
　|  |- bin
　|  |   |- phpunit・・・phpunitの実行ファイル
　|  ~
　|- composer　　　　・・・ファイル
　|- composer.json　・・・ファイル
　|- README.md　　　 ・・・ファイル
```
- 設定ファイルを作成したら再度コミットしてプッシュする。

```
git add .
git commit -m "CircleCIの設定ファイル(config.yml)を作成"
git push
```
※GitHubの画面は省略する。

- CircleCIへログイン > Projects > phpunit-circleciのSet Up Project > Use Existing Config > Start Buildingをクリックする。
![スクリーンショット 2020-08-23 10.16.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/377612/c16404b2-ac2c-31f8-7cce-be6d354775e1.png)

このようになるので`circleci-phpunit #2`Actionsの一番左のアイコンをクリックする。
![スクリーンショット 2020-08-23 10.20.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/377612/e00bb956-aea4-4c98-3f66-935dd3c824eb.png)

セットアップできているか確認できるのでSUCCESSとなっていれば問題ありませんね。
CircleCIとGitHubリポジトリとの連携は完了です。
これでローカルのソースコードを修正してGitHubにプッシュした際にCircleCIが自動的にデバッグphpunitを実行してくれる環境を作成することができました。
実際にプッシュして試す。

# CircleCIでPHPUnitを動作させるためにGitHubにTestコードを修正してプッシュしてみる
- すでに成功する環境が作ってあるので、`README.md`にテストを追加してcommitしてプッシュする。

```README.md
# circleci-phpunit
テスト
```

コマンドで以下を実行する。

```
git add .
git commit -m "README.mdの修正"
git push
```

- CircleCI > Projects > phpunit-circleciをクリックするとこのようになっている。
![スクリーンショット 2020-08-23 10.30.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/377612/a437cc8b-3d32-c15f-c839-b3391b0d4df9.png)  
RUNNING状態で`config.yml`ファイルに従ってビルド実行中がわかる。
- 青色塗り潰し箇所の`RUNNING`をクリックすると以下の画面になる。
![スクリーンショット 2020-08-23 10.31.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/377612/35bea3e7-777f-2c67-bfae-ee35c838204d.png)
- `build`をクリックすると以下の画面になる。
![スクリーンショット 2020-08-23 10.31.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/377612/c1a0e83a-9095-5953-d98d-0b8b9c71ccd6.png)
この画面は、config.ymlファイルの設定に従って実行中の実行内容がリアルタイムで閲覧可能な画面になりる。  
`config.yml`ファイルを修正する際は、config.yml修正後にpush操作をして、この画面をみながら問題有無をチェックして問題あれば修正するという流れとする。  
この画像ではすでにSUCCESS表示となっているが、CircleCIの無料版ではビルド時間に制限があるので、ここを確認してビルドが終わらないような問題がある場合は「・・・」からビルドの停止ができるので停止するなどしましょう。ビルドが成功または失敗する場合は、ビルド終了となる。  

- CircleCIでビルドが成功するとGitHub表示も成功表示となる。
![スクリーンショット 2020-08-23 10.42.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/377612/a8b8f169-f5e0-8706-a2bb-b552247667de.png)  
緑色のチェック表示が入り、ビルドに失敗すると×表示となる。

# おまけ・・・config.yml作成時につまづいた点について
- circleciでcheckoutした後にいる、現在の作業ディレクトリはGitHubからcheckoutしたプロジェクトのルートディレクトリとなる
- phpインストール時にタイムゾーンが聞かれてずっと停止したままとなってしまっていた。
 - DockerでUbuntuを立ててそれ上に構築しようとしていたため以下を参考にしていたがわからず。  
  https://qiita.com/yagince/items/deba267f789604643bab
 - で、以下の記事を参考にtzdataをインストールすることで解決した。  
 https://sleepless-se.net/2018/07/31/docker-build-tzdata-ubuntu/
 - タイムゾーンの設定の仕方は[これ](https://circleci.com/docs/ja/2.0/faq/#docker-%E3%82%A4%E3%83%A1%E3%83%BC%E3%82%B8%E3%81%AE%E3%82%BF%E3%82%A4%E3%83%A0%E3%82%BE%E3%83%BC%E3%83%B3%E3%82%92%E8%A8%AD%E5%AE%9A%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95%E3%82%92%E6%95%99%E3%81%88%E3%81%A6%E3%81%8F%E3%81%A0%E3%81%95%E3%81%84)を参考にした。  
- runコマンドでUNIXコマンドが叩ける。
- circleciのWEB画面上で環境変数の設定が実はできる。
- CircleCIドキュメント上の上部の検索窓も便利