Buy Access to Course
26.

Loading Fixtures References

Share this awesome video!

|

Keep on Learning!

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

Despite all our precautions, we still sometimes have enclosures with no security. Yea... a lot of people are getting eaten, a lot of lawsuits - very expensive. To help with this, I want to add an "Alarm" button on the homepage next to any enclosures that do not have active security.

Because this sounds pretty important, let's write the test first. Add public function testThatThereIsAnAlarmButtonWithoutSecurity():

// ... lines 1 - 8
class DefaultControllerTest extends WebTestCase
{
// ... lines 11 - 27
public function testThatThereIsAnAlarmButtonWithoutSecurity()
{
// ... lines 30 - 42
}
}

Copy the fixture and request code from before and paste it here:

// ... lines 1 - 8
class DefaultControllerTest extends WebTestCase
{
// ... lines 11 - 27
public function testThatThereIsAnAlarmButtonWithoutSecurity()
{
$fixtures = $this->loadFixtures([
LoadBasicParkData::class,
LoadSecurityData::class,
])->getReferenceRepository();
$client = $this->makeClient();
$crawler = $client->request('GET', '/');
// ... lines 38 - 42
}
}

But, at the end of loadFixtures(), add getReferenceRepository() and assign this to a new $fixtures variable:

// ... lines 1 - 8
class DefaultControllerTest extends WebTestCase
{
// ... lines 11 - 27
public function testThatThereIsAnAlarmButtonWithoutSecurity()
{
$fixtures = $this->loadFixtures([
LoadBasicParkData::class,
LoadSecurityData::class,
])->getReferenceRepository();
// ... lines 34 - 42
}
}

What are Fixture References?

Here's the deal: if you look in the fixtures, you can see that the first two Enclosures do not have any security. You can also see that we're using some sort of "reference" system. This allows us to store a specific object in memory so that we can re-use it somewhere else. For example, in LoadSecurityData, we get the herbivorous-enclosure object out and add security! We're safe from those wild veggie eating dinos!

It does the same for carnivorous-enclosure... but then adds two Security objects that are both inactive. Doh! Yep, this means that the carnivorous-enclosure is the only Enclosure that is not secure. In the test, our goal is to assert that, on the homepage, this exact Enclosure has the alarm button.

And we planned ahead for this! Remember, in the template, we added an enclosure-{id} to each tr element. So if we can get the actual id value of the Carnivorous Enclosure, it will be really easy to find its tr element and look for the alarm button. The reference system gives us that power!

Yep, we can fetch the exact Enclosure object by saying $enclosure = $fixtures->getReference('carnivorous-enclosure'):

// ... lines 1 - 8
class DefaultControllerTest extends WebTestCase
{
// ... lines 11 - 27
public function testThatThereIsAnAlarmButtonWithoutSecurity()
{
// ... lines 30 - 36
$crawler = $client->request('GET', '/');
$enclosure = $fixtures->getReference('carnivorous-enclosure');
// ... lines 40 - 42
}
}

Next, create a $selector variable set to sprintf('#enclosure-%s .button-alarm') and $enclosure->getId(). We'll expect the alarm button to have this class:

// ... lines 1 - 8
class DefaultControllerTest extends WebTestCase
{
// ... lines 11 - 27
public function testThatThereIsAnAlarmButtonWithoutSecurity()
{
// ... lines 30 - 38
$enclosure = $fixtures->getReference('carnivorous-enclosure');
$selector = sprintf('#enclosure-%s .button-alarm', $enclosure->getId());
// ... lines 41 - 42
}
}

Finish the test! $this->greaterThan(0, $crawler->filter($selector)->count()):

// ... lines 1 - 8
class DefaultControllerTest extends WebTestCase
{
// ... lines 11 - 27
public function testThatThereIsAnAlarmButtonWithoutSecurity()
{
// ... lines 30 - 41
$this->assertGreaterThan(0, $crawler->filter($selector)->count());
}
}

I love it! So first, of course, make sure the test fails. Copy the method name and run phpunit with the --filter option:

./vendor/bin/phpunit --filter testThatThereIsAnAlarmButtonWithoutSecurity

Awesome!

Adding the Alarm Button

So let's code! In index.html.twig, add one more <td>: if enclosure.isSecurityActive() with else and endif:

// ... lines 1 - 2
{% block body %}
// ... lines 4 - 5
<table class="table-enclosures">
<tbody>
{% for enclosure in enclosures %}
<tr id="enclosure-{{ enclosure.id }}">
// ... lines 10 - 13
<td>
{% if enclosure.isSecurityActive %}
// ... line 16
{% else %}
// ... line 18
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}

If security is active, we rock! Add a cute lock icon and say "Security active". Yep, just sit back and enjoy some Jolt soda: nobody is getting eaten today!

// ... lines 1 - 2
{% block body %}
// ... lines 4 - 5
<table class="table-enclosures">
<tbody>
{% for enclosure in enclosures %}
<tr id="enclosure-{{ enclosure.id }}">
// ... lines 10 - 13
<td>
{% if enclosure.isSecurityActive %}
? Security active!
{% else %}
// ... line 18
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}

But if security is not active, ah crap! Add the button with the button-alarm class that the test is looking for. And say "Sound alarm!!!":

// ... lines 1 - 2
{% block body %}
// ... lines 4 - 5
<table class="table-enclosures">
<tbody>
{% for enclosure in enclosures %}
<tr id="enclosure-{{ enclosure.id }}">
// ... lines 10 - 13
<td>
{% if enclosure.isSecurityActive %}
? Security active!
{% else %}
<button class="button button-alarm">Sound alarm !!!</button>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}

That should be it! Run the test:

./vendor/bin/phpunit --filter testThatThereIsAnAlarmButtonWithoutSecurity

Ha! It passes!

Debugging Functional Tests

But... what if it didn't pass? Well... the errors wouldn't be very helpful: it would basically just say that 0 is not greater than 0. When things fail, the trick is to go above the failure and dump($client->getResponse()->getContent()). If you're using Flex, make sure to install the var-dumper package.

Tip

To install the symfony/var-dumper package, run: composer require --dev var-dumper

The --dev option tells Composer to install it as a dev dependency.

Now when you run the test, it will at least print out the HTML body. By the way, with a little bit of clever coding, you can hook into the onNotSuccessfulTest method and have the last response content printed automatically when a test fails. I'll leave that as a challenge for you. But, ask us in the comments if you have any questions.

Ok, there's one more thing I want to talk about with functional tests: filling out and submitting a form.