Using BDD Language In Your Tests
I wanted to write this piece based off an Uncon Session that I gave at TrueNorth PHP in Toronto, well yesterday actually. To start, this concept is not an alternative Behavior Driven Development; if you need to use Behat or Mink then you should. This concept focuses around the idea of using the same language you might use in a Behavioral Test, to document your unit tests and make them more valuable to you or your organization.
What is behavior driven development?
Behavior driven development is the use of language that is commonly used by all stakeholders to describe the behavior of a feature or user interaction. One of the huge benefits to using language like this is that people with varying technical skills and abilities can operate on the same information. Marketing, sales and development have different needs for that information; having it simplified allows each department to then transform to fit their departmental needs.
Let's be honest, business requirements rarely make sense and transforming business requirements that don't make sense is very difficult to do. The frustrating part is that ultimately, the pressure is on developers to deliver; because we deliver functionality. Frustration creeps in when all the stakeholders have a different understand of the feature and how it's supposed to function. Nothing is more embarrassing for a company than when the communicated expectations, the marketing material and the actual system are all different.
Use the language
Now that I've droned on and on about how business requirements usually suck, lets fix it! Remember now, we're interested in testing this stuff on the unit level so the full user stories still need to be broken down to that level; but we can't do that until we understand the big picture. So how do we start?
- State your assumptions - we do this using 'Given', Given that we're provided a valid number, Given that the username and password are correct or Given that the username and password are incorrect. These are examples of assumptions that can be made. When we're on the unit level, a good template to use is this: Given that (provided parameter) is (what you are trying to test (valid/invalid/numeric/alpha-numeric/etc))
- Trigger your scenario - we do this using 'when' to describe the trigger. Given that the form is provided a valid username and password; when the 'Login' button is clicked...you can read that sentence and have a good expectation about how that sentence is going to end.
- Describe what's supposed to happen - finally we finish it up with then and it flows nicely. This is what we have, this what we're doing and this is the expected result, doesn't get much more straight forward than that! 'Then the user is logged in successfully'
- Given that the form is provided a valid username and password; when the Login button is clicked; then the user is logged in successfully
We're not trying to do the work of Behat with PHPUnit, we're not actually trying to do BDD. That was a point that I don't feel like I communicated very well during my presentation, this isn't in the place of or trying to replicate BDD. We're still unit testing, but we're just describing how we're going to do it in a doc block. We're trying to write succinct, effective code; our tests should be focused, succinct. We're trying to write classes and methods with single responsibilities; so our tests shouldn't try to test too much either. I know there are other developers like me, if I sit down and start writing code without a plan; it ends up not being very good. If I don't understanding what I'm supposed to be doing, I can't do it.
The benefits I've gained from doing my tests this way handle both of those scenarios. If I have a "story" for my unit of code and I'm unclear; I can take that story to the people gathering requirements and get useful feedback very quickly. There's not a barrier of limited understanding, so it works. Once I have that story right, I know what each test method is supposed to do. It's really, really easy to notice when something is trying to do too much in your spoken language; for example: "Given that we have a valid username and password; when we click the login button and then get to the homepage and click on admin link; then we should be able to click on the add post button, write a post and save it" - is a story that's trying to do way too much in a single test. This may be similar to something we get early in the proces that needs to be distilled into smaller functional chunks.
We do this so:
- We can keep our tests focused
- We can translate business requirements into technical requirements
- Our tests don't do too much
- We can get misunderstandings clarified quickly, because we're using English (or whatever you speak) and not tech-nese.
I know that testing in the PHP world hasn't fully caught on quite yet, but these problems in communication are common to the process of software development. Communicating efficiently allows us to write efficient code and deliver what everyone is expecting us to deliver. Everybody wins!