こもろぐ @tenkoma

What We Find Changes Who We Become -- Peter Morville著『アンビエント・ファインダビリティ 』

2018年に買った物まとめ

満足度高い順で。

27インチ 4Kモニタ, USB-Cで給電可能

外部モニタをずっと使って無くて2003年に買ったEIZO L465しか家に無かったので買った。 ベゼル1cm以下、USB Type-CでMBP 15インチを給電できる。 Nintendo Switch もHDMI接続で使ってる。 すごく作業しやすくなった。 不満があるとすれば

  • USB Type-Aポートが2つだけど4つは欲しかった
  • MBP とNintendo Switchを切り替えることがよくある。入力が5つあるが3つは使ってないので、入力切り替えで、使わないポートには切り替わらないようにできたらいいのにと思った。

くらい。

オカムラ オフィスチェア コンテッサ(メッシュ)+ウレタンキャスター

オカムラ オプションパーツ ウレタンキャスター (5個セット) G93107X

オカムラ オプションパーツ ウレタンキャスター (5個セット) G93107X

こしをだいじに。 オフィスでアーロンチェアを使ってるけど、背もたれを傾けて固定できたほうが便利だと思ったのでこちらを選択。 長時間座って作業できます。不満なし。 2月に買ったときは ¥175,050 だったが、¥9,000ほど値上がりしてた。

ドライヤー

パナソニック ヘアドライヤー ナノケア ピンクゴールド EH-NA99-PN

パナソニック ヘアドライヤー ナノケア ピンクゴールド EH-NA99-PN

ドライヤーで髪を乾かす習慣がなかったけど、ちゃんと乾かすようになった。 ドライヤーは風量が大事ですね。

電動歯ブラシ

Amazon プライムデーで購入。 歯の裏側を磨くのがすごく楽になった。

NAS + HDD4つ

WD HDD 内蔵ハードディスク 3.5インチ 2TB WD Red NAS用 WD20EFRX SATA3.0 5400rpm 3年保証

WD HDD 内蔵ハードディスク 3.5インチ 2TB WD Red NAS用 WD20EFRX SATA3.0 5400rpm 3年保証

Time Capsule で毎回バックアップ失敗するようになったので、渋々NASを検討して、これを購入。 コントロールパネルにブラウザでアクセスできて、何ができるのかわかりやすかった。 バックアップにしか使ってないけど、用途がふえたら、HDDの容量を増やしていくかも。

加湿器

引っ越す前から使っていたパナソニックの加湿器(FE-KLG03)だと、フルパワーでも加湿量不足を感じたので購入。 5リットル入るので、平日だと1日はもつ。 常に静音モードでつかってます。

Kindle Oasis

Kindle Oasis、電子書籍リーダー、防水機能搭載、Wi-Fi、8GB

Kindle Oasis、電子書籍リーダー、防水機能搭載、Wi-Fi、8GB

画面切り替えが速い。薄い。 せっかく買ったけど、あんまり使ってない。マンガを読みたいときだけは使う。

新訳版テスト駆動開発をPHPで写経した

f:id:tenkoma:20190109215900p:plain

テスト駆動開発

テスト駆動開発

旧訳(ピアソン)版をJavaで写経したことはあったけど、PHPではなかったので、正月に読んでいた。コードはGitHubに。

github.com

第Ⅰ部 他国通貨は完了。

第Ⅱ部 xUnitと付録B フィボナッチは気が向いたら写経しよう。

PHP で実装してみて

変数やプロパティの型宣言がないので、変更を省略した箇所が多い。 (ただしプロパティはPHP7.4で型宣言の機能が追加される予定(PHP: rfc:typed_properties_v2)なので、1年後はさらに型のエラーが利用できるはず) クラスをキャストするという概念もないので省略した。

HashMap に当たる機能が見つけられなかった。PHP: SplObjectStorage - Manualを利用してHashMap に近いものを実装しようかと思ったが写経の途中だったのでやめた。

assertEquals() は型チェックをしてくれないし、 assertSame() でオブジェクトの equals() を使ってくれないので、assertTrue()

$this->assertTrue($five->times(2)->equals(Money::dollar(10)));

というふうに書いた。(第4章 意図を語るテスト by tenkoma · Pull Request #6など)

CakePHP3 アプリケーションのためのDocker開発環境を作ってみた

CakePHP Advent Calendar 2018 - Qiita 24日目の記事になります。

先日、第133回 PHP勉強会@東京で「PhpStormとPHPUnitを連携してユニットテスト作成を楽にする」というテーマで発表しました という記事を書きました。 そのとき、利用したサンプルアプリケーションとして tenkoma/cakephp_cms を使ったのですが、デモを簡単にするためにDocker環境を作ったので紹介します。

サンプルアプリケーション tenkoma/cakephp_cmsは去年のCakePHP Advent Calendar 2017 - Qiitaに投稿したCakePHP 3 のチュートリアルにユニットテストを追加する (1), (2)で使ったものです。

Docker設定の概要

Docker環境の設定はshin1x1さんのPhpStorm + Docker for Mac(docker-compose)での PHPUnit と Remote Debug の設定 - Shin x Blogをベースとして以下の変更を加えてあります。

  • CakePHP3アプリケーションに必要な intl拡張をインストール
  • MySQLコンテナを追加
  • 設定ファイルを手動で編集しなくても、環境が作れるように環境変数を docker-compose.yml に記述
  • その他細かい調整

使い方

git clone https://github.com/tenkoma/cakephp_cms.git
cd cakephp_cms

でリポジトリを取得して使います。以下のコマンドが使えます。

  • make 起動してcomposerパッケージインストール(make up + make install)
  • make up 起動
  • make install composerパッケージインストール
  • make migrate データベースマイグレーション
  • make test テスト実行
  • make clean 終了

MySQLデータベースのデータは dbdata/ 以下に保存されるので、リセットしたい場合は、make clean して dbdata/ を削除してください。

設定ファイル

以下、設定ファイル一式です。

docker-compose.yml

docker-compose up -d, docker-compose down に必要な大本の設定です。

version: "3.7"
services:
  web:
    build: ./docker/web
    environment: &app-environment
      DATABASE_URL: "mysql://my_app:secret@dev-db/my_app?encoding=utf8mb4&timezone=UTC&cacheMetadata=true"
      DATABASE_TEST_URL: "mysql://my_app:secret@dev-db/test_myapp?encoding=utf8mb4&timezone=UTC&cacheMetadata=true"
      PHP_IDE_CONFIG: "serverName=localhost"
    volumes:
      - ./:/var/www/html:cached
      - ./docker/web/php.ini:/usr/local/etc/php/php.ini:cached
    working_dir: /var/www/html
    ports:
      - "8000:80"
  php-cli:
    build: ./docker/web
    environment:
      <<: *app-environment
    volumes:
      - ./:/var/www/html
      - ./docker/web/php.ini:/usr/local/etc/php/php.ini
    working_dir: /var/www/html
  dev-db:
    image: mysql:5.7
    command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_general_ci
    hostname: dev-db
    container_name: dev-db
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: my_app
      MYSQL_USER: my_app
      MYSQL_PASSWORD: secret
    volumes:
      - ./docker/mysql:/docker-entrypoint-initdb.d:cached
      - ./dbdata:/var/lib/mysql:cached
    ports:
      - "3306:3306"
  composer:
    build: ./docker/composer
    volumes:
      - ./:/app:cached
      - ./docker/web/php.ini:/usr/local/etc/php/php.ini:cached
    working_dir: /app

docker/composer/Dockerfile

Composer パッケージのインストールが早くなるよう、composerコンテナにhirak/prestissimo: composer parallel install pluginを追加しました。

FROM composer

RUN composer global require hirak/prestissimo

docker/mysql/001_initialize_database.sh

MySQL の公式コンテナは1つ目のデータベースは環境変数で指定できるのですが、それ以上は指定する方法がなさそうだったので初回起動時に作成するためのものです。

#!/bin/sh

echo "CREATE DATABASE IF NOT EXISTS \`test_myapp\` ;" | "${mysql[@]}"
echo "GRANT ALL ON \`test_myapp\`.* TO '${MYSQL_USER}'@'%' ;" | "${mysql[@]}"
echo 'FLUSH PRIVILEGES ;' | "${mysql[@]}"

docker/web/Dockerfile

webとphp-cliコンテナのためのDockerfileです。CakePHPアプリ向けにintl拡張、pdo_mysql拡張を追加して、ドキュメントルートを変更しました。

FROM php:7.2-apache

RUN apt-get update && apt-get install -y --no-install-recommends \
        nano \
        libicu-dev \
    && rm -rf /var/lib/apt/lists/*

RUN docker-php-ext-install intl \
    && docker-php-ext-install pdo_mysql \
    && pecl install xdebug \
    && docker-php-ext-enable xdebug

ENV APACHE_DOCUMENT_ROOT /var/www/html/webroot

RUN a2enmod rewrite

RUN sed -ri -e 's!/var/www/html!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/sites-available/*.conf
RUN sed -ri -e 's!/var/www/!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/apache2.conf /etc/apache2/conf-available/*.conf

docker/web/php.ini

デバッグの切り替えをBookmarkletで制御したかったのでautostart をOffにして、idekeyを有効にしてあります。

; timezone
date.timezone = Asia/Tokyo

; error reporing
log_errors = On
error_log = /dev/stderr

; xdebug
xdebug.remote_enable = On
xdebug.remote_autostart = Off
xdebug.remote_connect_back = Off
xdebug.remote_host = docker.for.mac.localhost
;xdebug.remote_port=9000
xdebug.idekey=phpstorm

Makefile

migratetest を追加しました。

all: install up
.PHONY: all

up:
  docker-compose up -d
.PHONY: up

install:
  docker-compose run composer install --ignore-platform-reqs --no-interaction
.PHONY: install

migrate:
  docker-compose run php-cli bin/cake migrations migrate
.PHONY: migrate

test:
  docker-compose run php-cli ./vendor/bin/phpunit
.PHONY: test

clean:
  docker-compose down
.PHONY: clean

第133回 PHP勉強会@東京で「PhpStormとPHPUnitを連携してユニットテスト作成を楽にする」というテーマで発表しました

CakePHP Advent Calendar 2018の3日目でCakePHP2 アプリでも PHPUnit と PhpStormを連携させるという記事を書きました。 実はこの連携と、連携することによってテスト作成が楽になる、という話は意外と知られていないのでは、と思い、PHP勉強会では初めての20分枠で発表することにしました。(PHPカンファレンス2018の会場から参加登録しました 笑)

20分枠でしたが内容を詰めていく内に以下の内容を削りました。

少し早口だったと思いますが20分に収まって良かったです。

日本PHPユーザ会のみなさん、参加してくださったみなさん、会場を提供していただいたGMOインターネットさん 発表の場をいただき、ありがとうございました!

CakePHP2 アプリでも PHPUnit と PhpStormを連携させる

CakePHP Advent Calendar 2018 - Qiita 3日目の記事になります。

去年のCakePHPアドベントカレンダーではCakePHP 3 のチュートリアルにユニットテストを追加する話を書きましたが、 今年はCake 2のテストの話を書きます。

CakePHP2 アプリは PhpStorm の恩恵が受けにくい

プロダクト開発でCakePHP2 を使ってアプリを構築していると、 PhpStorm 機能の恩恵を受けられない以下のような難点があります。

  • DBのデータを取り出すと配列で、コード補完ができない
  • 名前空間対応してないので、コード補完ができない
  • ユニットテスト実行が、phpunitコマンドではなく、独自のコマンド Console/cake test であり、PhpStorm と連携できない

1つ目と2つ目もつらい感じですが、テストを書くうえは3番目もつらいですね。 連携できれば、特定のテストメソッド実行や、行カバレッジをエディタに反映できてテストを書くのがはかどるのに!

ずっとそう思っていましたが、CakePHP のGitHubを見ていると次のIssueを見つけました。

[CakePHP2] Support for running phpunit directly? · Issue #12700 · cakephp/cakephp

But this means you can't integrate it with tooling like IDE/PhpStorm which expect to run phpunit directly and pass it appropriate flags.

This is very useful for re-running partial tests (i.e. only the failed tests), etc.

要は 「phpunit コマンドでテスト実行できないと、PhpStorm と連携できないけど、連携できると部分的にテスト再実行できたりして便利」という話です。

同じことを考えているCake2ユーザーは結構いるんじゃないかと思います。 ちなみにCakePHP 3は phpunit コマンドで実行できるので、PhpStorm 連携も簡単にできるはずです。

(2013年には独自コマンド Console/cake test のまま連携するためのノウハウがPhpStorm Blogで公開されていました(Running CakePHP2 Unit Tests in PhpStorm | PhpStorm Blog)が現在は動かないみたいです。)

さて、このIssueを見て僕は思いました。PhpStorm と連携できると、テストをとても書きやすくなるし、敷居も下がるはず。 そのためになんとかして phpunit コマンドでテストを動かせないか。

試行錯誤してみたところ、狙い通り動かすことができたので、記事としてまとめます。

目次

  • CakePHP 2 アプリのユニットテストをPhpStorm と連携させてできること
  • 実装1: テストクラス・テスト対象クラスを実行するのに必要な前処理を app/Test/bootstrap.php に書く
  • 実装2: CakeTestRunner がやっていたFixtureManagerを準備する処理を実装する
  • PhpStorm の設定
  • 既存のテストやプロダクションコードでテスト時にエラーが出るようになった場合は
  • おまけ: FixtureManager の準備をもう少し改善する

CakePHP 2 アプリのユニットテストをPhpStorm と連携させてできること

できたことを先に紹介します!

PhpStorm からテスト実行できる、結果がエディタに反映される

CakePHP2 アプリをインストールすると用意されている PagesController にテストを書いてみましょう。(app/Test/Case/Controller/PagesControllerTest.php)

<?php
App::uses('AppControllerTestCase', 'TestSuite');
class PagesControllerTest extends AppControllerTestCase
{
    public function testDisplay()
    {
        $this->testAction('/');
        $this->assertContains(
            'CakePHP is a rapid development framework for PHP',
            $this->contents
        );
    }

    public function testMyPage()
    {
        $this->testAction('/mypage');
    }
}

これを実行すると以下のような画面になります。

f:id:tenkoma:20181203023635p:plain

エディタ行番号の右にマークが付いていて、ここからテストを実行できます。 テスト結果は下のパネルに表示され、デバッグトレースで表示されるパスから、エディタにジャンプできます。

コードカバレッジがエディタに表示される

コードカバレッジを見れば、ユニットテストでよく分からないエラーになったときなど、どのコードまで実行されたのか簡単に追いかけることが出来ます。

class PagesControllerTest の左側のマークをクリックして、「Run 'PagesControllerTest (PHPUnit)' with Coverage」を選択してテストを実行してみましょう。 そして、テスト実行後に PagesController.php を開きます。

f:id:tenkoma:20181203024940p:plain

右側に Coverage パネルが表示され、エディタでは、実行された行(緑)と実行されてない行(赤)で色分けされています。

Xdebug 連携

Xdebug を使ったブレーク・ステップ実行自体は、phpunitコマンド化しなくても、なんとか可能と思います。 しかし、PHPUnit 連携すれば、テスト実行時にデバッグの有効無効を切り替えられるので、ブレークポイントの掃除が不要になります。

f:id:tenkoma:20181203030406p:plain

配列のアサーションDiff がエディタのDiffで見られる

assertEquals(), assertSame()*1 でテスト失敗時に「<Click to see difference>」をクリックすると以下のように表示されます。

f:id:tenkoma:20181203030717p:plain

以上のように便利になるので、ユニットテストが書きやすくなります。 それでは、連携を実現するための実装について説明します。

実装1: テストクラス・テスト対象クラスを実行するのに必要な前処理を app/Test/bootstrap.php に書く

Cake2アプリのユニットテストを実行するにはフレームワークとアプリで定義する定数、Configure、App クラスやその他の関数を読み込む必要があります。 そこで phpunit 向けのブートストラップファイルを用意し、CakePHP 2 TestShell がやってくれていた処理を書きます。

app/Test/bootstrap_for_phpunit_command.php

<?php
/**
 * Bootstrap for phpunit command
 */
/**
 * copy from app/Console/cake.php
 */
$dispatcher = 'Cake' . DS . 'Console' . DS . 'ShellDispatcher.php';
$root = dirname(dirname(dirname(__FILE__)));
$appDir = basename(dirname(dirname(__FILE__)));
$install = $root . DS . 'lib';
$composerInstall = $root . DS . $appDir . DS . 'Vendor' . DS . 'cakephp' . DS . 'cakephp' . DS . 'lib';
// the following lines differ from its sibling
// /lib/Cake/Console/Templates/skel/Console/cake.php
if (file_exists($composerInstall . DS . $dispatcher)) {
    $install = $composerInstall;
}
ini_set('include_path', $install . PATH_SEPARATOR . ini_get('include_path'));
if (!include $dispatcher) {
    trigger_error('Could not locate CakePHP core files.', E_USER_ERROR);
}
unset($dispatcher);
define('ROOT', $root);
define('APP_DIR', $appDir);
define('APP', ROOT . DS . APP_DIR . DS);
// ShellDispatcher内の、定数と環境変数を初期化するメソッドは利用するが、シェルは実行しない
new ShellDispatcher(array(getenv('_'), '-working', $appDir));
unset($root, $appDir, $install, $composerInstall);

// FixtureManager セットアップに必要
App::uses('AppFixtureManager', 'TestSuite/Fixture');

// cakeアプリならどこでも呼び出すのであえて App::uses() しないクラス。エラーになるので書く
App::uses('ClassRegistry', 'Utility');

app/Test/bootstrap.php

<?php
// phpunit コマンドから実行された場合に追加のbootstrap.php を読み込む
if (!defined('DS')) {
    define('DS', DIRECTORY_SEPARATOR);
    include_once dirname(__FILE__) . DS . 'bootstrap_for_phpunit_command.php';
}

bootstrap_for_phpunit_command.php に書かれていることは、app/Console/cake.php の記述をコピーして、必要な改変を加えたものです。 app/Test/bootstrap.php では DS 定数が定義されていたら*2読み込まないようにして、 Console/cake test でもテスト実行できるようにしています。 phpunit コマンドでこの app/Test/bootstrap.php を読み込むように設定します。

phpunit.xml.dist

<?xml version="1.0" encoding="UTF-8"?>
<phpunit
    colors="true"
    bootstrap="./app/Test/bootstrap.php"
    >
    <filter>
        <whitelist>
            <directory suffix=".php">app/Config</directory>
            <directory suffix=".php">app/Console</directory>
            <directory suffix=".php">app/Controller</directory>
            <directory suffix=".php">app/Lib</directory>
            <directory suffix=".php">app/Model</directory>
            <directory suffix=".php">app/Routing</directory>
            <directory suffix=".php">app/TestSuite</directory>
            <directory suffix=".php">app/View</directory>
        </whitelist>
    </filter>
</phpunit>

4行目の bootstrap="./app/Test/bootstrap.php" がその設定になります。 <whitelist> の部分は、コードカバレッジの連携時に必要です。

実装2: CakeTestRunner がやっていたFixtureManagerを準備する処理を実装する

phpunit コマンドでテスト実行すると、CakeTestRunner が面倒を見ていた TestCase::$fixtureManager が用意されなくなりますので、自前で準備します。 ここでは、テストケースの run() メソッドをオーバーライドして、$fixtureManager を用意しました。

コントローラーテスト用の基底クラス AppControllerTestCase とその他のテスト用の基底クラス AppTestCase にそれぞれ追加します。 追加するコードは同じなので、トレイトにまとめてもいいでしょう。

app/TestSuite/AppControllerTestCase.php

<?php
App::uses('ControllerTestCase', 'TestSuite');
class AppControllerTestCase extends ControllerTestCase
{
    public function run(PHPUnit_Framework_TestResult $result = null)
    {
        $this->setUpFixtureManagerForPhpunitCommand();
        return parent::run($result);
    }
    /**
    * 正式なユニットテスト実行コマンドではCakeTestRunnerからテスト実行するが
    * phpunit コマンドから実行したときは準備されないので TestCase::run() で準備できるようにする
    */
    private function setUpFixtureManagerForPhpunitCommand()
    {
        if (is_null($this->fixtureManager)) {
            App::uses('AppFixtureManager', 'TestSuite');
            if (class_exists('AppFixtureManager')) {
                $this->fixtureManager = new AppFixtureManager();
            } else {
                App::uses('CakeFixtureManager', 'TestSuite/Fixture');
                $this->fixtureManager = new CakeFixtureManager();
            }
            $this->fixtureManager->fixturize($this);
        }
    }
}

app/TestSuite/AppTestCase.php

<?php
App::uses('CakeTestCase', 'TestSuite');
class AppTestCase extends CakeTestCase
{
    public function run(PHPUnit_Framework_TestResult $result = null)
    {
        $this->setUpFixtureManagerForPhpunitCommand();
        return parent::run($result);
    }
    /**
    * 正式なユニットテスト実行コマンドではCakeTestRunnerからテスト実行するが
    * phpunit コマンドから実行したときは準備されないので TestCase::run() で準備できるようにする
    */
    private function setUpFixtureManagerForPhpunitCommand()
    {
        if (is_null($this->fixtureManager)) {
            App::uses('AppFixtureManager', 'TestSuite');
            if (class_exists('AppFixtureManager')) {
                $this->fixtureManager = new AppFixtureManager();
            } else {
                App::uses('CakeFixtureManager', 'TestSuite/Fixture');
                $this->fixtureManager = new CakeFixtureManager();
            }
            $this->fixtureManager->fixturize($this);
        }
    }
}

app/TestSuite/Fixture/AppFixtureManager.php

テストを作り込んでいくと、FixtureManager もカスタマイズしたくなると思うので、先に継承しておきましょう。

<?php
App::uses('CakeFixtureManager', 'TestSuite/Fixture');
class AppFixtureManager extends CakeFixtureManager
{
}

以上で、基本的な追加の設定は終わりです。

PhpStorm の設定

多少設定を変えているので自信はありませんが、「PHP CLI」の設定と「Test Frameworks」の設定をすると動くと思います。 手元の環境の設定をスクリーンショットで紹介します。

PHP CLI 設定

f:id:tenkoma:20181203020457p:plain

php-build を使ってビルドした、Xdebugが使えるPHP を指定しています。

PHP Test Frameworks 設定

f:id:tenkoma:20181203020957p:plain

Configuration TypeがLocalの設定を追加して、 phpunitコマンドと phpunit.xml.dist の場所を指定しています。

既存のテストやプロダクションコードでテスト時にエラーが出るようになった場合は

phpunit コマンドでテストを実行すると、既存のテストやアプリケーションコードがエラーになるかもしれません。 理由として考えられるのが、CakePHP TestShell 経由だとロード済みのクラスが読み込まれてない可能性です。 たとえば ClassRegistry クラスがそうです。 その場合、使っているクラスファイル内で App::uses('ClassRegistry', 'Utility'); とちゃんと書くか、 bootstrap_for_phpunit_command.php に書けばテスト実行できるようになるはずです。

おまけ: FixtureManager の準備をもう少し改善する

CI 含めて Console/cake test によるテスト実行を捨て、 phpunit コマンドに移行出来る場合は、FixtureManager の準備をもう少し改善することができます。 CakePHP 3 の phpunit.xml.dist が参考になります。 それを見ると、PHPUnit のテストリスナー機能を利用してテスト前後のイベントにフックしてテストケースに FixtureManager オブジェクトを渡していました。

移植できるか試してみたところ、CakePHP 2.10 + PHPUnit 3.7 または 5.7 で動作しました。

参考: PHPUnit_Framework_TestListener の実装

app/TestSuite/Fixture/AppFixtureInjector.php

<?php
App::uses('CakeTestCase', 'TestSuite');

class AppFixtureInjector implements PHPUnit_Framework_TestListener
{
    /** @var CakeFixtureManager */
    protected $fixtureManager;

    /** @var PHPUnit_Framework_TestSuite */
    protected $first;

    public function __construct(CakeFixtureManager $manager)
    {
        $this->fixtureManager = $manager;
        $this->fixtureManager->shutDown();
    }

    public function startTestSuite(PHPUnit_Framework_TestSuite $suite)
    {
        if (empty($this->first)) {
            $this->first = $suite;
        }
    }

    public function endTestSuite(PHPUnit_Framework_TestSuite $suite)
    {
        if ($this->first === $suite) {
            $this->fixtureManager->shutDown();
        }
    }

    public function startTest(PHPUnit_Framework_Test $test)
    {
        $test->fixtureManager = $this->fixtureManager;
        if ($test instanceof CakeTestCase) {
            $this->fixtureManager->fixturize($test);
            $this->fixtureManager->load($test);
        }
    }

    public function endTest(PHPUnit_Framework_Test $test, $time)
    {
        if ($test instanceof CakeTestCase) {
            $this->fixtureManager->unload($test);
        }
    }

    /**
    * {@inheritdoc}
    */
    public function addError(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
    }

    /**
    * {@inheritdoc}
    */
    public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time)
    {
    }

    /**
    * {@inheritdoc}
    */
    public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
    }

    /**
    * {@inheritdoc}
    */
    public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
    }
    
    /**
    * {@inheritdoc}
    */
    public function addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
    }
}

定義した AppFixtureInjector をテストリスナーとして追加するには phpunit.xml.dist<listeners> の部分を追加します。

phpunit.xml.dist

<?xml version="1.0" encoding="UTF-8"?>
<phpunit
    colors="true"
    bootstrap="./app/Test/bootstrap.php"
    >
    <listeners>
        <listener class="AppFixtureInjector" file="./app/TestSuite/Fixture/AppFixtureInjector.php">
            <arguments>
                <object class="AppFixtureManager" />
            </arguments>
        </listener>
    </listeners>
    <filter>
        <whitelist>
            <directory suffix=".php">app/Config</directory>
            <directory suffix=".php">app/Console</directory>
            <directory suffix=".php">app/Controller</directory>
            <directory suffix=".php">app/Lib</directory>
            <directory suffix=".php">app/Model</directory>
            <directory suffix=".php">app/Routing</directory>
            <directory suffix=".php">app/TestSuite</directory>
            <directory suffix=".php">app/View</directory>
        </whitelist>
    </filter>
</phpunit>

以上です。

4日目は @tsyama さんです。

*1:PHPUnit 7以上

*2:CakePHP TestShellから実行された場合は定義済み

Read the Docs にあるドキュメント(Sphinx) で引用符が "「", "」" などに変換されるのを防止する

PHPUnit のドキュメントは reStructuredTextというマークアップ言語で記述され、Sphinx というビルドツールでHTMLなどのフォーマットに変換されている。Sphinx によるビルドはRead the Docsというドキュメントホスティングサービス上で実行される。

Sphinx で使っている docutils の機能かと思うが、引用符 "' を各自然言語ごとにローカライズされた引用符に変換する機能があり、既定で有効になっている。日本語だと , , に変換される。

便利な場面もあるが、英単語、英文の場合は英語版と同じ , が見やすく感じるし、全体的にそれでもよさそうに思う。, を使おうと思えば翻訳時に使えるので。

そこで調査してプルリクエストを作った

必要な修正は

  • docutils は 0.14以上を使うようにする
  • docutils.conf に日本語の引用符スタイルに使う文字の設定を書く

となる。

以上。

CakePHPのカンファレンス "CakeFest 2019" の候補地に日本が!

  • Japan,
  • Atlanta GA, USA
  • France
  • Germany

全世界のCakePHP コミッターやユーザーが集まるカンファレンス CakeFest 🍰 - CakePHP Conference - Home Page 2019 の候補地に日本が選ばれています。

2008年からおよそ年に1回開催されていますが、日本で開催されたことはありません。CakeFestが日本で開催してほしいと思っている方は、是非一緒に投票しましょう!

投票ページ(cakefest.org)にある "vote!" ボタンを押すと My CakePHPというサイトへのログインを求められます。 アカウントがない方は下の "Sine-up" をクリックしてアカウントを作成してください。 CakePHPユーザーとしてのプロフィール表示機能以外はなさそうなサイトですが、投票には必要です。

よくわからないことがあれば、この記事のコメント欄か CakePHP公式の日本語話者向けSlackチャンネル などで聞いてもらえればと思います。よろしくお願いします。