In this article I introduce the topic of Acceptance Testing (aka Functional Testing), something more PHP programmers should be starting to practice. I'm sure many of us are well aware of Unit Testing and even Integration Testing so where does this third wheel come into play for web applications given our growing obsession with Web 2.0 and AJAX and how does it differ from the former two practices? Below I'll explain this. I will also introduce how to implement Acceptance Testing using the killer combination of PHPUnit and Selenium.
Why Acceptance Tests?
Acceptance Testing is a high level testing procedure which ensures that an application behaves as expected by the client (whomever commissioned the application). Now that a definition is out of the way...
It's sometimes difficult to see where Acceptance Testing fits into our testing practices. Some people will find it suspiciously similar (if not identical) to Integration Testing. Integration Testing is another high level testing process, distinct from Unit Testing, which verifies that high level components of the application work as expected. As with Unit Tests, this testing usually takes place isolated from the rest of the code using mocks, stubs and specialised test classes.
The primary differences from Acceptance Testing are pretty simple. Integration tests operate similarly to Unit Tests but on groups of associated classes. The group concept is essential. We're not testing each class in isolation as with Unit Tests, but groups of classes which together generate a desired result. Integration Tests are therefore written by and for programmers. On the other hand, Acceptance Tests operate on a fully integrated application (no isolation of classes/components) normally testing against the user interface, whether HTML for browsers or the XML/JSON response from web services. They are written to assure an application performs its purpose as defined by the client. In fact, the client may even be responsible for writing the tests!
Now that we know Acceptance Testing is a distinct practice, why should we do it?
1. It captures the expectations of the client (and not the developer!)
2. It measures when functionality valued by the client is complete (a "knowing when to stop" signal)
3. It ensures future behaviour which deviates from the expected is quickly identified (regression testing)
4. Like any good set of tests they support refactoring in the same way as Unit and Integration Tests
Of User Stories and Acceptance Tests
For those who practice Extreme Programming, Acceptance Tests are normally written to verify that the implementation of a "User Story" is complete and stays that way over time. If you recall, I noted that Acceptance Tests are client driven to the point where they may even write them.
A User Story is a short description written by the client about some unit of valuable functionality the completed application will offer, i.e. the expected behaviour Acceptance Tests should verify. In a sense, XP replaces the traditional reliance on specification which tries to plan every step of development at the cost of rigidity with a more flexible approach where collections of User Stories track current requirements, and are maintained under the assumption that any release plans based on them are subject to frequent change.
Let's take a quick foray into writing User Stories.
A customer may login to a personal account.
As PHP developers some of us may view this as a stupidly obvious requirement. Try not to. To the client this is a valuable feature. So stifle those giggles! Assuming any continuing discussion with the client raises no further changes to this User Story we can preempt any coding with a set of Acceptance Tests. The process likely sounds familiar to Unit Testers - test first, code later. You measure the success of an implementation based on whether it passes all its pre-written Acceptance Tests (in our case these test the web interface). Here, the client has put in some thought and after a discussion with the programmers written up the following tests.
1. Login page displays a login form
2. Submitting a valid identity/password combination results in a successful login.
3. Submitting an invalid identity/password combination redisplays the login form with errors.
4. The login form is always accompanied with a "Forgot identity or password?" hyperlink.
Pretty straight forward, right? As chance would have it, the fourth Acceptance Test is also a second User Story, an extra nugget of valuable functionality the client came up with after going through their conditions of success.
A customer may retrieve a forgotten password or identity.
If each of the four Acceptance Tests pass we can assume the two User Stories have been completed. Also, when the Acceptance Tests are passed it's a clear signal that it's time to stop programming for this feature. Unless the client comes up with new User Stories (or elaborations thereof) there's no point throwing more resources at it. It's done! Move on!
The Iteration Plan
Since our crack team of PHP code monkeys is at the forefront of Extreme Programming they have also been busy assigning User Stories to a specific "Iteration". An Iteration, in broad terms, is a fixed period of development at the end of which we should have a fully tested, working (albeit incomplete) version of the application which will pass all Acceptance Tests for all User Stories assigned to that Iteration. I'm sure many of you have bumped into the Iteration Programming ideal before. An Iteration is typically no more than a few weeks long. Considering a project may take months there are going to be many Iterations on the release schedule, each building on the other.
Back to our example. The login User Stories have been assigned to Iteration #1. Secure that it's time to implement the Stories, you whip out your favourite IDE/editor and set to. Next step? Write the Acceptance Tests in a form where automated testing is possible.