目次
ユニットテストとは
インストール方法
簡単なテストコードの書き方と実行方法
テスト結果の確認方法
複数データのテスト
プライベートメソッドのテスト
ユニットテストとは
プログラムの小さな機能単位に対して行う動作確認のこと
ある操作を行った時に結果がどうなるかを
順々に試していく作業を自動化するためにある
インストール方法
composer
はインストールされている前提で進めていきます。
インストールしてない方はインストールしてから戻ってきてください
これは推奨されていないやり方ですが、
composer global require 'phpunit/phpunit:*'
echo "export PATH=\$PATH:\$HOME/.config/composer/vendor/bin" >> ~/.bash_profile
source ~/.bash_profile
phpunit -version
で確認してみてください
推奨されているやり方は以下の通りです
wget https://phar.phpunit.de/phpunit-|version|.phar
chmod +x phpunit-|version|.phar
./phpunit-|version|.phar --version
参考
https://phpunit.readthedocs.io/ja/latest/installation.html
簡単なテストコードの書き方と実行方法
<?php
class User
{
public $name;
public __construct($name)
{
$this->name = $name;
}
public function sayHi(): string
{
//return 'Hi ' . $this->name;
return 'Hi ' . $name;
}
}
<?php
require __DIR__ . '/User.php';
class User extends PHPUnit\Framework\TestCasee
{
public function testConstruct()
{
$user = new User('Perry');
$this->assertEquals('Perry', $user->name);
}
public function testSayHi()
{
$user = new User('Satoru');
$test = $user->sayHi();
$expected = 'Hi Satoru';
$this->assertEquals($expected, $test);
}
}
これを実行するときは
phpunit UserTest.php
とすれば良い
ディレクトリ単位でも実行することができる
実際にこれを実行すると
PHPUnit 7.5.20 by Sebastian Bergmann and contributors.
.E 2 / 2 (100%)
Time: 24 ms, Memory: 4.00 MB
There was 1 error:
1) UserTest::testCreateMessage
Undefined variable: name
/PATH/User.php:11
/PATH/UserTest.php:13
ERRORS!
Tests: 2, Assertions: 1, Errors: 1.
こんな感じで帰ってくる
最初の.
はテスト通ったよってこと
その次のE
はエラーを表している
エラーが出た場合は上のように内容が詳細に記される
最初の行ではテストメソッドの名前とエラーの個数が表示される
次の行ではエラーの内容
次に該当箇所が表示され、その次の行で、UserTest.php
の13行目を実行した時に
エラーが発生したと丁寧に書いてくれています
それではコードを修正しましょう
<?php
class User
{
public $name;
public __construct($name)
{
$this->name = $name;
}
public function sayHi(): string
{
return 'Hi ' . $this->name;
}
}
これで実行してみましょう
.. 2 / 2 (100%)
Time: 22 ms, Memory: 4.00 MB
OK (2 tests, 2 assertions)
いい感じですね!
ちなみに文字リテラルが間違っていた場合はE
ではなく
F
が出力されます
スキップされた際はS
らしいですみたことないけど!
このようにテスト実行->失敗した場合は修正->テスト
を繰り返して進めていきます
今回はassertEqual
しか使わなかったですが、他にもたくさんあるので調べてみてください
分かりやすい記事があったので説明は省略します
https://qiita.com/rev84/items/12fbd16d210d6a86eff9
https://www.wakuwakubank.com/posts/425-php-php-unit/
複数データのテスト
データプロバイダを使って複数のデータをまとめてテストしましょう
<?php
class Calc
{
private $number1;
private $number2;
public function __construct(int $number1, int $number2)
{
$this->number1 = $number1;
$this->number2 = $number2;
}
public function calc(string $option = '+'): int
{
switch ($option) {
case '+':
$answer = $this->number1 + $this->number2;
break;
case '-':
$answer = $this->number1 - $this->number2;
break;
case '*':
$answer = $this->number1 * $this->number2;
break;
case '/':
if ($this->number2 == 0) return false;
$answer = $this->number1 / $this->number2;
break;
default:
return false;
}
return $answer;
}
}
<?php
require_once __DIR__ . '/Calc.php';
class CalcTest extends PHPUnit\Framework\TestCase
{
/**
* @dataProvider numberProvider
*/
public function testCalc(int $number1, int $number2, string $option, int $expected)
{
$calc = new Calc($number1, $number2);
$test = $calc->calc($option);
$this->assertEquals($expected, $test);
}
public function numberProvider()
{
return [
[1, 2, '+', 3],
[1, 2, '-', 3],
[1, 2, '*', 3],
[1, 2, '/', 3],
[3, 4, '+', 12],
[3, 4, '-', 12],
[3, 4, '*', 12],
[3, 4, '/', 12],
[7, 8, '+', 15],
[7, 8, '-', 15],
[7, 8, '*', 15],
[7, 8, '/', 15],
];
}
}
実行方法は先ほどのコマンドに--debug
オプションをつけるだけー
実行結果は以下のようになりました
.FFFFF.F.FFF 12 / 12 (100%)
Time: 24 ms, Memory: 4.00 MB
There were 9 failures:
1) CalcTest::testCalc with data set #1 (1, 2, '-', 3)
Failed asserting that -1 matches expected 3.
/PATH/CalcTest.php:12
2) CalcTest::testCalc with data set #2 (1, 2, '*', 3)
Failed asserting that 2 matches expected 3.
/PATH/CalcTest.php:12
3) CalcTest::testCalc with data set #3 (1, 2, '/', 3)
Failed asserting that 0 matches expected 3.
/PATH/CalcTest.php:12
4) CalcTest::testCalc with data set #4 (3, 4, '+', 12)
Failed asserting that 7 matches expected 12.
/PATH/CalcTest.php:12
5) CalcTest::testCalc with data set #5 (3, 4, '-', 12)
Failed asserting that -1 matches expected 12.
/PATH/CalcTest.php:12
6) CalcTest::testCalc with data set #7 (3, 4, '/', 12)
Failed asserting that 0 matches expected 12.
/PATH/CalcTest.php:12
7) CalcTest::testCalc with data set #9 (7, 8, '-', 15)
Failed asserting that -1 matches expected 15.
/PATH/CalcTest.php:12
8) CalcTest::testCalc with data set #10 (7, 8, '*', 15)
Failed asserting that 56 matches expected 15.
/PATH/CalcTest.php:12
9) CalcTest::testCalc with data set #11 (7, 8, '/', 15)
Failed asserting that 0 matches expected 15.
/PATH/CalcTest.php:12
FAILURES!
Tests: 12, Assertions: 12, Failures: 9.
データセットごとにtestCalc()メソッドが実行されていますね!
テストメソッドであるtestCalc()メソッドの直前に@dataProvider numberProvider
と記述しましたが
これがデータプロバイダとしてメソッドを使うための指示になっているので書き忘れ厳禁
データプロバイダメソッドのnumberProvider()
メソッドはデータ配列かIteratorインターフェイスを実装したオブジェクトを返すように記述してください
フィクスチャの話
テストを実行するための事前準備をフィクスチャと呼ぶ
PHPUnitには準備用のコードを共有するために
各テストメソッドが実行される前に自動的に実行されるsetUp()
メソッドや
各テストメソッドが実行された後に自動的に実行されるtearDown()
メソッドがある
プライベートメソッドのテスト
ReflectionMethod
クラスを使う
テストケースを記述するクラスはテストするクラスとは別なので
プライベートメソッドをそのままテストすることができない
そこでReflectorMethod
クラスを使うと外部からアクセス可能なように書き換えてテストしてくれるらしい
めっちゃ便利
$r = new ReflectionMethod('テスト対象のクラス', 'テスト対象のメソッド');
$r->setAccessible(true);
$test = $r->invoke('テスト対象のクラスのインスタンス');
こんな感じ
ReflectionMethodのインスタンスを作成してメソッドのアクセス権を書き換えて
テスト対象のメソッドを呼び出して結果を取得してるー
//@dataProvider numberProvider
public function testCalc()
{
$r = new ReflectionMethod('Calc', 'calc');
$r->setAccessible(true);
$test = $r->invoke(new Calc(1, 2));
$expected = 3;
$this->assertEquals($expected, $test);
}
実行結果はこんな感じー
. 1 / 1 (100%)
Time: 22 ms, Memory: 4.00 MB
OK (1 test, 1 assertion)
データベースのテスト方法は長くなるのでまた次の機会にまとめられたらなと思いますー
テストのカバー率やSeleniumを使ってブラウザテストもできるみたいなので
それも合わせてまとめられたらなと思ってますー