Skip to content

Commit

Permalink
Merge pull request #27 from knpuniversity/code-blocks-ch14
Browse files Browse the repository at this point in the history
Add code blocks to the ch14 (clear-data-symfony-extension)
  • Loading branch information
weaverryan committed Nov 9, 2015
2 parents f9eaa31 + 4e3417d commit aa8e0dc
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 24 deletions.
86 changes: 62 additions & 24 deletions knpu/clear-data-symfony-extension.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
# The SymfonyExtension & Clearing Data Between Scenarios

Change the user and pass back to match the original user in the database: "admin"
and "admin". *Now* rerun the scenario:
and "admin":

[[[ code('40b5daac0b') ]]]

*Now* rerun the scenario:

```bash
./vendor/bin/behat features/web/authentication.feature
```

Boom! This time it explodes!

> Integrity constraint violation Unique constraint failed user.username
> Integrity constraint violation: UNIQUE constraint failed: user.username
We already have a user called "admin" in the database... and since I made that a
unique column, creating *another* user in `Given` is putting a stop to our party.
Expand All @@ -26,12 +30,28 @@ want to empty the database before each scenario... except for any lookup tables.
Since *we* don't have any of these pesky look-up guys, we can empty everything before
every scenario. To do this, we'll of course, use hooks.

Create a new `public function clearData`. Clearing data now is pretty easy, since
we have access to the entity manager via `self::container->get('doctrine')->getManager();`.
Create a new `public function clearData()`:

[[[ code('664a9b2c3b') ]]]

Clearing data now is pretty easy, since we have access to the entity manager via
`self::container->get('doctrine')->getManager();`:

[[[ code('73379cb924') ]]]

Now we can issue DELETE queries on the two entities that we care about so far:
product and user. I'll use `$em->createQuery('DELETE FROM AppBundle:Product')->execute();`.
Copy and paste that line and change "Product" to "User". Oh and make sure that says
"Product" and not "Products". Activate all of this with the `@BeforeScenario` annotation.
product and user. I'll use `$em->createQuery('DELETE FROM AppBundle:Product')->execute();`:

[[[ code('ef81ad1396') ]]]

Copy and paste that line and change "Product" to "User":

[[[ code('17d499e619') ]]]

Oh and make sure that says "Product" and not "Products". Activate all of this with the
`@BeforeScenario` annotation:

[[[ code('ff2e48a1ed') ]]]

Try it all again:

Expand All @@ -47,30 +67,44 @@ And, surprise! There's an easier way to bootstrap Symfony and clear out the data
I always like taking the long way first so we can see how things work.

First, install a new library called `behat/symfony2-extension` with `--dev` so it
goes into my require dev:
goes into my require `dev` section:

```bash
composer require behat/symfony2-extension --dev
```

An `extension` in Behat is a plugin. We're already using the `MinkExtension`.
An `extension` in Behat is a plugin. We're already using the `MinkExtension`:

[[[ code('27daea06b7') ]]]

Activate the new plugin in `behat.yml`: `Behat\Symfony2Extension:`. And as luck would
have it, it doesn't need any configuration. It looks like we still need to wait for
it to finish installing in the terminal... there we go!
Activate the new plugin in `behat.yml`: `Behat\Symfony2Extension:`:

[[[ code('f5e71d6cc0') ]]]

And as luck would have it, it doesn't need any configuration. It looks like we still
need to wait for it to finish installing in the terminal... there we go!

The most important thing the Symfony2 Extension gives you is, access to Symfony's
container...but wait, we already have that? Well, this just makes it easier.
container... but wait, we already have that? Well, this just makes it easier.

Remove the `private static $container;` property and the `bootstrapSymfony()` function.
Instead of these, we'll use a PHP 5.4 trait called `KernelDictionary`:

[[[ code('a636d6da19') ]]]

This gives us two new functions, `getKernel()`, but more importantly `getContainer()`:

[[[ code('cc77eab4d3') ]]]

Remove the `private static $container;` property and the `bootstrapSymfony` function.
Instead of these, we'll use a PHP 5.4 trait called `KernelDictionary`.
This gives us two new functions, `getKernel()`, but more importantly `getContainer()`.
It takes care of all of the booting of the kernel stuff for us, and it even reboots
the kernel between each scenario so they don't run into each other. That's important
because remember, each scenario should be completely independent of the others.

Search for the old `self::$container` code. Change it to `$this->getContainer()`.
You see that PhpStorm all of a sudden autocompletes the methods on the services
Search for the old `self::$container` code. Change it to `$this->getContainer()`:

[[[ code('c1941ef181') ]]]

You see that PhpStorm all of a sudden auto-completes the methods on the services
we fetch because it recognizes this as the container and so knows that this returns
the entity manager.

Expand All @@ -85,10 +119,15 @@ can use the `KernelDictionary` on all of them to get access to the container.

## Clearing the Database Easily

Ok, so what about clearing the database? It'll be a huge pain to add more and more
OK, so what about clearing the database? It'll be a huge pain to add more and more
manual queries. Fortunately Doctrine gives us a better way: a `Purger`. Create a new
variable called `$purger` and set it to a `new ORMPurger()`. Pass it the entity manager.
After that, type `$purger->purge();`, and that's it.
variable called `$purger` and set it to a `new ORMPurger()`. Pass it the entity manager:

[[[ code('4a1ce99a60') ]]]

After that, type `$purger->purge();`, and that's it:

[[[ code('61566dcc53') ]]]

This will go through each entity and clear out all of your data. If it's working,
then our tests should pass:
Expand All @@ -97,10 +136,9 @@ then our tests should pass:
./vendor/bin/behat features/web/authentication.feature
```

And they do! Same functionality and a lot less code. For bigger databases with lots
And they do! Same functionality and a lot less code. For bigger databases with lots
of lookup tables, it may be too much to clear every table and re-add all the data
you need. In those cases, trying experimenting with creating a SQL file that populates
the database and executing that before each scenario. Or, populate an Sqlite file
the database and executing that before each scenario. Or, populate an SQLite file
with whatever you want to start with, then copy this and use it as your database
before each test. That's a super-fast way to roll back to your known data set.

Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

/*
* This file is part of the Behat Symfony2Extension
*
* (c) Konstantin Kudryashov <ever.zet@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

namespace Behat\Symfony2Extension\Context;

use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
* Kernel support methods for Symfony2Extension.
*
* @author Konstantin Kudryashov <ever.zet@gmail.com>
*/
trait KernelDictionary
{
private $kernel;

/**
* Sets Kernel instance.
*
* @param KernelInterface $kernel
*/
public function setKernel(KernelInterface $kernel)
{
$this->kernel = $kernel;
}

/**
* Returns HttpKernel instance.
*
* @return KernelInterface
*/
public function getKernel()
{
return $this->kernel;
}

/**
* Returns HttpKernel service container.
*
* @return ContainerInterface
*/
public function getContainer()
{
return $this->kernel->getContainer();
}
}

0 comments on commit aa8e0dc

Please sign in to comment.