狠狠撸

狠狠撸Share a Scribd company logo
Prophecyを使った
ユニットテスト
笔贬笔カンファレンス北海道2016
2016.4.16
自己紹介
? 石田朗雄(@iakio)
? フリーランス
? 近所から来ました
? OSC2015-Hokkaidoで
「phpspecで学ぶLondon-School-TDD」
という発表をしました
Motivation
? 布教活動
? Prophecy, PhpSpec, Behatの作者@everzet氏のファン
? PHPの偉い人でもTDDの偉い人でもありません
? あと特別英語が得意なわけでもないので独自解釈が含まれてい
る可能性があります
? 今日の話に興味を持った方はぜひ@everzet氏のプレゼンテー
ションやBlogをチェックしてみてください
Agenda
Prophecyとは、PhpSpecのために開発され、PHPUnitにもバンドル
されるようになったMocking Frameworkです
? Mocking Frameworkとは
? 笔谤辞辫丑别肠测の使い方
? 他のMocking Frameworkとの違いや注意点
すこしずつ前に進む
? 俺が今書いたこのコード本当に動くんだろうか
? 自信をもって前に進むために
? 今书いたコードを今すぐ动かすためにはどうしたらいいだろう
例えば単に一部を別ファイルにコピペして動か
すことができるのであれば、それも立派な戦略
だが
問題点
? コピペが面倒
? 未定義の変数を使っている
? しかもメソッド呼んじゃってる
今日の話はこれらを解決するもうちょっといい方法、みたいな
感じです
TDD is not about Testing
Test Driven Development
テストによって駆動される開発手法
開発を駆動するためにテストを使う
? テスト駆動開発?開発手法の話
? 良いテストとは?
? カバレッジは?
? これらはテスト手法の話なので別のレイヤの話
ドライブを感じるとき
? 難しい問題、複雑な問題に取り組んでいるとき
? ?どんなクラスを作ろう
? ?どんなメソッドを作ろう
? ?どんな引数を渡そう
ぼんやり?くっきり
? こんなクラスがあればいいな
? こんなメソッドがあればいいな
? こんな引数をわたせばいいな
? そのオブジェクトを使う最初のコードを書いてみる(実装する
前に)
? この試し書きをする場所をユニットテストと呼んでいる
依存関係
? Bが無いとAを作れない
? CとDが無いとBを作れない
? どのように単体テストを行う
か
class A {
function __construct(B $b) {
}
}
class B {
function __construct(C $c, D $d) {
}
}
class C {
}
class D {
}
class B {
function __construct(C $c, D $d) {
}
}
class C {
}
class D {
}
class A {
function __construct(B $b) {
}
}
Outside-In/Inside-Out
? Outside-In
? Bをダミー(Mock, Stub)に置き換
えてAをテストする
? AがBに要求しているものが明確に
なる
? Inside-Out
? C,Dを先に実装してからBを実装す
る
? テストの粒度は次第に大きくなる
? ダミーを実装する手間はかからな
い
? Bを実装中はAが存在しないので、
AがBに何を要求するかわからない
Mockとは
? ユニットテストを書くとき、テスト対象が依存しているオブ
ジェクトの代わりとなるオブジェクトをTest Doubleという
? Test DoubleにはDummy, Fake Object, Stub, Mock, Spyなどがある
(この違いは今日は説明しません)
? これらをサポートする仕組みがMocking Framework
例
Markdown::outputHtml($markdown, $writer)
MarkdownをHTMLに変換し、ファイル等に出力するメソッド
Writer::writeText($text)を呼び出す
説明のために先に実装コードをお見せします
class Markdown {
public function outputHtml($markdown, $writer) {
// $writer->writeText(
// MarkdownをHTMLに変換したもの
// );
}
}
class Writer {
public function writeText($text) {
// 何かする
}
}
src/Markdown.php
src/Writer.php
<?php
class MarkdownTest extends PHPUnit_Framework_TestCase
{
/** @test */
public function 変換したhtmlを出力できること()
{
$writer = ???;
// $writer->writeText('<p>Hi, there</p>') が
// 呼ばれること
$markdown = new Markdown();
$markdown->outputHtml('Hi, there', $writer);
}
}
tests/MarkdownTest.php
まず試し書き
<?php
class MarkdownTest extends PHPUnit_Framework_TestCase
{
/** @test */
public function 変換したhtmlを出力できること()
{
$writer = ???;
// $writer->writeText('<p>Hi, there</p>') が
// 呼ばれること
$markdown = new Markdown();
$markdown->outputHtml('Hi, there', $writer);
}
}
tests/MarkdownTest.php
class FakeWriter {
public function writeText($text) {
$this->text = $text;
}
}
Mocking Frameworkを使わずに書く
引数に何が渡されたかを後で調べるために保存
しておく
$writer = new FakeWriter();
$markdown = new Markdown();
$markdown->outputHtml('Hi, there',$writer);
$this->assertEquals(
'<p>Hi, there</p>'
$writer->text
);
tests/MarkdownTest.php
これはこれで場合によっては十分有効
笔谤辞辫丑别肠测の使い方
$writer = ???;
// $writer->writeText('<p>Hi, there</p>') が
// 呼ばれること
$markdown = new Markdown();
$markdown->outputHtml('Hi, there', $writer);
tests/MarkdownTest.php
$writer = $this->prophesize('Writer');
$writer->writeText('<p>Hi, there</p>')
->shouldBeCalled();
$markdown = new Markdown();
$markdown->outputHtml('Hi, there', $writer->reveal());
tests/MarkdownTest.php
$writer = $this->prophesize('Writer');
$writer->writeText('<p>Hi, there</p>')
->shouldBeCalled();
$markdown = new Markdown();
$markdown->outputHtml('Hi, there', $writer->reveal());
tests/MarkdownTest.php
prophesize()メソッドで、Writerの振る舞いを記
述するためのオブジェクトを作る
$writer = $this->prophesize('Writer');
$writer->writeText('<p>Hi, there</p>')
->shouldBeCalled();
$markdown = new Markdown();
$markdown->outputHtml('Hi, there', $writer->reveal());
$writer->writeText()というメソッドを呼び出して
いるように見えますがそうではなく
Mockに期待する振る舞いを定義しています
tests/MarkdownTest.php
$writer = $this->prophesize('Writer');
$writer->writeText('<p>Hi, there</p>')
->shouldBeCalled();
$markdown = new Markdown();
$markdown->outputHtml('Hi, there', $writer->reveal());
tests/MarkdownTest.php
reveal()メソッドでモックを取り出す
$ vendor/bin/phpunit tests/MarkdownTest.php
F 1 / 1 (100%)
Time: 85 ms, Memory: 4.00Mb
There was 1 failure:
1) MarkdownTest::test_変換したhtmlを出力できること
Some predictions failed:
Double?Writer?P1:
No calls have been made that match:
Double?Writer?P1->writeText(exact("<p>Hi, there</p>"))
but expected at least one.
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
<?php
class Markdown {
public function outputHtml($markdown, Writer $writer) {
$writer->writeText(
'<p>'.htmlspecialchars($markdown).'</p>'
);
}
}
src/Markdown.php
$ vendor/bin/phpunit tests/MarkdownTest.php
.
1 / 1 (100%)
Time: 77 ms, Memory: 4.00Mb
OK (1 test, 1 assertion)
Mockeryとの違い
? パーシャルモックやstatic methodのモックなどの機能は無い
? Writerクラスまたはインターフェースが存在しないとpassしな
い
? 上記にwriteText()メソッドが存在しないとpassしない
$writer = m::mock('Writer');
$writer->shouldReceive('writeText')
->with('<p>Hi, there</p>')
->once();
$markdown = new Markdown();
$markdown->outputHtml('Hi, there', $writer);
Mockeryだとこんな感じ
There was 1 error:
1) MarkdownTest::test_変換したhtmlを出力できること
Prophecy?Exception?Doubler?MethodNotFoundException:
Method `Double?stdClass?P1::writeText()` is not defined.
There was 1 error:
1) MarkdownTest::test_変換したhtmlを出力できること
Prophecy?Exception?Doubler?MethodNotFoundException:
Method `Double?Writer?P1::writeText()` is not defined.
Writerクラスが無い場合
writeTextメソッドが無い場合
“Do not use broken mocking tools
? モックと実装の食い違いを指摘できないMocking Frameworkは
壊れてるから使うな
? Mockと実装との食い違いを防ぐことができる
? テストがパスしたときには、Writeクラス(or インターフェー
ス)のひな形が出来上がっている
? 次にすべきことが明確になる
? 「先にWriterを作っておかなければならない」ではなく「テス
トに言われたからWriterを作る」と思った方がいいと思います
? 一方で、Partial Mockのような機能はないので、レガシーコード
のテストを書くときはMockeryの方が便利かもしれません
さいごに
? 少しずつ前に進む
? 小さな部品を組み合わせて大きなプログラムを作る
? そのための手段としてのMockとユニットテスト
? 何故なら、プログラミングは未知との遭遇の連続だから
? あと、興味を持った方は ”Design How Your Objects Talk Through
Mocking at Laracon EU 2014 ” をCheck
参考資料
? GitHub - phpspec/prophecy: Highly opinionated mocking framework
for PHP 5.3+
https://github.com/phpspec/prophecy
? thePHP.cc - PHPUnit 4.5 and Prophecy
https://thephp.cc/news/2015/02/phpunit-4-5-and-prophecy
? Design how your objects talk through mocking at Laracon EU 2014
? http://www.slideshare.net/everzet/design-how-your-objects-talk-through-
mocking
? https://www.youtube.com/watch?v=X6y-OyMPqfw
? Design How Your Objects Talk Through Mocking"を見た - iakioの日
記
http://iakio.hatenablog.com/entry/2014/10/06/000000
ご清聴
ありがとうございました

More Related Content

笔谤辞辫丑别肠测を使ったユニットテスト