PHP, Zend Framework and Other Crazy Stuff
Archive for November, 2007
PHPSpec Reporting Gets A Needed Boost
Nov 14th
PHPSpec is closing in on its first stable release, so the time had finally come to spruce up its output! No more the simple reporting of failed specs – now you get a few more details in a readable format, exceptions and errors even come with traces. In addition, I’ve implemented specdoc output as an option (using “-s”) so you can get a list of specs in their plain text form.
I’ve pasted in a copy of the output from a real PHPSpec run and added a few issues to show how they are output. I’ve been writing some experimental source code for a possible Mock Object framework in PHP (see last blog entry), so I used PHPSpec to apply Behaviour-Driven Development in its development. You can read the actual spec files over at http://phpmock.googlecode.com/svn/branches/padraic/specs/.
PHPSpec development snapshots have been updated on http://dev.phpspec.org, which also hosts the manual being written (HTML).
$ phpspec -rs
EE..................................................P..
Finished in 0.334152936935 seconds
p h p mock method expectations
-should expect each method exactly once unless otherwise specified (ERROR)
-should throw default exception if method call count is unexpected (EXCEPTION)
-should expect number of methods in times term
-should return self from times term invocation
-should set return value in terms and default to returning value for all calls
-should return values in order of setting but return last value remaining on other calls
-should return self from return term invocation
-should set expected args using with term
-should return self from with term invocation
-should allow no calls for zeroormoretimes term
-should allow one call with once term
-should throw exception if less than once using once tern
-should throw exception if greater than once using once term
-should return self instance from once term
-should allow two calls with twice term
-should throw exception if less than twice using twice term
-should throw exception if greater than twice using twice term
-should return self instance from twice term
-should allow no calls with never term
-should throw exception if any call after never term
-should return self instance from never term
-should allow any calls for zeroormoretimes term
-should allow zero calls for zeroormoretimes term
-should return self instance from zeroormoretimes term
-should set times minimum with once using atleast term
-should set times minimum with times using atleast term
-should throw exception if less than minimum using atleast term
-should return self instance from atleast term
-should set times maximum with once using atmost term
-should set times maximum with times using atmost term
-should throw exception if more than twice using twice term
-should return self instance from atmost term
-should allow any args using withanyargs term
-should return self instance from withanyargs term
-should disallow args using withnoargs term
-should return self instance from withnoargs term
-should obey ordering via sequence of ordered term calls
-should disallow method calling if method has specified order
-should allow method calling of unordered expectations in any order
-should return self instance from ordered term
-should throw specified exception using andthrow term
-should throw specified exception with message using andthrow term
-should throw exception if class passed to andthrow term not an exception
-should return self instance from andthrow term
p h p mock
-should create mock inheriting class type from original
-should create mock inheriting type if original an interface
-should create mock with specified name
-should create stub from an array of methods and return values
-should throw exception if class is final
-should throw exception if class does not exist
-should not throw class not exists exception if an interface
-should mock all allowed public methods which do nothing since mocked
-should mock public static methods which do nothing since mocked (PENDING)
-should retain method parameter definitions in mocks
-should return true on validation by default if no expectations set
55 examples, 0 failures, 1 error, 1 exception, 1 pending
Errors:
1)
'p h p mock method expectations should expect each method exactly once unless otherwise specified' ERROR
exception 'PHPSpec_Runner_ErrorException' with message 'more bad stuff happenin'' in /user/opt/projects/phpmock/specs/PHPMockMethodExpectationsSpec.php:19
Stack trace:
#0 [internal function]: PHPSpec_ErrorHandler(1024, 'more bad stuff ...', '/user/opt/...', 19, Array)
#1 /user/opt/projects/phpmock/specs/PHPMockMethodExpectationsSpec.php(19): trigger_error('more bad stuff ...')
#2 /user/opt/projects/phpspec/trunk/src/PHPSpec/Runner/Example.php(81): DescribePHPMockMethodExpectations->itShouldExpectEachMethodExactlyOnceUnlessOtherwiseSpecified()
#3 /user/opt/projects/phpspec/trunk/src/PHPSpec/Runner/Collection.php(73): PHPSpec_Runner_Example->execute()
#4 /user/opt/projects/phpspec/trunk/src/PHPSpec/Runner/Base.php(51): PHPSpec_Runner_Collection->execute(Object(PHPSpec_Runner_Result))
#5 /user/opt/projects/phpspec/trunk/src/PHPSpec/Runner/Base.php(44): PHPSpec_Runner_Base->executeExamples()
#6 /user/opt/projects/phpspec/trunk/src/PHPSpec/Console/Command.php(82): PHPSpec_Runner_Base::execute(Object(PHPSpec_Runner_Collection), Object(PHPSpec_Ru
nner_Result))
#7 /user/opt/projects/phpspec/trunk/src/PHPSpec/Console/Command.php(125): PHPSpec_Console_Command::main()
#8 {main}
Exceptions:
1)
'p h p mock method expectations should throw default exception if method call count is unexpected' EXCEPTION
exception 'PHPSpec_Exception' with message 'no specification object created yet' in /user/opt/projects/phpspec/trunk/src/PHPSpec/Context.php:92
Stack trace:
#0 /user/opt/projects/phpspec/trunk/src/PHPSpec/Runner/Example.php(89): PHPSpec_Context->getCurrentSpecification()
#1 /user/opt/projects/phpspec/trunk/src/PHPSpec/Runner/Collection.php(73): PHPSpec_Runner_Example->execute()
#2 /user/opt/projects/phpspec/trunk/src/PHPSpec/Runner/Base.php(51): PHPSp
ec_Runner_Collection->execute(Object(PHPSpec_Runner_Result))
#3 /user/opt/projects/phpspec/trunk/src/PHPSpec/Runner/Base.php(44): PHPSpec_Runner_Base->executeExamples()
#4 /user/opt/projects/phpspec/trunk/src/PHPSpec/Console/Command.php(82): PHPSpec_Runner_Base::execute(Object(PHPSpec_Runner_Collection), Object(PHPSpec_Runner_Result))
#5 /user/opt/projects/phpspec/trunk/src/PHPSpec/Console/Command.php(125): PHPSpec_Console_Command::main()
#6 {main}
Pending:
1)
'p h p mock should mock public static methods which do nothing since mocked' PENDING
Static methods will need static handling
Mocks, Stubs, And SimpleTest Wins
Nov 10th
The word is that SimpleTest is moving towards PHP5 in the near future which is great news for all Mockists in PHP. When I moved to PHP, SimpleTest became the main ingredient in many a coding session up to 2 in the morning but once PHP5 gained traction and I was seduced into leaving PHP4 behind I found myself relying more heavily on PHPUnit. Not that SimpleTest is anti-PHP5 in any way (only some small things and obviously E_STRICT giving it a heart attack), but more projects I didn’t control leaned heavily towards PHPUnit once PHP5 took off.
Why the noting of SimpleTest? Because it’s a Mockist library. Between PHPUnit and SimpleTest, ST under Marcus Baker’s leadership stomps all over PHPUnit for Mock Object support, and has done for years. PHPUnit is a great Unit Testing library, but I just don’t like it when applying TDD (or even some limited BDD before recently). The SimpleTest documentation (which is itself deceptively simple) is basically a TDD tutorial which runs you through basic Unit Testing, how test-first approaches lead to better design decisions, and how Mocks and Stubs make designing even better by exploring the interactions between objects (i.e. SimpleTest’s introduction to TDD is behaviour driven) and server resources.
PHPUnit has always seemed to attract people who prefer to test state, or use Stubs predominantly (hand coded objects with fixed return values), or at a minimum those who can endure the lack of Mock Objects in their lives. I prefer none of these – being behaviour focused. I find it really hard to work with a Unit Testing library that first spent years ignoring Mock Object support, and which then bundles one which is a) a bit foggy, and b) rarely used or highlighted anywhere, and c) useful mainly on a hit or miss basis (some nagging missing features).
Is this short sightedness on my part? Maybe it’s just a matter of development styles? I am afterall relatively new to PHPUnit – SimpleTest has served all my needs previously since time began. What does the atypical PHPUnit user do though when they need a Mock Object or Stub? Hand code it???
I’ve become more interested in Mock Object frameworks recently, mainly to see the broad range available (count them for Java or Ruby and you get quite a collection) before dabbling in one for PHPSpec on the premise a loosely coupled Mock Object framework has value beyond PHPSpec (which I’m sure it does – phpt + mocks would be interesting, but other uses suggest themselves). PHPSpec + PHPMock = BDD At Last.
My vision is still pretty SimpleTest tinted. Noting this is only one part of PHPUnit I’m bashing, but the PHPUnit MO API leaves too much to be desired. It looks quite cute – PHP5, fluent interface, etc., all fluffy and friendly – but the fluent interface doesn’t flow naturally, and it seems fickle to a fault. Surely my syntax opinions are questionable, but I just like to have one that doesn’t need to be memorised so precisely and flows more intuitively. Handling of indexed method calls with parameters would also be nice – nearly everything I do with Mocks/Stubs needs it at some point. Dropping the fluent interface entirely might actually be an improvement until (if) it’s changed.
Is syntax really that important? I know Rubyists would have fun debating the benefits of Mocha vs Flexmock vs rspec mocks. Nearly the whole argument ends up being a syntax choice coloured by your approach to TDD or BDD. Some like to use real objects, others stubs, others mocks, and others even partial-objects that are more real than stubs but not production quality. Martin Fowler even has a wonderously clarifying post on Mock Objects and Stubs, and the kind of people who prefer each. Going by it, SimpleTesters who refuse to get with the popular program of PHPUnit (lots of them about still
– many live over on Devnetwork) seem to more likely be inclined towards the BDD-related style of TDD – focusing on behaviour and object interactions over state and simple verification.
I mean here’s a quick thought. PHPUnit’s standard Mock Object example:
[geshi lang=php]$observer = $this->getMock(‘Observer’, array(‘update’));
$observer->expects($this->once())
->method(‘update’)
->with($this->equalTo(‘something’));[/geshi]
Seriously – this is just horrible! What about:
[geshi lang=php]$observer = $this->getStub(‘Observer’, array(‘update’));
$observer->shouldReceive(‘update’)->once()->with(‘something’);[/geshi]
Anyone see a difference? Obviously, with all my moaning above I prefer the second. It’s simpler, less typing, reads like plain English (I seem to note that a lot recently
), does the same thing, and just feels cleaner. I used Stub since it’s slightly more accurate for what this is doing – a canned response from an otherwise real object. SimpleTest refers to these as Partial Mocks; respects to Marcus but I still consider them Stubs with partial Mock behaviour – just to be vague, difficult and convoluted
.
Personally though, this is all enough to convince me SimpleTest remains the Mockist’s hero. PHPUnit is all shiny and bespeckled, but it’s just not working for me since I rely heavily on Mock Objects and really really don’t like writing Stubs (except for server/slow resources) if I can help it.
