Injecting mocks in Laravel tests

If you’re doing much testing in Laravel, you may have found yourself writing a lot of code like this for mock objects:

Sometimes you’ll find yourself duplicating versions of this code throughout a class if you’re testing a lot of interactions with an outside service. It’s not a big deal, but it’s a little tedious. (Also, I tend to forget the last line and not understand why we’re hitting the real service instead of the mock.)

I’ve made myself a little shortcut:

Once that code is in my base TestCase class, I can use the following code in my test cases:

It’s not a huge simplification, but I do think it’s a little easier to read and easier to not screw up.

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 seeThisBeforeThat() 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.

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.