Behat

Behat + FuelPHP = RESTful Testing Happiness

If you’ve been following my recent posts, you know I’ve been working more lately with Behat for resting some REST services. In this post I showed you how to get things set up for some testing. In this post, I’ll show you how to use a custom class that I’ve put together to make a reusable system for testing REST.

For those that want to cut to the chase, I’ve posted some example code to github showing the code for the two different sides of the equation – the Behat testing and the PHP framework side (I went with FuelPHP because I was already familiar with it and it makes RESTful interfaces dead simple). The key is in the FeatureContextRest.php file that uses the Guzzle client to make the HTTP requests over to the framework. It provides some handy contexts that make it easy to create Scenarios.

So, still with me? Good – I’ll show you how to get my sample scripts installed so you can see my example in action. In the repository you’ll find two different directories – “fuel” and “behat”. In each you’ll find some files:

behat

  • features/index.features:
    An example set of Scenarios that use the REST testing contexts to create, find and deleting a “User” from the system
  • features/bootstrap/FeaturesContextRest.php:
    The key to the puzzle, the implementation of some context methods for use in Behat

fuelphp

  • fuelphp/classes:
    These are the example models and controllers you can drop into your Fuel install to have them “magically” work (with any recent version of the framework).

This is assuming you already have Behat installed and working and have set up FuelPHP on a host somwhere. For our example, we’ll just use http://behat-test.localhost:8080” Here’s what to do:

  1. Start by copying over the FeaturesContextRest.php file to the “features/bootstrap” directory of your Behat installation alongside the default FeaturesContext.php.
  2. Copy over the behat.yml configuration file into your testing root (the same level as the “features” directory) and update the base_url value for your hostname.
  3. Take the “classes” directory and copy over its contents to the location of your FuelPHP installation – if you’re familiar with the structure of the framework, this is simple…it just goes in “app/classes”.
  4. You’ll need a database to get this working, so you’ll need to get the FuelPHP ORM stuff configured and working in your install. Be sure to update your config.php to automatically load the “orm” module.
  5. Make a database for the project and use the init.sql to create the “users” table.
  6. To test and be sure our models/controllers are working right, hit your host in a browser, calling the “User” action like: “http://behat-test.localhost:8080/user”. This should call what’s in Controller_User::get_index(). It’ll probably just return an empty result though, since there’s nothing for it to pull.
  7. To use the FeaturesContextRest.php, be sure that you include that file into your default FeaturesContext.php file and that your class “extends FeaturesContextRest”.

So, if all has gone well, you have all the pieces in place to get started. Let’s start with a sample Scenario to show you how it all works:

Scenario: Creating a new User
	Given that I want to make a new "User"
	And that its "name" is "Chris"
	When I request "/user/index.json"
	Then the response is JSON
	And the response has a "userId" property
	And the type of the "userId" property is numeric
	Then the response status code should be 200

If you’ve used Mink in the past, so of this will seem familiar. This doesn’t use Mink, however, and I’ve opted to use Guzzle as the client for the backend to replace some of the browser interactions. Really, we don’t need all of the fancy interface interaction Mink gives, so this streamlined interface is more useful.

In this example, we’re creating a user with a property “name” of “Chris” and sending that off to the “/user/index.json” endpoint. A few tests are included like: checking the response code, seeing if the return value has a “userId” property, etc. If all went well and the user was created (this is a POST, so it calls “post_index” in the controller), this test should pass with flying colors….green, hopefully. 🙂

The key is in the phrases “to make a new” (POST), “to find a” (GET) and “to delete a” (DELETE) to tell the tests which verb to use.

Hopefully this library will be useful to some folks out there – I’m going to be improving it as I work on more tests for some current projects to ensure better quality of the software.

From the Start: Setting up Behat testing

In a previous post I showed you how to use HTTP Auth with the Goutte driver in Behat testing. I want to take a step back and show you how I even got to that point with a simple guide to installing the tool and creating your first tests. In a future post, I’ll show the (super simple) integration you can do with Jenkins to execute the tests and plug in the results.

First off, a definition for those not sure what Behat is:

Behat was inspired by Ruby’s Cucumber project and especially its syntax part (Gherkin). It tries to be like Cucumber with input (Feature files) and output (console formatters), but in core, it has been built from the ground on pure php with Symfony2 components.

And, for those not sure what Cucumber is:

Cucumber lets software development teams describe how software should behave in plain text. The text is written in a business-readable domain-specific language and serves as documentation, automated tests and development-aid – all rolled into one format.

These are both tools that implement something called behavior-driven development, the practice of approaching testing from a more natural angle, allowing tests to be written in “plain English” (or the language of your choosing with Behat) and executed at a domain level rather than a code level (as unit tests would). Behat makes this super simple and it really only takes a few minutes to get it up and running.

For my examples, I’m going to be working on a Linux-based system (CentOS actually) but the same kinds of ideas apply to other platforms too, just modified slightly for things like pathing. So, let’s get started…first off, the installation. Personally, I recommend the Composer method for installation – it just makes it easier to get everything you need without having to think hardly at all.

Here’s a set of commands (linux command line) to get everything set up for a basic project structure:

mkdir behat-testing-ftw; cd behat-testing-ftw;
curl -s http://getcomposer.org/installer | php
vi composer.json

Inside the composer.json file, enter the stuff from the Behat instructions here (the JSON format), then we can run composer on it:

php composer.phar install

At this point you should see a bunch of things being installed in the “Installing dependencies” section (at the time of this post, that includes things like symfony/finder, symfony/dependency-injection and behat/gherkin). Once this is finished, you should have a directory structure similar to:

Chriss-MacBook-Pro:behat-testing-ftw chris$ ls
bin		composer.json	composer.lock	composer.phar	vendor

Behat should be magically installed in the bin/ directory – to test it out, try this:

Chriss-MacBook-Pro:behat-testing-ftw chris$ bin/behat

If all goes well, you should see a big red error talking about “features” not existing. This is good – we’ll fix this now by making the testing base for Behat. Thankfully, it makes this easy too:

Chriss-MacBook-Pro:behat-testing-ftw chris$ bin/behat --init
+d features - place your *.feature files here
+d features/bootstrap - place bootstrap scripts and static files here
+f features/bootstrap/FeatureContext.php - place your feature related code here

Your directory should now look like this:

Chriss-MacBook-Pro:behat-testing-ftw chris$ ls
bin		composer.json	composer.lock	composer.phar	features	vendor

The new features directory is where your Behat tests will live. If you look in there now, there’s not much – just a bootstrap directory with a FeatureContext.php file in it. This file is where you’ll define any custom rules you might need to use outside of the pre-defined ones Behat (and later Mink) come with. If you re-run Behat again, you should see:

Chriss-MacBook-Pro:behat-testing-ftw chris$ bin/behat
No scenarios
No steps
0m0.001s

So, let’s give Behat something to do now – Behat tests are organized in “feature” files. Let’s make a simple one for illustration and call it “sample.features” and put some content in it:

cd features
vi sample.feature

And put the following inside it:

Feature: Testing Behat install
        This is a testing feature to see if everything
        is working correctly

        Scenario: My first test
                When I run "ls"
                Then I should see the file "composer.json"

By default, Behat doesn’t come with any contexts pre-defined (you can see this by executing /bin/behat -dl) so we’ll need to make some for our new feature. Thankfully, Behat makes this simple too (sensing a theme here?) – if you run behat again, you should get something similar to:

eature: Testing Behat install
  This is a testing feature to see if everything
  is working correctly

  Scenario: My first test                      # features/sample.feature:5
    When I run "ls"
    Then I should see the file "composer.json"

1 scenario (1 undefined)
2 steps (2 undefined)
0m0.051s

You can implement step definitions for undefined steps with these snippets:

    /**
     * @When /^I run "([^"]*)"$/
     */
    public function iRun($argument1)
    {
        throw new PendingException();
    }

    /**
     * @Then /^I should see the file "([^"]*)"$/
     */
    public function iShouldSeeTheFile($argument1)
    {
        throw new PendingException();
    }

Se those last few lines? That tells you exactly what you’ll need to drop into the FeaturesContext.php file to make things work. The key to the matching is in the DocBlock comments – there’s regular expression matches in there that Behat tries to use when it’s executing the tests. So, let’s update our FeaturesContext with some new code:

vi features/bootstrap/FeatureContext.php

And insert into the class the following:

[php]
/**
* @When /^I run "([^"]*)"$/
*/
public function iRun($command)
{
exec($command,$result);
$this->output = $result;
}

/**
* @Then /^I should see the file "([^"]*)"$/
*/
public function iShouldSeeTheFile($fileName)
{
if (!in_array($fileName,$this->output)) {
throw new Exception(‘File named ‘.$fileName.’ not found!’);
}
}
[/php]

This code tells Behat to execute the $command (via exec), return the results to the “output” property and then check to see if the $fileName is in the list. As long as you were in the main “behat-testing-ftw” directory when you executed this, all the tests should come up green:

Feature: Testing Behat install
  This is a testing feature to see if everything
  is working correctly

  Scenario: My first test                      # features/sample.feature:5
    When I run "ls"                            # FeatureContext::iRun()
    Then I should see the file "composer.json" # FeatureContext::iShouldSeeTheFile()

1 scenario (1 passed)
2 steps (2 passed)
0m0.016s

To see how it looks when tests fail, try changing the filename in the sample.feature to something like “composer.json-foo” and you’ll see that step turn red.

So, that’s pretty much it – that’s, in a nutshell how to get Behat testing up and running (via the Composer option). There’s a few minor things that I came across when I was trying to execute the Composer and Behat tools, but those usually stemmed from missing PHP functionality/packages not being installed on the server. Those were solved with a quick “yum install” call for the needed functionality.

I hope to continue this series and talk next about getting Mink up and working to add some really useful features to Behat. I’ve been using it for testing a REST service, so that’s one of the end goals.

Behat and HTTP Auth (and Goutte)

So I’ve been trying out Behat for some REST testing recently and came across an interesting situation – I needed to send HTTP Auth credentials as a part of my test. Of course, there’s a method for this on the Session object (setBasicAuth) but it’s not implemented as a default phrase. It took me a bit to make it to the (now logcial) point that I needed to set these before I used the “Given I am on…” phrase to go to the page.

So, to accomplish this, I set up a custom “Given” phrase to set them: “Given that I log in with ‘username’ and ‘password'” so that my full Scenario looks like:
[php]
Scenario: Get the main Index view
Given that I log in with "myuser" and "mypass"
And I am on "/index/view"
Then the response should contain "TextFromPageTitle"
[/php]

And the code is super simple:

[php]
/**
* Features context.
*/
class FeatureContext extends BehatMinkBehatContextMinkContext
{
/**
* @Given /^that I log in with "([^"]*)" and "([^"]*)"$/
*/
public function thatILogInWithAnd($username, $password)
{
$this->getSession()->setBasicAuth($username,$password);
}
}
[/php]

Hope this helps someone else out there trying to send HTTP Auth pre-connection. I’m sure there’s probably an easier way that I’m missing, but this seemed the simplest to me.