WEBVTT

NOTE Created by CaptionSync from Automatic Sync Technologies www.automaticsync.com

00:00:01.066 --> 00:00:04.666 align:middle
The captain is tired of people running
after the rocket because they show up late!

00:00:05.066 --> 00:00:08.096 align:middle
That's why we created a command
to send reminder emails!

00:00:08.366 --> 00:00:09.356 align:middle
Problem solved!

00:00:09.956 --> 00:00:12.356 align:middle
Now let's write a test to
ensure it keeps working.

00:00:12.786 --> 00:00:15.266 align:middle
"New feature, new test", that's my motto!

00:00:15.786 --> 00:00:21.016 align:middle
Jump over to your terminal and run:
symfony console make:test Type?

00:00:21.316 --> 00:00:23.076 align:middle
KernelTestCase.

00:00:23.076 --> 00:00:26.146 align:middle
Name? SendBookingRemindersCommandTest.

00:00:27.306 --> 00:00:30.296 align:middle
In our IDE, the new class was added to tests/.

00:00:30.296 --> 00:00:33.806 align:middle
Open it up and move the class
to a new namespace:

00:00:34.806 --> 00:00:38.696 align:middle
App\Tests\Functional\Command,
to keep things organized.

00:00:39.466 --> 00:00:46.816 align:middle
Perfect. First, clear out the guts and add some
behavior traits: use ResetDatabase, Factories,

00:00:47.176 --> 00:00:53.876 align:middle
InteractsWithMailer: Stub out two tests:
public function testNoRemindersSent()

00:00:54.556 --> 00:01:01.776 align:middle
with $this-&gt;markTestIncomplete() and
public function testRemindersSent().

00:01:02.516 --> 00:01:11.316 align:middle
Also mark it incomplete: Back in the terminal
run the tests with: bin/phpunit Check it out,

00:01:11.316 --> 00:01:14.106 align:middle
our original two tests are
passing, the two dots,

00:01:14.406 --> 00:01:17.066 align:middle
and these I's are the new incomplete tests.

00:01:17.456 --> 00:01:20.736 align:middle
I love this pattern: write
test stubs for a new feature,

00:01:20.976 --> 00:01:24.896 align:middle
then make a game of removing the incompletes
one-by-one until they're all gone.

00:01:25.186 --> 00:01:26.626 align:middle
Then, the feature is done!

00:01:26.626 --> 00:01:31.606 align:middle
Symfony has some out-of-the-box tooling for
testing commands, but I like to use a package

00:01:31.606 --> 00:01:34.136 align:middle
that wraps these up into a nicer experience.

00:01:34.536 --> 00:01:36.936 align:middle
Install it with: composer require --

00:01:37.016 --> 00:01:45.526 align:middle
dev zenstruck/console-test To enable this
package's helpers, add a new behavior trait

00:01:45.526 --> 00:01:50.636 align:middle
to our test: InteractsWithConsole:
We're ready to knock down those I's!

00:01:51.306 --> 00:01:55.456 align:middle
The first test is easy: we want to ensure
that, when there's no bookings to remind,

00:01:55.666 --> 00:01:57.646 align:middle
the command doesn't send any emails.

00:01:58.056 --> 00:02:04.976 align:middle
Write $this-&gt;executeConsoleCommand() and just
the command name: app:send-booking-reminders.

00:02:05.706 --> 00:02:10.836 align:middle
Ensure the command ran successfully
with -&gt;assertSuccessful()

00:02:10.936 --> 00:02:16.886 align:middle
and -&gt;assertOutputContains('Sent 0
booking reminders'): On to the next test!

00:02:17.266 --> 00:02:21.516 align:middle
This one is more involved: we need to create
a booking that is eligible for a reminder.

00:02:22.116 --> 00:02:27.836 align:middle
Create the booking fixture with
$booking = BookingFactory::createOne().

00:02:28.336 --> 00:02:34.646 align:middle
Pass an array with 'trip' =&gt;
TripFactory::new(), and inside that,

00:02:34.646 --> 00:02:42.996 align:middle
another array with 'name' =&gt; 'Visit Mars',
'slug' =&gt; 'iss' (to avoid the image issue).

00:02:43.836 --> 00:02:49.666 align:middle
The booking also needs a customer:
'customer' =&gt; CustomerFactory::new().

00:02:50.456 --> 00:02:55.986 align:middle
All we care about is the customer's
email: 'email' =&gt; 'steve@minecraft.com'.

00:02:55.986 --> 00:03:05.786 align:middle
Finally, the booking date: 'date' =&gt;
new \DateTimeImmutable('+4 days'): Phew!

00:03:06.166 --> 00:03:08.956 align:middle
We have a booking in the database
that needs a reminder sent.

00:03:09.306 --> 00:03:12.096 align:middle
This test's setup, or arrange step, is done.

00:03:12.526 --> 00:03:16.346 align:middle
Add a pre-assertion to ensure this
booking hasn't had a reminder sent:

00:03:16.816 --> 00:03:24.146 align:middle
$this-&gt;assertNull($booking
-&gt;getReminderSentAt()): Now for the act step:

00:03:24.616 --> 00:03:32.866 align:middle
$this -&gt;executeConsoleCommand('app:send-booking
-reminders') -&gt;assertSuccessful()

00:03:33.176 --> 00:03:36.846 align:middle
-&gt;assertOutputContains('Sent
1 booking reminders'):

00:03:37.636 --> 00:03:40.406 align:middle
Onto the assert phase to
ensure the email was sent.

00:03:40.996 --> 00:03:44.316 align:middle
In BookingTest, copy the email
assertion and paste it here.

00:03:48.386 --> 00:03:55.326 align:middle
Make a few adjustments: the email is
steve@minecraft.com, subject is Booking Reminder

00:03:55.386 --> 00:04:00.526 align:middle
for Visit Mars and this email doesn't have an
attachment, so remove that assertion entirely:

00:04:00.526 --> 00:04:05.546 align:middle
Finally, write an assertion that the
command updated the booking in the database.

00:04:05.956 --> 00:04:12.976 align:middle
$this-&gt;assertNotNull($booking
-&gt;getReminderSentAt()): Moment of truth!

00:04:12.976 --> 00:04:16.446 align:middle
Run the tests: All green!

00:04:16.446 --> 00:04:20.936 align:middle
I find these type of outside-in tests really
fun and easy to write because you don't have

00:04:20.936 --> 00:04:23.436 align:middle
to worry too much about testing the inner logic

00:04:23.436 --> 00:04:26.036 align:middle
and they mimic how a user
interacts with your app.

00:04:26.526 --> 00:04:30.166 align:middle
It's no accident that the assertions
are focused on what the user should see

00:04:30.446 --> 00:04:34.816 align:middle
and some high level post-interaction checks,
like checking something in the database.

00:04:34.816 --> 00:04:38.466 align:middle
Now that we have tests for both
of our email sending paths,

00:04:38.726 --> 00:04:42.936 align:middle
let's take a victory lap &amp; refactor
with confidence to remove duplication.

