I'm always looking for ways to make my code better. One of the things I fully believe in is that everytime you make something you should be learning something while doing it. So, when I sat down to rewrite this site, I wanted to learn how to use Behaviour Driven Development (BDD) to make the site do what I wanted it to do.

The key difference, in my eyes at least, between BDD and TDD (Test Driven Development) is that we describe how we want to do something - create a blog post, sort out tagging, log in etc - instead of testing the mechanisms that actually do that sort of thing individually.

It's quite beautiful to behold when you compare the test suite to my personal site compared to the test suite used elsewhere. Compare the following

Feature: Login

  As a user
  I can log in
  To update the site

  Background: There is a user
    Given there is a user

  Scenario: I can log in with valid information
    Given I am on "/login"
    And I fill in my login details
    Then I should see a flash message "Login successful!"
    And I should be logged in

  Scenario: I can't log in without valid information
    Given I am on "/login"
    And I fill in invalid login details
    Then I should not be logged in
    And I should be on "/login"
    And I should see a flash message "Incorrect username/password"

  Scenario: I can log out
    Given I am logged in
    And I am on "/logout"
    Then I should not be logged in
    And I should see a flash message "You are now logged out"

To something like:

class HelpersTest extends BootstrapperWrapper
{

    public function testOutputCorrectCSSLinks() {
        $css = Helpers::get_CSS();

        $this->assertEquals("<link rel='stylesheet' href='//netdna.bootstrapcdn.com/bootstrap/3.1.0/css/bootstrap.min.css'><link rel='stylesheet' href='//netdna.bootstrapcdn.com/bootstrap/3.1.0/css/bootstrap-theme.min.css'>", $css);
    }

    public function testOutputCorrectJSLinks() {
        $js = Helpers::get_JS();

        $this->assertEquals("<script src='http://code.jquery.com/jquery-2.1.0.min.js'></script><script src='//netdna.bootstrapcdn.com/bootstrap/3.1.0/js/bootstrap.min.js'></script>", $js);
    }

}

Now obviously, this sort of thing isn't perfect. I'd much rather use unit tests and TDD when I work with the Bootstrapper package. But on the other hand, I'm building a website. I'm making something which people need to digest, that people need to interact with. A unit test doesn't really make that sort of thing work.

Behat

So, while I've been working with BDD I've been using a package called Behat. It was built to provide PHP developers the same sort of thing that Cucumber provides Ruby developers*. It means that when I think about the new feature I want to implement, I can sit down and think "Okay, how do I actually want this to work?".

Why is this important? It starts you thinking in a much cleaner way that just doing a unit test. Every step that you define is there because an actual user told you that's how they wanted it to work. Some of the features I've defined and the steps that I follow are because I know who the user is (me), and I feel confident that I can say "I'm going to go to /blog/create" because that's what I would do automatically - I'm confident in my abilities to play about with URIs.

It makes a big difference from a coding perspective. It causes a couple of problems when you start out - if creating a blog post fails, I actually don't know if the problem was because my validator failed or the storage method failed or any other myriad of things - but I'm now confident that I built something usable.

Tips and tricks

So, here's a couple of tips and tricks I'd suggest using. These are mostly Laravel specific, but you can probably chop and change things if you're using something else.

  • Import your MVC stack (or at the very least, your Composer Autoload file). For Laravel apps, you need to add something akin to require_once __DIR__ . "/../../../../bootstrap/start.php"; (assuming that you have Behat features inside app/tests/acceptance). Why? Because then you can all sorts of sugar that make your features run a lot faster.
  • Use the @BeforeFeature and @AfterFeature annotations to clear the database or whatever before and after each test. I would suggest using transactions, but I've not found it to work while I'm using Behat. I've been using Artisan::call("migrate:refresh") to rollback all the migrations and then re-run them. If you work out how to get transactions to work properly then do send me a message.
  • Use things like Faker to speed up your features. The first version of my features took about 2 minutes to run. That is awful in so many ways. The current version takes 30 seconds to run. It's not perfect, but it works for now. Using steps like Given there is a blog post with title "Editing Test" and content "I made a boo boo" and creating the post directly inside the database is much faster than using a step like Given I create a blog post with title "Foo" and content "Baring all the baz" since that'll have to do the entire request process.

* Side note: You can actually use Cucumber with anything. The problem is that the step definitions - the bit that turns I fill in my login information into actual steps the program can follow - have to be written in Ruby. I like Ruby, but I also like staying in the same language from minute to minute if at all possible.