If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
Remember way back in the beginning when we had search feature? Try running that again:
./vendor/bin/behat features/web/search.feature
Huh, this one is failing now: it says that the text "Samsung Galaxy" was not found anywhere on the page. Now that you're an expert, I hope you can spot the problem: we're not adding this product at the beginning of the scenario. This worked originally because the fixtures that come with the project have a "Samsung Galaxy" product. But now that other tests have cleared the database, we're in trouble.
We could put some Given
statements at the top to add the products. But there's
another way: load the project's fixtures automatically before the scenario. This
LoadFixtures
class is responsible for putting in the Kindle and Samsung products.
I think that entering the data manually with the Given
statements is the most readable
way to do things. But, if you do load the fixtures, here's the best way. First, I
don't want to load fixtures before every scenario. That would make my scenarios
run slower, even when I don't need that stuff.
Instead, I need a way to tag scenarios to say "this one needs fixtures". Add
@fixtures
at the top of this scenario outline:
Feature: Search | |
In order to find products dinosaurs love | |
As a website user | |
I need to be able to search for products | |
... lines 6 - 8 | |
@fixtures | |
Scenario Outline: Search for a product | |
... lines 11 - 19 |
That's called a tag, and you can put many as you want, separating each by a space.
At first, adding a tag does nothing except for the magic @javascript
that changes
to use a JavaScript driver.
But in FeatureContext
you can add an @BeforeScenario
method that's only executed
when a scenario has a certain tag. Make a new public function loadFixtures()
. Inside,
just to see if it's working, put var_dump('GO!');
. Above, put the normal @BeforeScenario
:
... lines 1 - 42 | |
/** | |
* @BeforeScenario @fixtures | |
*/ | |
public function loadFixtures() | |
{ | |
var_dump('GO!'); | |
} | |
... lines 50 - 258 |
Here's the trick: after this, add @fixtures
. Now, this will only run for scenarios
tagged with @fixtures
. To prove that, re-run our search.feature
:
./vendor/bin/behat features/web/search.feature
There's our 'GO!'. Now run the authentication.feature
:
./vendor/bin/behat features/web/authentication.feature
This passes with no var_dump()
. Perfect!
One way to execute the fixture is by running the doctrine:fixtures:load
command.
I use a different method that gives me more control. Add $loader = new ContainerAwareLoader()
and pass it the container:
... lines 1 - 47 | |
public function loadFixtures() | |
{ | |
$loader = new ContainerAwareLoader($this->getContainer()); | |
... lines 51 - 53 | |
} | |
... lines 55 - 263 |
Now, point to the exact fixtures objects that you want to load. There are two methods
available: loadFromDirectory()
or loadFromFile()
. Move up a few directories and
load from src/AppBundle/DataFixtures
:
... lines 1 - 50 | |
$loader->loadFromDirectory(__DIR__.'/../../src/AppBundle/DataFixtures'); | |
... lines 52 - 263 |
That should do it!
Next, create an $executor = new ORMExecutor()
and pass it the entity manager:
... lines 1 - 51 | |
$executor = new ORMExecutor($this->getEntityManager()); | |
... lines 53 - 263 |
A purger is the second argument, which you only need if you want to clear out data.
We're already doing that, so I'm not going to worry about it here. Finally type
$executor->execute($loader->getFixtures())
and pass true as the second argument:
... lines 1 - 52 | |
$executor->execute($loader->getFixtures(), true); | |
... lines 54 - 263 |
This says to not delete the data, but to append it instead.
Ok, run search.feature
:
./vendor/bin/behat features/web/search.feature
It fails for a completely different reason. Things are never boring here! This is
a unique constraint violation because it's not clearing out the data before loading
the fixtures. This is a funny edge case. Because the new @BeforeScenario
is near
the top and the other for clearing the data is lower, they're being run in that order.
Move these @BeforeScenarios
up top and keep them in the order that you want:
... lines 1 - 35 | |
/** | |
* @BeforeScenario | |
*/ | |
public function clearData() | |
{ | |
$purger = new ORMPurger($this->getContainer()->get('doctrine')->getManager()); | |
$purger->purge(); | |
} | |
/** | |
* @BeforeScenario @fixtures | |
*/ | |
public function loadFixtures() | |
... lines 49 - 263 |
Back to the terminal and run this sucker again!
./vendor/bin/behat features/web/search.feature
Pop the champagne people, it passes! It clears the data and then loads the fixtures. And life is super awesome!
There's another benefit to tagging scenarios. But first, if you ever need some details
about the behat executable, run it with a --help
flag to get all the info:
./vendor/bin/behat --help
One of the options is tags:
Only execute the features or scenarios with these tags
Well that's sweet. So we could say: --tags=fixtures
and it will only execute scenarios
tagged with fixtures:
./vendor/bin/behat --tags=fixtures
Or, we can get real crazy and say that we want to run all scenarios except the ones
tagged with @fixtures
by using the handy ~
(tilde) character:
./vendor/bin/behat --tags=~fixtures
One more tip! If something goes wrong, there's also a verbosity option that will show
you the full stack trace. Just add -v
:
./vendor/bin/behat --tags=~fixtures -v
Hey, that's all! Hop in there, celebrate behavior driven development, create beautiful tests and sleep better at night!
See ya next time!
Hey Soltan!
When you go into that finish directory, make sure to also open the README.md file - it has some setup instructions that you need, including initializing the database. In this case, because the tutorial uses MySQL, I believe your error means that you don't have the mysql or PDO extension compiled into your PHP. That's what the "could not find driver" means. Make sure you have the php-pdo extension installed - here are some more details about that: https://stackoverflow.com/q...
Cheers!
Great course!
It's a pity I can't get a certificate of achievement because some challenges are not working :(
Update: They are working now! :)
Hey boykodev
I see you also reported that some challenges were not working, I just check it out and they are working well. Could you confirm this? Thanks :)
I keep getting an error for "The text "Samsung Galaxy S II" was not found anywhere in the text of the current page."
Here is my code:
/**
* @BeforeScenario @fixtures
*/
public function loadFixtures()
{
$loader = new ContainerAwareLoader($this->getContainer());
$loader->loadFromDirectory(__DIR__.'/../../src/AppBundle/DataFixtures');
$executer = new OrmExecutor($this->getEntityManager());
$executer->execute($loader->getFixtures(), true);
}
@fixtures
Scenario Outline:
When I fill in the search box with "<term>"
And I press the search button
Then I should see "<result>"
Examples:
| term | | result |
| Samsung | | Samsung Galaxy S II |
| XBox | | No products found |
Everything has gone good up till this point. Please help!
Yo Josh!
If everything worked up to this point, it must be something small! If you add a var_dump('hey!') to loadFixtures and run just this one Scenario Outline, is it called? Also, if you add a "And print last response" to the outline, what do you see? Does the page work? Are the products visible on the page?
It could be several things - use the debugging tools from earlier: I'm sure we'll find the issue. Nothing stands out to me - so you must be close.
Cheers!
Hey Ryan. The var_dump inside the loadFixtures works fine. I also added a "And print last response" and on the page it showed a "No products found message" and the table was empty. It looks like my Features are running fine. It just seems that the fixtures are not getting loaded, once the loadFixtures method is called. Here is my code if you can look over quick and see if I missed something. Thanks! https://github.com/joshcraw...
Hey Josh!
Yep, now I see the problem: move clearData() *above* loadFixtures() - we talk about this near the bottom of this section: https://knpuniversity.com/s.... For you, it's adding the fixtures, and then clearing everything. Hence, no results :). It's a tricky one.
Cheers!
This worked great. Thanks a lot. This is kind of a weird piece of code:
$executer = new ORMExecutor($this->getEntityManager());
$executer->execute($loader->getFixtures(), true);
If is supply a new ORMPurger($em) object as the second argument to ORMExecuter(), then suddenly $executer->execute won't run unless I change true to false as the second argument. I just find it a little weird that what you pass to the second method is dependent upon what you pass to the constructor. I dunno. Maybe I'm being weird. Thanks for everything!
// 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
}
}
How can i make run finish script? I just downloaded it but when I run cucumber authentication.feature file I have this error below:
@BeforeScenario # FeatureContext::clearData()
│
╳ An exception occured in driver: could not find driver (Doctrine\DBAL\Exception\DriverException)
So all steps are skipping.
How can I fix it?
Thank you in advance.