以前書いた PHP Paiza 標準入力テンプレート の記事が好評でPaizaの問題を解くときに参考にしてますというお声をよくいただいてます。とても嬉しいです☺️
環境
- PHP: 7.3以上
お悩み相談
Paizaに問題を提出すると標準入力と標準出力の結果を確認されるのですが、毎回手でポチポチ入力例を入力していくのは手間ですよね...?
入力例が1行であれば、まぁいいかとなるんですが、複数行入力するのは割と時間かかっちゃいますし、誤入力やりがちです。
phpunit というPHPの便利なテストフレームワークはありますが、Paizaの問題を解くためにフレームワークを入れるのは、まぁ大掛かりな気持ちがあります。
ということで今回は自前のテストコードを作っていきたいと思います。
FizzBuzz問題
今回はレベルアップ問題集 | PaizaからFizz Buzz (paizaランク C 相当) | Paizaを解いていきます。
整数 N が入力として与えられます。
1からNまでの整数を1から順に表示してください。
ただし、表示しようとしている数値が、
・3の倍数かつ5の倍数のときには、"Fizz Buzz"
・3の倍数のときには、"Fizz"
・5の倍数のときには、"Buzz"
を数値の代わりに表示してください。
入力例1
5
出力例1
1
2
Fizz
4
Buzz
入力例2
20
出力例2
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
Fizz Buzz
16
17
Fizz
19
Buzz
FizzBuzz解答例
色々な解き方はあるかと思いますが、私の解答例です。
<?php declare(strict_types=1);
$input = trim(fgets(STDIN));
for ($i = 1; $i <= $input; $i++) {
echo fizzBuzz($i), PHP_EOL;
}
function fizzBuzz(int $num): string
{
return [
[(string) $num, 'Buzz'],
['Fizz', 'Fizz Buzz'],
][$num % 3 == 0][$num % 5 == 0];
}
久しぶりにFizzBuzz問題を解きましたが、難しいですね。
何とかかんとか解けましたが、果たしてこのコードは正しいのでしょうか...?
自信がありません...。自動テストさえできれば...
本題: テストコードを用意する
test.php
を用意します。
<?php declare(strict_types=1);
// テストしたいファイルを指定する
$command = 'fizzbuzz.php';
// テストケースを定義する
$patterns = [
'case1' => [
'stdin' => ['5'],
'stdout' => ['1', '2', 'Fizz', '4', 'Buzz'],
],
'case2' => [
'stdin' => ['20'],
'stdout' => explode(PHP_EOL, <<<EOD
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
Fizz Buzz
16
17
Fizz
19
Buzz
EOD
),
],
'case3' => [
'stdin' => ['3'],
'stdout' => ['1', '2', 'Fuga'], // わざとテストが落ちるデータを入れてます
],
];
foreach ($patterns as $case => $pattern) {
assertStandard($command, $case, $pattern['stdin'], $pattern['stdout']);
}
function assertStandard(string $command, string $case, array $stdin, array $stdout): void
{
$args = implode('\\\n', $stdin);
$actual = trim(`echo $args | php $command`);
$expected = implode(PHP_EOL, $stdout);
echo sprintf("%s: %s, stdin: ['%s'], stdout: ['%s'] ... ", $command, $case, implode("', '", $stdin), implode("', '", $stdout));
try {
assert($expected === $actual);
echo 'ok', PHP_EOL;
} catch (AssertionError $exception) {
echo 'ng', PHP_EOL, '[actual]', PHP_EOL, $actual, PHP_EOL;
exit;
}
}
標準入力と標準出力をテストするため、 echo $args | php $command
でphpコード内でphpを実行してます。
バッククォート(実行演算子)で囲むとshell_execと等価です。
$actual
には実際のコードの実行結果、$expected
にはテストケースに定義した予期する値を設定してます。
assert
メソッドで結果を比較してます。
実際に使う場合は、テストしたいファイル名とテストケースを定義して使ってください。
テストケースは配列で定義する必要があります。標準入力や標準出力の数が多い場合は例のようにヒアドキュメントを使って定義してください。
テストケースは別ファイルから読み込むようにするか悩みましたが、どうだろう...? またご要望あれば検討しようかなと思います。
自動テストを実行する
$ php test.php
fizzbuzz.php: case1, stdin: ['5'], stdout: ['1', '2', 'Fizz', '4', 'Buzz'] ... ok
fizzbuzz.php: case2, stdin: ['20'], stdout: ['1', '2', 'Fizz', '4', 'Buzz', 'Fizz', '7', '8', 'Fizz', 'Buzz', '11', 'Fizz', '13', '14', 'Fizz Buzz', '16', '17', 'Fizz', '19', 'Buzz'] ... ok
fizzbuzz.php: case3, stdin: ['3'], stdout: ['1', '2', 'Fuga'] ... ng
[actual]
1
2
Fizz
テストケース1, 2はちゃんと通ってるのでひと安心ですね☺️
テストケース3はあえて落ちるようにしています。
テストが通った場合は ok
テストが通らなかった場合は ng
と実行結果を返すようにしてます。
これでPaizaの問題を解くときの動作確認がラクになるかなと思います!
もしよかったら使ってみてください!