Extending Laravel’s Functional Tests with Macros

I was fiddling around with a new project and I wanted to add a quick functional test to make sure that entries
were appearing in the order that I expected. I know that neither Laravel nor PHPUnit have an assertion for that built in, but I figured it would be no problem to add it to the base TestCase class.

Bzzzt, sorry, nope, thanks for playing.

In the pre-Laravel 5.4 days, I would have created a function called assertThisBeforeThat() on the TestCase class and been all set.

$this->visit('/entries/')->seeThisBeforeThat('Entry 1'. 'Entry 4');

With 5.4, though, we mostly don’t test assertions against the TestCase class, we test them against the Response that’s returned when we get() or post().

OK, no sweat, I’ll just extend the TestResponse class to add an extra assertion. (Did you know that when you’re testing, the Response that you get back isn’t a standard Illuminate\Http\Response? The Response is wrapped in a class called TestResponse which is where the assertions live.) Extending the TestResponse class will work as long as the Response is instantiated through the IOC container and not … oh, it’s instantiated directly.

Stuck, right? Wrong.

A recent blog post https://unnikked.ga/understanding-the-laravel-macroable-trait-dab051f09172#.lcxjul9oz reminded me that lots of Laravel classes implement the Macroable trait and TestResponse is one of them. Macros, for those who haven’t used them, allow you to add functions to existing classes without making a new class. (The Laravel docs are a little spotty on macros – https://laravel.com/docs/5.4/responses#response-macros is all there is and it doesn’t explain how widely available macros are. Extending those docs could be a good first PR for someone.)

It’s precisely what I need in a case where I can’t inject an extended TestResponse class but want to add a quick function to it.

Here’s what I did:

In AppServiceProvider, I added the following code:

And also … oh, wait, no, that’s it.

The test is pretty straightforward:

Is there something else you want to assert() about the response? Just make a macro.

A suggestion for conference organizers

I attended SunshinePHP this year. Adam Culp has written up a good recap of the conference including Code of Conduct violations and how they were handled. I want to applaud Adam for his transparency in talking about what happened – no conference organizer wants to admit that there was bad behavior on-site, but acknowledging what happened is the first step to preventing it in future.

When SunshinePHP attendees wanted to report bad behavior, they had to find or message Adam to let him know what happened. I think at most conferences, the approach to reporting CoC violations is to find the organizer and tell him what’s going on. I’d like to see an easier way to report issues; one that doesn’t rely on finding one or two people.

CenturyLink field in Seattle (home of the Seahawks) has these signs posted throughout the stadium:

2015-09-14 10.35.18

I really like that there’s a number to text and that it’s easy to find. I’d love to see conferences set up an SMS/email account (hello, Twilio) and post the numbers prominently. Perhaps a copy in the gimme bag, in each room where conference sessions are happening, at the bar, and at the pool/hot tub. Having signs up that remind people that the CoC is in place even when they’re “just hanging out” is probably also a good reminder.

Look, nobody likes CoCs. Nobody likes that they’re necessary, but at just about every conference, someone reminds us that they are. If we’re going to have people behaving badly (and we are), let’s make reporting that bad behavior as easy as it can be.

In Which … J.T. learns how traits work

On the home page of the site I’m building right now, the user is asked to enter a city and state.

Homepage City/State Entry

I’m using Laravel 5.1’s super-simple validation to make sure that both fields are entered:

That’s all it takes to (1) make sure both fields are present and (2) display an error if they’re not.  The only problem for me is that when validation fails, the user is redirected to the previous page. 99% of the time, this is the desired behavior, but the one place I don’t want it is on the home page. Displaying errors there just ruins the layout.

Here are the highlights from Laravel’s ValidatesRequest trait:

So if we can just change getRedirectUrl(), everything else should fall into place. Obviously, we don’t want to change anything in the core Laravel files. Can we override a trait function?

190px-Obama_logomarkYES WE CAN

Traits don’t use inheritance (we can’t use the extends keyword), but they compose very nicely. By creating a new trait which uses the base ValidationRequests trait, we have all the functions available in that trait and can override one.

Also note the new $redirectOnError variable – we only redirect if this is set.

The base Controller class is updated to point to this new ValidatesRequests trait and all the inheriting controllers now have this feature available.  The new function just has one extra line:

require-dev in Laravel

THIS POST IS NOW OBSOLETE. This post referred to Laravel 4, and the information was always a little iffy. This post remains only to prevent this blog from being entirely empty.


Note: this post, like all Laravel tips, is best read in Jeffrey Way’s voice.

There are some packages (Jeffrey Way’s Generators and Barry van den Heuvel’s IDE Helper come to mind) which we only need during development, but which become tightly coupled with Laravel. Once you’ve added the packages to the array of ServiceProviders in app/config/app.php, you have to either have a different config for development or include those packages in your deployment. Deploying them isn’t going to do any harm, but I think it’s a good habit to keep your deployment as lean as possible — the fewer packages on your production server, the fewer things that can go wrong.

One way to keep these packages from having to be deployed is to have a different config file for your local machine. Laravel certainly supports this, but I’m not wild about it as a solution to this particular problem. There’s (presently) no way to merge an array of service providers in the main config/app.php file with one in config/local/app.php. You’d have to maintain two lists and make sure that you keep them in sync. Add a service provider in one file, and it’s up to you to remember to add it in the other.

EDIT:Matt Stauffer points out that Laravel already has a built-in helper function for this: append_config. Check out the Laravel docs for more detail.

[And now we return to the original, obsolete post]

I’ve come up with a different solution that I’m using — I don’t promise that it will work for you, but I don’t see why it wouldn’t.

Instead of adding service providers to the list in app/config/app.php, I manually register them from /app/start/local.php. That way, they’re only available during local development. Here’s an example with Jeffrey Way’s Laravel 4 Generators (which you should totally install.)

First, we add the package to our composer.json file as a development dependency:

We run composer update --dev so that composer knows we want the development packages too.

The instructions for installing Generators tells us to add 'Way\Generators\GeneratorsServiceProvider' to the service providers array. That’s just what we won’t be doing. Instead, we’re going to edit /app/start/local.php and … add one line. Really, that’s all it takes:

Assuming that your environment correctly determines that it’s local (/bootstrap/start.php is your friend and so is Steve Grunwell’s article Laravel Application Environments without Hostnames), you’ll have php artisan generate commands available on your development machine without having to muck around when it’s time to deploy.