WEBVTT

NOTE Created by CaptionSync from Automatic Sync Technologies www.automaticsync.com

00:00:01.106 --> 00:00:02.466 align:middle
Okay, testing time!

00:00:02.966 --> 00:00:05.396 align:middle
If you've explored the codebase
a bit, you may have noticed

00:00:05.396 --> 00:00:07.366 align:middle
that someone (it could've been anyone...

00:00:07.446 --> 00:00:11.706 align:middle
but probably a Canadian) snuck some tests
into our tests/Functional/ directory.

00:00:12.116 --> 00:00:12.966 align:middle
Do these pass?

00:00:13.246 --> 00:00:14.616 align:middle
Idk! Let's find out!

00:00:15.086 --> 00:00:21.246 align:middle
Jump over to your terminal and
run: bin/phpunit Uh-oh, 1 failure.

00:00:21.506 --> 00:00:25.076 align:middle
Uh-oh, because, truth time, I'm the
friendly Canadian that added these

00:00:25.076 --> 00:00:27.556 align:middle
and I know they were passing
at the beginning of the course!

00:00:27.896 --> 00:00:34.046 align:middle
The failure is in BookingTest, specifically,
testCreateBooking: Expected redirect status code

00:00:34.046 --> 00:00:37.416 align:middle
but got 500 on line 38 of BookingTest.

00:00:37.916 --> 00:00:39.206 align:middle
That's where we send the email...

00:00:39.206 --> 00:00:43.636 align:middle
so if we're looking for someone to blame, I feel
like we should start with the Canadian, ahem,

00:00:43.786 --> 00:00:46.546 align:middle
me and my wild email-sending ways.

00:00:47.216 --> 00:00:49.406 align:middle
Open BookingTest.php.

00:00:49.406 --> 00:00:53.096 align:middle
If you've written functional tests with
Symfony before, this may look a tad different

00:00:53.096 --> 00:00:55.336 align:middle
because I'm using some rocking helper libraries.

00:00:55.756 --> 00:01:01.016 align:middle
zenstruck/foundry gives us this ResetDatabase
trait which wipes the database before each test.

00:01:01.626 --> 00:01:07.096 align:middle
It also gives us this Factories trait which
lets us create database fixtures in our tests.

00:01:07.096 --> 00:01:10.446 align:middle
And HasBrowser is from another
package - zenstruck/browser -

00:01:10.706 --> 00:01:14.586 align:middle
and is essentially a user-friendly
wrapper around Symfony's test client.

00:01:15.156 --> 00:01:17.366 align:middle
testCreateBooking is the actual test.

00:01:17.366 --> 00:01:21.206 align:middle
First, we create a Trip in the
database with these known values.

00:01:21.896 --> 00:01:26.496 align:middle
Next, some pre-assertions to ensure there
are no bookings or customers in the database.

00:01:27.026 --> 00:01:32.446 align:middle
Now, we use -&gt;browser() to navigate to a trip
page, fill in the booking form, and submit.

00:01:33.026 --> 00:01:37.046 align:middle
We then assert that we're redirected
to a specific booking URL and check

00:01:37.046 --> 00:01:39.536 align:middle
that the page contains some expected HTML.

00:01:39.536 --> 00:01:44.096 align:middle
Finally, we use Foundry to make some
assertions about the data in our database.

00:01:44.836 --> 00:01:46.536 align:middle
Line 38 caused the failure...

00:01:46.786 --> 00:01:50.506 align:middle
we're getting a 500 response code
when redirecting to this booking page.

00:01:50.506 --> 00:01:54.576 align:middle
500 status codes in tests can be
frustrating because it can be hard

00:01:54.576 --> 00:01:56.306 align:middle
to track down the actual exception.

00:01:56.896 --> 00:02:00.136 align:middle
Luckily, Browser allows us to
throw the actual exception.

00:02:00.646 --> 00:02:05.766 align:middle
At the beginning of this chain, add
-&gt;throwExceptions(): Back in the terminal,

00:02:05.796 --> 00:02:13.786 align:middle
run the tests again: Now we see an exception:
Unable to find template "@images/mars.png".

00:02:13.786 --> 00:02:18.566 align:middle
If you recall, this looks like how we're
embedding the trip images into our email.

00:02:19.146 --> 00:02:23.086 align:middle
It's failing because mars.png
doesn't exist in public/imgs.

00:02:23.086 --> 00:02:26.956 align:middle
For simplicity, let's adjust our
test to use an existing image.

00:02:27.286 --> 00:02:31.746 align:middle
For our fixture here, change
mars to iss, and down below,

00:02:31.746 --> 00:02:36.676 align:middle
for -&gt;visit(): /trip/iss: Run the tests again!

00:02:38.226 --> 00:02:41.386 align:middle
Passing! It looks like our
email is being sent...

00:02:41.526 --> 00:02:42.366 align:middle
but let's confirm!

00:02:42.886 --> 00:02:46.006 align:middle
At the end of this test, I want
to make some email assertions.

00:02:46.336 --> 00:02:49.866 align:middle
Symfony does allow this out of the
box, but I like to use a library

00:02:49.866 --> 00:02:52.526 align:middle
that puts the fun back in
email functional testing.

00:02:53.126 --> 00:02:56.176 align:middle
At your terminal, run: composer require --

00:02:56.276 --> 00:03:02.706 align:middle
dev zenstruck/mailer-test
Installed and configured...

00:03:03.086 --> 00:03:08.536 align:middle
back in our test, enable it by adding the
InteractsWithMailer trait: Start simple,

00:03:08.796 --> 00:03:16.706 align:middle
at the end of the test, write
$this-&gt;mailer()-&gt;assertSentEmailCount(1);:

00:03:17.566 --> 00:03:23.056 align:middle
Quick note: .env.local - where we put our
real Mailtrap credentials - is not read

00:03:23.056 --> 00:03:29.476 align:middle
or used in the test environment: our tests
only load .env and this .env.test file.

00:03:30.116 --> 00:03:33.806 align:middle
And in .env, MAILER_DSN is set to null://null.

00:03:33.906 --> 00:03:34.506 align:middle
That's great!

00:03:34.796 --> 00:03:37.976 align:middle
We want our tests to be fast,
and not actually sending emails.

00:03:38.726 --> 00:03:39.376 align:middle
Re-run them!

00:03:41.656 --> 00:03:44.056 align:middle
Passing - 1 email is being sent!

00:03:44.736 --> 00:03:49.286 align:middle
Go back and add another assertion:
-&gt;assertEmailSentTo().

00:03:49.926 --> 00:03:51.666 align:middle
What email address are we expecting?

00:03:52.076 --> 00:03:55.936 align:middle
The one we filled in the form:
bruce@wayne-enterprises.com.

00:03:56.506 --> 00:03:57.466 align:middle
Copy and paste that.

00:03:58.226 --> 00:04:04.876 align:middle
The second argument is the subject: Booking
Confirmation for Visit Mars: Run the tests!

00:04:06.366 --> 00:04:07.206 align:middle
Still passing!

00:04:07.476 --> 00:04:10.376 align:middle
And notice we have 20 assertions
now instead of 19.

00:04:10.966 --> 00:04:11.976 align:middle
But we can go further!

00:04:12.266 --> 00:04:18.236 align:middle
Instead of a string for the subject in this
assertion, use a closure with TestEmail $email

00:04:18.286 --> 00:04:22.866 align:middle
as the argument: Inside, we can now make
loads more assertions on this email.

00:04:22.866 --> 00:04:27.046 align:middle
Since we aren't checking the subject
above anymore, add this one first:

00:04:27.296 --> 00:04:36.176 align:middle
$email-&gt;assertSubject('Booking Confirmation for
Visit Mars'): And we can chain more assertions!

00:04:36.596 --> 00:04:40.056 align:middle
Write -&gt;assert to see what our editor suggests.

00:04:40.056 --> 00:04:40.706 align:middle
Look at them all...

00:04:41.156 --> 00:04:45.056 align:middle
Note the assertTextContains
and assertHtmlContains.

00:04:45.536 --> 00:04:50.016 align:middle
You can assert on each of these separately,
but, because it's a best practice for both

00:04:50.016 --> 00:04:54.466 align:middle
to contain the important details, use
assertContains() to check both at once.

00:04:55.116 --> 00:04:58.626 align:middle
Check for Visit Mars: Links
are important to check,

00:04:58.856 --> 00:05:04.136 align:middle
so make sure the booking URL is
there: -&gt;assertContains('/booking/'..

00:05:05.086 --> 00:05:12.296 align:middle
Now, BookingFactory::first()-&gt;getUid():
this fetches the first Booking entity

00:05:12.296 --> 00:05:17.236 align:middle
in the database (which we know from above
there is only the one), and gets its uid.

00:05:17.816 --> 00:05:24.686 align:middle
Heck! We can even check the attachment:
-&gt;assertHasFile('Terms of Service.pdf'):

00:05:24.686 --> 00:05:29.646 align:middle
You can check the content-type and file contents
via extra arguments, but I'm fine just checking

00:05:29.646 --> 00:05:31.276 align:middle
that the attachment exists for now.

00:05:32.026 --> 00:05:33.036 align:middle
Go tests go!

00:05:33.946 --> 00:05:35.906 align:middle
Awesome, 25 assertions now!

00:05:36.566 --> 00:05:39.806 align:middle
One last thing: if you're ever
having trouble figuring out why one

00:05:39.806 --> 00:05:45.446 align:middle
of these email assertions isn't passing,
chain a -&gt;dd(): and run your tests.

00:05:45.446 --> 00:05:48.696 align:middle
When it hits that dd(), it dumps
the email to help you debug.

00:05:49.676 --> 00:05:51.266 align:middle
Don't forget to remove it when you're done!

00:05:52.266 --> 00:05:54.776 align:middle
Next, I want to add a second email to our app.

00:05:55.156 --> 00:05:57.786 align:middle
To avoid duplication and keep things consistent,

00:05:58.026 --> 00:06:00.696 align:middle
we'll create a Twig email
layout that both share.

