proposal for xspep bdd framework for php
DESCRIPTION
TRANSCRIPT
Proposal for xSpec BDD Framework
for PHPBeyond PHPUnit and PHPSpec
@yuya_takeyama
Recent Activities•Making DSL with [] (#phpcon2011 LT)
http://blog.yuyat.jp/archives/1355
• PHPUnit でよりよくテストを書くためにhttp://blog.yuyat.jp/archives/1386
• phpenv で複数の PHP 環境を管理するhttp://blog.yuyat.jp/archives/1446
• Building Development Environmentwith php-build and phpenvhttp://blog.yuyat.jp/archives/1461
•例えば, Singleton を避ける (New! #TddAdventJp)
http://blog.yuyat.jp/archives/1500
xSpec?
xUnit?
xUnit (Unit Testing Frameworks)
•SUnit (Smalltalk)
•JUnit (Java)•Test::Unit (Ruby)•PHPUnit (PHP)
xSpec?reprise
xSpec (Testing Frameworks for BDD)
•RSpec (Ruby)•Jasmine (JavaScript)
•specs (Scala)•PHPSpec (PHP)
BDD?
BDD (Behaviour Driven Development)
•Not test, specify!•“It should ...”•Expressive•Test as Documentation
RSpec exampledescribe Bowling do describe '#score' do context 'all gutter game' do before do @bowling = Bowling.new 20.times { @bowling.hit(0) } end
it 'should equal 0' do @bowling.score.should eq(0) end end endend
RSpec exampledescribe Bowling do describe '#score' do context 'all gutter game' do before do @bowling = Bowling.new 20.times { @bowling.hit(0) } end
it 'should equal 0' do @bowling.score.should eq(0) end end endend Not Test, Specify
RSpec exampledescribe Bowling do describe '#score' do context 'all gutter game' do before do @bowling = Bowling.new 20.times { @bowling.hit(0) } end
it 'should equal 0' do @bowling.score.should eq(0) end end endend “It should ...”
RSpec exampledescribe Bowling do describe '#score' do context 'all gutter game' do before do @bowling = Bowling.new 20.times { @bowling.hit(0) } end
it 'should equal 0' do @bowling.score.should eq(0) end end endend Expressive
RSpec exampledescribe Bowling do describe '#score' do context 'all gutter game' do before do @bowling = Bowling.new 20.times { @bowling.hit(0) } end
it 'should equal 0' do @bowling.score.should eq(0) end end endend
Test as Documentation
I ❤RSpec
PHPUnit exampleclass BowlingTest extends PHPUnit_Framwork_TestCase{ private $_bowling;
public function setUp() { $this->_bowling = new Bowling; }
public function testScore0ForAllGutterGame() { for ($i = 0; $i <= 20; $i++) { $this->_bowling->hit(0); } $this->assertEquals(0, $this->_bowling->score); }}
PHPSpec exampleclass DescribeBowling extends \PHPSpec\Context{ private $_bowling;
public function before() { $this->_bowling = $this->spec(new Bowling); }
public function itShouldScore0ForAllGutterGame() { for ($i=1; $i<=20; $i++) { $this->_bowling->hit(0); } $this->_bowling->score->should->equal(0); }}
PHPSpec exampleclass DescribeBowling extends \PHPSpec\Context{ private $_bowling;
public function before() { $this->_bowling = $this->spec(new Bowling); }
public function itShouldScore0ForAllGutterGame() { for ($i=1; $i<=20; $i++) { $this->_bowling->hit(0); } $this->_bowling->score->should->equal(0); }} Not Test, Specify
PHPSpec exampleclass DescribeBowling extends \PHPSpec\Context{ private $_bowling;
public function before() { $this->_bowling = $this->spec(new Bowling); }
public function itShouldScore0ForAllGutterGame() { for ($i=1; $i<=20; $i++) { $this->_bowling->hit(0); } $this->_bowling->score->should->equal(0); }} “It should ...”
PHPSpec exampleclass DescribeBowling extends \PHPSpec\Context{ private $_bowling;
public function before() { $this->_bowling = $this->spec(new Bowling); }
public function itShouldScore0ForAllGutterGame() { for ($i=1; $i<=20; $i++) { $this->_bowling->hit(0); } $this->_bowling->score->should->equal(0); }} Expressive
PHPSpec exampleclass DescribeBowling extends \PHPSpec\Context{ private $_bowling;
public function before() { $this->_bowling = $this->spec(new Bowling); }
public function itShouldScore0ForAllGutterGame() { for ($i=1; $i<=20; $i++) { $this->_bowling->hit(0); } $this->_bowling->score->should->equal(0); }}
Test as Documentation
I ❤PHPSpec
I ❤PHPUnit
But...
Issue of PHPUnit/PHPSpec
•No nested context•Specification with camelCase or lower_case_with_underscore
Issue of PHPUnit/PHPSpec
•No nested context•Specification with camelCase or lower_case_with_underscore
I want moreexpressiveness!
Isolved
Speciphy
•Nested context•Specification with string•PHPSpec matchers
https://github.com/yuya-takeyama/Speciphy
Speciphy examplenamespace Speciphy\DSL;
return describe('Bowling', array( describe('->score', array( context('all gutter game', array( 'subject' => function () { $bowling = new Bowling; for ($i = 1; $i <= 20; $i++) { $bowling->hit(0); } return $bowling; },
it('should equal 0', function ($bowling) { $bowling->score->should->equal(0); }), )), )),));
Speciphy examplenamespace Speciphy\DSL;
return describe('Bowling', array( describe('->score', array( context('all gutter game', array( 'subject' => function () { $bowling = new Bowling; for ($i = 1; $i <= 20; $i++) { $bowling->hit(0); } return $bowling; },
it('should equal 0', function ($bowling) { $bowling->score->should->equal(0); }), )), )),));
Not Test, Specify
Speciphy examplenamespace Speciphy\DSL;
return describe('Bowling', array( describe('->score', array( context('all gutter game', array( 'subject' => function () { $bowling = new Bowling; for ($i = 1; $i <= 20; $i++) { $bowling->hit(0); } return $bowling; },
it('should equal 0', function ($bowling) { $bowling->score->should->equal(0); }), )), )),));
“It should...”
Speciphy examplenamespace Speciphy\DSL;
return describe('Bowling', array( describe('->score', array( context('all gutter game', array( 'subject' => function () { $bowling = new Bowling; for ($i = 1; $i <= 20; $i++) { $bowling->hit(0); } return $bowling; },
it('should equal 0', function ($bowling) { $bowling->score->should->equal(0); }), )), )),));
Expressive
Speciphy examplenamespace Speciphy\DSL;
return describe('Bowling', array( describe('->score', array( context('all gutter game', array( 'subject' => function () { $bowling = new Bowling; for ($i = 1; $i <= 20; $i++) { $bowling->hit(0); } return $bowling; },
it('should equal 0', function ($bowling) { $bowling->score->should->equal(0); }), )), )),));
Test as Documentation?
Proposal #1Use functionin namespace,Not methodin class
Speciphy examplenamespace Speciphy\DSL;
return describe('Bowling', array( describe('->score', array( context('all gutter game', array( 'subject' => function () { $bowling = new Bowling; for ($i = 1; $i <= 20; $i++) { $bowling->hit(0); } return $bowling; },
it('should equal 0', function ($bowling) { $bowling->score->should->equal(0); }), )), )),));
\Speciphy\DSL\describe
Speciphy examplenamespace Speciphy\DSL;
return describe('Bowling', array( describe('->score', array( context('all gutter game', array( 'subject' => function () { $bowling = new Bowling; for ($i = 1; $i <= 20; $i++) { $bowling->hit(0); } return $bowling; },
it('should equal 0', function ($bowling) { $bowling->score->should->equal(0); }), )), )),));
\Speciphy\DSL\context
Speciphy examplenamespace Speciphy\DSL;
return describe('Bowling', array( describe('->score', array( context('all gutter game', array( 'subject' => function () { $bowling = new Bowling; for ($i = 1; $i <= 20; $i++) { $bowling->hit(0); } return $bowling; },
it('should equal 0', function ($bowling) { $bowling->score->should->equal(0); }), )), )),));
\Speciphy\DSL\it
Proposal #2
Use arrayto build
nested context.
Speciphy examplenamespace Speciphy\DSL;
return describe('Bowling', array( describe('->score', array( context('all gutter game', array( 'subject' => function () { $bowling = new Bowling; for ($i = 1; $i <= 20; $i++) { $bowling->hit(0); } return $bowling; },
it('should equal 0', function ($bowling) { $bowling->score->should->equal(0); }), )), )),));
Nested context
Array,A Strange
Data Structure
[ "foo", "bar", "hoge" => "piyo"]
array(3) { [0]=> string(3) "foo" [1]=> string(3) "bar" ["hoge"]=> string(4) "piyo"}
Likekeywordarguments
public $id = [ 'int', 'primary' => true, 'serial' => true];public $name = [ 'string', 'required' => true, 'unique' => true];public $birthday = ['date'];
Model Definition
["div", ["ul", ["li", "Foo"] ["li", "Bar"] ["li", "Baz"]]]
Pamlhttps://github.com/yuya-takeyama/php-HTML_Paml
<div> <ul> <li>Foo</li> <li>Bar</li> <li>Baz</li> </ul></div>
HTML Generation
['begin', ['define', 'fib', ['lambda', ['x'], ['if', ['<', ':x', 2], ':x', ['+', ['fib', ['-', ':x', 2]], ['fib', ['-', ':x', 1]]]]]], ['print', 'fib(10) = '], ['println', ['fib', 10]]] => fib(10) = 55
LisPHPhttps://github.com/yuya-takeyama/LisPHP
Speciphy examplenamespace Speciphy\DSL;
return describe('Bowling', array( describe('->score', array( context('all gutter game', array( 'subject' => function () { $bowling = new Bowling; for ($i = 1; $i <= 20; $i++) { $bowling->hit(0); } return $bowling; },
it('should equal 0', function ($bowling) { $bowling->score->should->equal(0); }), )), )),));
$arr[‘subject’]
Speciphy examplenamespace Speciphy\DSL;
return describe('Bowling', array( describe('->score', array( context('all gutter game', array( 'subject' => function () { $bowling = new Bowling; for ($i = 1; $i <= 20; $i++) { $bowling->hit(0); } return $bowling; },
it('should equal 0', function ($bowling) { $bowling->score->should->equal(0); }), )), )),));
$arr[0]
Conclusion•Use function in namespace,Not method in class
•Use array to build nested structure
•Enjoy BDD with xSpec
Questions?or
Claims?