gstreamer0.10-ffmpeg
gstreamer0.10-plugins-good
packages.
Welcome to the magical world of Behat, my favorite library. No joke this thing is the best. Behat is about two things:
First, functionally testing your application. Which means writing code that will open a browser, fill out a form, hit submit and verify text on the other side.
Why test your application? Well, imagine you are in charge of safety at Jurassic Park, your job is to make sure guests aren't eaten by dinosaurs. You need to be certain that the new pterodactyl exhibit that's being put in won't turn off the electric fence around the velociraptor pen. No tests? Good luck, they know how to open doors.
And second, designing your application. As developers we'll often just start coding without thinking about what we're building or how the feature will behave.
Using behavior driven development, which Behat helps you do, you'll actually plan things out beforehand.
Imagine a world where communication on your team is perfect, you always deliver exactly what your client wants, electricity on the raptor fence never goes down and chocolate ice cream is always free. Yep, that's where we're going.
Over in the browser, let's surf to the Behat documentation. In this tutorial we're covering version 3, and for whatever reason when I recorded this the website still defaults to version 2.5. So double check that you are actually looking at version 3's documentation.
I've got our project, the raptor store, loaded here. This is where dinosaurs go for the newest iphone and other cool electronics. There's a homepage, an admin section and that's basically it. This store is built using a very small Symfony2 application -- but hey, don't panic if you're not using Symfony: everything here will translate to whatever you're using.
Cool, we've got a search box, let's look up some sweet Samsung products. And here are the two results we have. I want to start this whole Behat thing by testing this. Hold onto your butts, let's going to get this thing running!
Tip
Installing Behat on Symfony 5 or newer? Read this blog post to know about how to install and configure it properly.
Over in the terminal run composer require
and instead of using behat/behat
we'll grab:
behat/mink-extension
and behat/mink-goutte-driver
:
composer require behat/mink-extension behat/mink-goutte-driver
These are plugins for Behat and another library called Mink and they require Behat and Mink. We see the Mink library downloaded here, and the Behat library downloaded down there. So life is good!
Once you've downloaded Behat you'll have access to an executable called ./vendor/bin/behat
or just bin/behat
for Symfony2 users. Running it now gives us a nice strong error:
vendor/bin/behat
That's ok because we need to run it with --init
at the end just one time in
our application:
vendor/bin/behat --init
This did an underwhelming amount of things for us. It created two directories and one file.
In PhpStorm we see a features
directory, a bootstrap
directory and a little FeatureContext.php
file and that's all of it:
... lines 1 - 2 | |
use Behat\Behat\Context\Context; | |
use Behat\Behat\Context\SnippetAcceptingContext; | |
... lines 5 - 6 | |
... lines 8 - 10 | |
class FeatureContext implements Context, SnippetAcceptingContext | |
{ | |
... lines 13 - 19 | |
public function __construct() | |
{ | |
} | |
} |
While we're here, I'll add a use statement for MinkContext
and make it extend that.
I'll explain that in a minute:
... lines 1 - 6 | |
use Behat\MinkExtension\Context\MinkContext; | |
... lines 9 - 11 | |
class FeatureContext extends MinkContext implements Context, SnippetAcceptingContext | |
... lines 13 - 25 |
One last bit of setup: at the root of your project create a behat.yml
file. I'll paste in some
content to get us started:
default: | |
extensions: | |
Behat\MinkExtension: | |
base_url: http://localhost:8000 | |
goutte: ~ |
When we run Behat it will looks for a behat.yml
file and this tells it:
Yo! Our application lives at localhost:8000, so look for it there.
Behat is installed, let's get to writing features! In the features
directory create a new file
called search.feature
and we'll just start describing the search feature on the raptor store
using a language called Gherkin which you're about to see here.
Feature: Search | |
In order to find products dinosaurs love | |
As a website user | |
I need to be able to search for products | |
... lines 5 - 11 |
Here I'm just using human readable language to describe the search feature in general. Within
each feature we'll have many different scenarios or user flows. So let's start with
Scenario: Searching for a product that exists
. Now using very natural language I'll describe
the flow.
... lines 1 - 5 | |
Scenario: Search for a word that exists | |
Given I am on "/" | |
When I fill in "searchTerm" with "Samsung" | |
And I press "search_submit" | |
Then I should see "Samsung Galaxy S II" |
Don't stress out about the formatting of this, we'll cover that in detail.
The only two things that should look weird to you are searchTerm
and search_submit
because
they are weird. searchTerm
is the name
attribute of this box here, and search_submit
is the
id
of this button. We'll talk more about this later: I'm actually breaking some rules. But
I want to get this working as quickly as possible.
Ready for me to blow your mind? Just by writing this one scenario we now have a test for our app.
In the terminal run ./vendor/bin/behat
and boom! It just read that scenario and actually went
to our homepage, filled in the search box, pressed the button and verified that "Samsung Galaxy"
was rendering on the next page. Why don't we see this happen? By default, it runs things using
invisible curl request instead of opening up a real browser.
The downside to this is that if you have JavaScript on your page that this scenario depends on, it isn't going to work since this isn't actually opening up a real browser. So, how can we run this in a real browser? There are actually a bunch of different ways. The easiest is by using Selenium.
Tip
With Symfony 5 it became easier! You can find the required instructions about Selenium configuration here. Or you can try a new library called Panther that is a bit easier than Selenium, but we're going to cover Selenium further in this course.
Grab another library with composer require behat/mink-selenium2-driver
. You'll also need to download
the selenium server which is really easy, it's just a jar file. Click this link here under downloads
to get the Selenium Standalone Server. I already have
this, so I'm not actually going to download it.
To run things in Selenium, open a new tab in your terminal, and run the jar file that you just downloaded. For me that's
java -jar ~/Downloads/selenium-server-standalone-2.45.0.jar
Tip
Firefox 47.0.0
and lower is not supported at all since Selenium 3.0.0
- update Firefox
to the latest version and install the new geckodriver for it in order to use
the latest Selenium server.
This will load and run as a daemon, so it should just hang there.
Our library is done downloading and we just need to activate it in our behat.yml
with the line:
default: | |
extensions: | |
Behat\MinkExtension: | |
... lines 4 - 5 | |
selenium2: ~ |
This gives me the option to use goutte to run the test using curl requests or Selenium to have things run in a browser. By default, this will just select goutte. So how do we make it use Selenium? I'm so glad you asked!
Above the scenario that you want to run in Selenium add @javascript
:
... lines 1 - 5 | |
@javascript | |
Scenario: Search for a word that exists | |
... lines 8 - 12 |
And that's it. Go back to the terminal and let's rerun this test. It actually opens the browser, it's quick but you can see it clicking around to complete the scenario. Cool!
Tip
FireFox is buggy with the new Selenium 3 server that's why it's preferable to use Google Chrome.
You can explicitly specify the browser in the behat.yml
config file:
# behat.yml
default:
extensions:
Behat\MinkExtension:
browser_name: chrome
We write human readable instructions and they turn into functional tests, and this just barely scratches the surface of how this will change your development. Let's keep going and figure out what's really going on here.
// composer.json
{
"require": {
"php": ">=5.4.0, <7.3.0",
"symfony/symfony": "^2.7", // v2.7.4
"twig/twig": "^1.22", // v1.22.1
"sensio/framework-extra-bundle": "^3.0", // v3.0.16
"doctrine/doctrine-bundle": "^1.5", // v1.5.1
"doctrine/orm": "^2.5", // v2.5.1
"doctrine/doctrine-fixtures-bundle": "^2.2", // v2.2.1
"behat/symfony2-extension": "^2.0" // v2.0.0
},
"require-dev": {
"behat/mink-extension": "^2.0", // v2.0.1
"behat/mink-goutte-driver": "^1.1", // v1.1.0
"behat/mink-selenium2-driver": "^1.2", // v1.2.0
"phpunit/phpunit": "^4.8" // 4.8.18
}
}