New Fashioned Classics: BDD Specs in PHPUnit
Published on October 04, 2013
One of the most important requirements for tests is maintainability.
The tests can live in a project for a months or even years. One day it may happen that some old tests start failing and the team got no idea about what the test does.
Does this test checks something important? Maybe specification has changed and the test should be rewritten?
The team who worked on tests that early days wrote them only to make them pass. Team is not sure what was the purpose of test.
In such situations it would have been nice if a developer who wrote the test at least has left some comments. But not. Usually no one documents tests. Test passes and developer is completely satisfied with that fact.
Proper test structure and readability is the only way to maintainable tests. Tests must not turn to a legacy code. Is there a way to write better tests?
Even if we had such BDD framework none will ever adopt it as we have PHPUnit for all kind of testing in PHP. We won’t switch PHPUnit in favor of some geeky BDD tool. But actually to write BDD-styled tests, inspired by Jasmine we don’t need to do dramatic changes.
We can use Specify, a simple trait inserted into your PHPUnit’s TestCase that allows you to store several specifications in a test and write them in BDD way.
At first we will write down some specifications in a body of typical PHPUnit’s test:
Pretty sweet, we started with describing things before the test. But can’t we do the same with comments?
specify method is much better then comments as it introduces code blocks into PHPUnit.
To see it in action, let’s write the tests.
This code blocks will be executed inside the same test. But it’s important to notice that each code block is isolated, thus, when an assertion inside a block fails, the test won’t stop the execution. That is how the
specify is different from comments.
Now we’ve got a list of specification and code examples for each case. If one day our site will allow micro-blogging, we can easily find
post should contain a body specification and remove it. That’s pretty flexible, thus maintainable.
Please note, that all the specification are grouped by context. In plain PHPUnit you would create each code block as a separate method. This way it’s pretty hard to all the tests related to one specific feature, in our case - publishing.
Ok, we got nice specifications. But can we also replace classical asserts with some more BDD stuff? Sure. We have another tiny package Verify which is also inspired by Jasmine.
Assert keyword is replaced either with
expect (as Jasmine does) or
verify. This asserts change the order of assetion to improve readability.
Let’s rewrite our test with Verify so you could feel the difference.
Basically it’s a deal of habit. The
expect(XX)->toBe(YY) style is more natural for reading as you read from left to right. But If you got used to
assertXXX syntax (where the code is read from right), you can skip Verify library.
With just a few tiny libraries we converted a classical flat PHPUnit test into separate code blocks driven by specification. Test looks very similar to what we saw in Jasmine, just as we intended.
And the piece of advice to your team: introduce a rule “no assertion without specification” to make tests readable and easy to maintain.
You can install Specify and Verify via Composer:
specify codeblocks, just add
use Codeception\Specify into any TestCase file.
BDD styled assertions with
expect keywords are installed automatically.
Hint: Isolated codeblocks are especially useful for testing exceptions.