Matthew Daly's Blog

I'm a web developer in Norfolk. This is my blog...

6th December 2018 6:34 pm

Decorating Service Classes

I’ve written before about using decorators to extend the functionality of existing classes, in the context of the repository pattern when working with Eloquent. However, the same practice is applicable in many other contexts.

Recently, I was asked to add RSS feeds to the home page of the legacy project that is my main focus these days. The resulting service class looked something like this:

<?php
namespace App\Services;
use Rss\Feed\Reader;
use App\Contracts\Services\FeedFetcher;
class RssFetcher implements FeedFetcher
{
public function fetch($url)
{
return Reader::import($url);
}
}

In accordance with the principle of loose coupling, I also created an interface for it:

<?php
namespace App\Contracts\Services;
interface FeedFetcher
{
public function fetch($url);
}

I was recently able to add dependency injection to the project using PHP-DI, so now I can inject an instance of the feed fetcher into the controller by typehinting the interface and having it resolve to the RssFetcher class.

However, there was an issue. I didn’t want the application to make multiple HTTP requests to fetch those feeds every time the page loads. At the same time, it was also a bit much to have a scheduled task running to fetch those feeds and store them in the database, since many times that would be unnecessary. The obvious solution was to cache the feed content for a specified length of time, in this case five minutes.

I could have integrated the caching into the service class itself, but that wasn’t the best practice, because it would be tied to that implementation. If in future we needed to switch to a different feed handler, we’d have to re-implement the caching functionality. So I decided it made sense to decorate the service class.

The decorator class implemented the same interface as the feed fetcher, and accepted another instance of that interface in the constructor, along with a PSR6-compliant caching library. It looked something like this:

<?php
namespace App\Services;
use App\Contracts\Services\FeedFetcher;
use Psr\Cache\CacheItemPoolInterface;
class FetcherCachingDecorator implements FeedFetcher
{
protected $fetcher;
protected $cache;
public function __construct(FeedFetcher $fetcher, CacheItemPoolInterface $cache)
{
$this->fetcher = $fetcher;
$this->cache = $cache;
}
public function fetch($url)
{
$item = $this->cache->getItem('feed_'.$url);
if (!$item->isHit()) {
$item->set($this->fetcher->fetch($url));
$this->cache->save($item);
}
return $item->get();
}
}

Now, when you instantiate the feed fetcher, you wrap it in the decorator as follows:

<?php
$fetcher = new FetcherCachingDecorator(
new App\Services\RssFetcher,
$cache
);

As you can see, this solves our problem quite nicely. By wrapping our feed fetcher in this decorator, we keep the caching layer completely separate from any one implementation of the fetcher, so in the event we need to swap the current one out for another implementation, we don’t have to touch the caching layer at all. As long as we’re using dependency injection to resolve this interface, we’re only looking at a little more code to instantiate it.

In addition, this same approach can be applied for other purposes, and you can wrap the service class as many times as necessary. For instance, if we wanted to log all the responses we got, we could write a logging decorator something like this:

<?php
namespace App\Services;
use App\Contracts\Services\FeedFetcher;
use Psr\Log\LoggerInterface;
class FeedLoggingDecorator implements FeedFetcher
{
protected $fetcher;
protected $logger;
public function __construct(FeedFetcher $fetcher, LoggerInterface $logger)
{
$this->fetcher = $fetcher;
$this->logger = $logger;
}
public function fetch($url)
{
$response = $this->fetcher->fetch($url);
$this->logger->info($response);
return $response;
}
}

The same idea can be applied to an API client. For instance, say we have the following interface for an API client:

<?php
namespace Foo\Bar\Contracts;
use Foo\Bar\Objects\Item;
use Foo\Bar\Objects\ItemCollection;
interface Client
{
public function getAll(): ItemCollection;
public function find(int $id): Item;
public function create(array $data): Item;
public function update(int $id, array $data): Item;
public function delete(int $id);
}

Now, of course any good API client should respect HTTP headers and use those to do some caching itself, but depending on the use case, you may also want to cache these requests yourself. For instance, if the only changes to the entities stored by the third party API will be ones you’ve made, or they don’t need to be 100% up to date, you may be better off caching those responses before they reach the actual API client. Under those circumstances, you might write a decorator like this to do the caching:

<?php
namespace Foo\Bar\Services;
use Foo\Bar\Contracts\Client;
use Psr\Cache\CacheItemPoolInterface;
class CachingDecorator implements Client
{
protected $client;
protected $cache;
public function __construct(Client $client, CacheItemPoolInterface $cache)
{
$this->client = $client;
$this->cache = $cache;
}
public function getAll(): ItemCollection
{
$item = $this->cache->getItem('item_all');
if (!$item->isHit()) {
$item->set($this->client->getAll());
$this->cache->save($item);
}
return $item->get();
}
public function find(int $id): Item
{
$item = $this->cache->getItem('item_'.$id);
if (!$item->isHit()) {
$item->set($this->client->find($id));
$this->cache->save($item);
}
return $item->get();
}
public function create(array $data): Item
{
$this->cache->clear();
return $this->client->create($data);
}
public function update(int $id, array $data): Item
{
$this->cache->clear();
return $this->client->update($id, $data);
}
public function delete(int $id)
{
$this->cache->clear();
return $this->client->delete($id);
}
}

Any methods that change the state of the data on the remote API will clear the cache, while any that fetch data will first check the cache, only explicitly fetching data from the API when the cache is empty, and caching it again. I won’t go into how you might write a logging decorator for this, but it should be straightforward to figure out for yourself.

The decorator pattern is a very powerful way of adding functionality to a class without tying it to a specific implementation. If you’re familiar with how middleware works, decorators work in a very similar fashion in that you can wrap your service in as many layers as you wish in order to accomplish specific tasks, and they adhere to the single responsibility principle by allowing you to use different decorators for different tasks.

20th October 2018 2:48 pm

Simplify Your Tests With Anonymous Classes

Anonymous classes were added in PHP7, but so far I haven’t made all that much use of them. However, recently I’ve been working on building a simple dependency injection container for learning purposes. This uses the PHP Reflection API to determine how to resolve dependencies. For instance, if it’s asked for a class for which one of the dependencies required by the constructor is an instance of the DateTime class, it should create an instance, and then pass it into the constructor automatically when instantiating the class. Then it should return the newly created class.

Mocking isn’t really a suitable approach for this use case because the container needs to return a concrete class instance to do its job properly. You could just create a series of fixture classes purely for testing purposes, but that would mean either defining more than one class in a file (violating PSR-2), or defining a load of fixture classes in separate files, meaning you’d have to write a lot of boilerplate, and you’d have to move between several different files to understand what’s going on in the test.

Anonymous classes allow you a means to write simple classes for tests inline, as in this example for retrieving a very basic class. The tests use PHPSpec:

<?php
namespace spec\Vendor\Package;
use Vendor\Package\MyClass;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use DateTime;
class MyClassSpec extends ObjectBehavior
{
function it_can_resolve_registered_dependencies()
{
$toResolve = new class {
};
$this->set('Foo\Bar', $toResolve);
$this->get('Foo\Bar')->shouldReturnAnInstanceOf($toResolve);
}
}

You can also define your own methods inline. Here we implement the invoke() magic method so that the class is a callable:

<?php
class MyClassSpec extends ObjectBehavior
{
function it_can_resolve_registered_invokable()
{
$toResolve = new class {
public function __invoke() {
return new DateTime;
}
};
$this->set('Foo\Bar', $toResolve);
$this->get('Foo\Bar')->shouldReturnAnInstanceOf('DateTime');
}
}

You can also define a constructor. Here, we’re getting the class name of a newly created anonymous class that accepts an instance of DateTime as an argument to the constructor. Then, we can resolve a new instance out of the container:

<?php
class MyClassSpec extends ObjectBehavior
{
function it_can_resolve_dependencies()
{
$toResolve = get_class(new class(new DateTime) {
public $datetime;
public function __construct(DateTime $datetime)
{
$this->datetime = $datetime;
}
});
$this->set('Foo\Bar', $toResolve);
$this->get('Foo\Bar')->shouldReturnAnInstanceOf($toResolve);
}
}

For classes that will extend an existing class or implement an interface, you can define those inline too. Or you can include a trait:

<?php
class MyClassSpec extends ObjectBehavior
{
function it_can_resolve_dependencies()
{
$toResolve = get_class(new class(new DateTime) extends Foo implements Bar {
public $datetime;
public function __construct(DateTime $datetime)
{
$this->datetime = $datetime;
}
use MyTrait;
});
$this->set('Foo\Bar', $toResolve);
$this->get('Foo\Bar')->shouldReturnAnInstanceOf($toResolve);
}
}

In cases where the functionality is contained in a trait or abstract class, and you might need to add little or no additional functionality, this is a lot less verbose than creating a class the conventional way.

None of this is stuff you can’t do without anonymous classes, but by defining these sort of disposable fixture classes inline in your tests, you’re writing the minimum amount of code necessary to implement your test, and it’s logical to define it inline since it’s only ever used in the tests. One thing to bear in mind is that anonymous classes are created and instantiated at the same time, so you can’t easily create a class and then instantiate an instance of it separately. However, you can instantiate one, then use the get_class() function to get its class name and use that to resolve it, which worked well for my use case.

Another use case for anonymous classes is testing traits or abstract classes. I generally use Mockery as my mocking solution with PHPUnit tests, but I’ve sometimes missed the getMockForTrait() method from PHPUnit. However, another option is to instantiate an anonymous class that includes that trait for testing purposes:

<?php
$item = new class() {
use MyTrait;
};

This way, your test class is as minimal as possible, and you can test the trait/abstract class in a fairly isolated fashion.

16th October 2018 9:00 am

Adding React to a Legacy Project

The project I’m currently working on is a textbook example of what happens when a project uses jQuery when it really ought to use a proper Javascript framework, or it starts out just using jQuery and grows out of all proportion. It’s also not helped by the fact that historically it’s just been worked on when new functionality needs to be added, meaning that rather than refactoring the code base, it’s been copied-and-pasted. As a result, there’s lots of repetitive code in desparate need of refactoring, and huge reams of horrible jQuery spaghetti code.

When I first took over responsibility for the project, I integrated Laravel Mix into it so that I had the means to refactor some of the common functionality into separate files and require them during the build process, as well as use ES6. However, this was only the first step, as it didn’t sort out the fundamental problem of repetitive boilerplate code being copied and pasted. What I needed was a refactor to use something more opinionated. As it happened, I was asked to add a couple of modals to the admin, and since the modals were one of the worst parts of the admin in terms of repetitive code, they were a strong candidate for implementing using a more suitable library.

I looked at a few options:

  • I’ve used Angular 1 quite successfully in the past, but I didn’t really want to use a framework that was being killed off, and it would be difficult to retrofit into a legacy application
  • Angular 2+ is actively maintained, but it would again be difficult to retrofit it into a legacy application. In addition, the need for TypeScript would make it problematic.
  • Vue was a possibility, but it did a bit too much for this use case, and it wasn’t all that clear how to retrofit it to an existing application

Eventually, I settled on React.js, for the following reasons:

  • It has a preset in Laravel Mix, making it easy to get started with it.
  • It has a very limited target - React is closely focused on the view layer, dealing only with rendering and event handling, so it does just what I needed in this case.
  • It has a strong record of use with legacy applications - after all, it was created by Facebook and they added it incrementally.
  • It’s easy to test - Jest’s snapshot tests make it easy to verify the rendered content hasn’t changed, and using Enzyme it’s straightforward to test interactions with the component
  • Higher order components provide a straightforward way to share functionality between components, which I needed to allow different modals to deal with another modal in the same way.
  • By creating a series of components for common user interface elements, I could then re-use those components in future work, saving time and effort.

However, it wasn’t entirely clear how I might go about integrating React into a legacy application. In the end, I managed to figure out an approach which worked.

Normally, I would create a single root for my application, something like this:

import React from 'react';
import ReactDOM from 'react-dom';
import App from './components/App';
ReactDOM.render(
<App />,
document.getElementById('root')
);

However, that wasn’t an option here. The existing modals were using jQuery and Bootstrap, and the new modals had to work with them. I therefore needed to have only certain parts of the UI managed with React, and the rest wouldn’t be touched. Here’s an example of how I rendered the modal in the end:

import React from 'react';
import ReactDOM from 'react-dom';
import higherOrderComponent from './components/higherOrderComponent';
import modalComponent from './components/modalComponent';
const Modal = higherOrderComponent(modalComponent);
window.componentWrapper = ReactDOM.render(
<Modal />,
document.getElementById('modalTarget')
);
window.componentWrapper.setState({
foo: 'bar'
});

By extracting the duplicate functionality into a higher order component, I could easily wrap the new modals in that component and share that functionality between the modals. I could then render each component in a different target element, and assign it to a variable in the window namespace. The div with a ID of modalTarget needed to be added in the appropriate place, but otherwise the HTML didn’t need to be touched, since the required markup was in the React component instead.

Then, when I needed to change a value int the statee of the component, I could just call window.componentWrapper.setState({}), passing through the values to set, and these would propogate down to the child components as usual. I could also render multiple different modal components on the page, and refer to each one separately in order to set the state.

This isn’t an approach I’d recommend on a greenfield project - state isn’t really something you should be setting from outside a component like this, and normally I wouldn’t do it. However, it seemed to be the easiest way for this particular use case. Over time I’ll port more and more of the UI over to React, and eventually it won’t be necessary as I’ll be storing the application state in something like Redux.

11th October 2018 9:21 am

Do You Still Need Jquery?

There was a time not so long ago when jQuery was ubiquitous. It was used on almost every website as a matter of course, to the point that many HTML boilerplates included a reference to the CDN.

However, more and more I think it’s probably unnecessary for two main use cases:

jQuery is probably unnecessary for many web apps with simple Javascript

When jQuery first appeared, IE6 was commonplace, and browser API’s were notoriously inconsistent. jQuery was very useful in ironing out those inconsistencies and helping to make the developer’s experience a bit better.

Nowadays, that’s no longer the case. Internet Explorer is on its way out, with IE11 being the only version still supported by Microsoft, and it’s becoming increasingly hard to justify support for older versions, especially with mobile browsers forming a bigger than ever chunk of the market. We’ll probably need to continue supporting IE11 for a good long while, and possibly IE10 for some time too, but these aren’t anything like as bad to work with as IE6. It’s worth noting that newer versions of jQuery are also dropping support for these older browsers, so in many ways it actually does less than it used to.

This is the usual thrust of articles on whether you should still be using jQuery so I’m not going to go over this matter , but for many smaller web apps, jQuery is no longer necessary, and a lot of developers have a tendency to keep using it when it’s probably not required.

jQuery is insufficient for web apps with complex Javascript

Nowadays, there’s a lot of web applications that have moved big chunks of functionality from the server side to the client side. Beyond a certain (and quite small) level of complexity, jQuery just doesn’t do enough to cut it. For me personally, the nature of the projects I work on means that this is a far, far bigger issue than the first one.

I used to work predominantly with Phonegap, which meant that a lot of functionality traditionally done on the server side had to be moved to the client side, and for that jQuery was never sufficient. My first Phonegap app started out using jQuery, but it quickly became obvious that it was going to be problematic. It wound up as a huge mass of jQuery callbacks and Handlebars templates, which was almost impossible to test and hard to maintain. Given this experience, I resolved to switch to a full-fledged Javascript framework next time I built a mobile app, and for the next one I chose Backbone.js, which still used jQuery as a dependency, but made things more maintainable by giving a structure that it didn’t have before, which was the crucial difference.

The more modern generation of Javascript frameworks such as Vue and React, go further in making jQuery redundant. Both of these implement a so-called Virtual DOM, which is used to calculate the minimum changes required to re-render the element in question. Subsequently using jQuery to mutate the DOM would cause problems because it would get out of sync with the Virtual DOM - in fact, in order to get a jQuery plugin working in the context of a React component, you have to actively prevent React from touching the DOM, thereby losing most of the benefits of using React in the first place. You usually see better results from using a React component designed for that purpose (or writing one, which React makes surprisingly simple), than from trying to shoehorn a jQuery plugin into it.

They also make a lot of things that jQuery does trivially easy - for instance, if you want to conditionally show and hide content in a React component, it’s just a case of building it to hide that content based on a particular value in the props or state, or filtering a list is just a case of applying a filter to the array containing the data and setting the state as appropriate.

In short, for single-page web apps or other ones with a lot of Javascript, you should look at other solutions first, and not just blithely assume jQuery will be up to the task. It’s technically possible to build this sort of web app using jQuery, but it’s apt to turn into a morass of spaghetti code unless approached with a great deal of discipline, one that sadly many developers don’t have, and it doesn’t exactly make it easy to promote code reuse. These days, I prefer React for complex web apps, because it makes it extremely intuitive to break my user interface up into reusable components, and test them individually. Using React would be overkill on brochure-style sites (unless you wanted to build it with something like Gatsby), but for more complex apps it’s often a better fit than jQuery.

So when should you use jQuery?

In truth, I’m finding it harder and harder to justify using it at all on new builds. I use it on my personal site because that’s built on Bootstrap 3 and so depends on jQuery, but for bigger web apps I’m generally finding myself moving to React, which renders it not just unnecessary for DOM manipulation, but counter-productive to use it. Most of what I do is big enough to justify something like React, and it generally results in code that is more declarative, easier to test and reason about, and less repetitive. Using jQuery for an application like this is probably a bad idea, because it’s difficult (not impossible, mind, if you follow some of the advice here, use a linter and consider using a proper client-side templating system alongside jQuery) to build an elegant and maintainable Javascript-heavy application.

As a rule of thumb, I find anything which is likely to require more than a few hundred lines of Javascript to be written, is probably complex enough that jQuery isn’t sufficient, and I should instead consider something like React.

I doubt it’d be worth the bother of ripping jQuery out of a legacy application and rewriting the whole thing to not require it, but for new builds I would think very hard about:

  • Whether jQuery is sufficient, or you’d be better off using something like React, Vue or Angular
  • If it is sufficient, whether it’s actually necessary

In all honesty, I don’t think using it when it’s technically not necessary is as much of a big deal as the issue of using it when it’s not really sufficient. Yes, dowloading a library you technically don’t need for a page is a bad practice, and it does make your site slower and harder for users on slow mobile connections, but there are ways to mitigate that such as CDN’s, caching and minification. If you build a web app using jQuery alone when React, Vue or Angular would be more suitable, you’re probably going to have to write a lot more code that will be difficult to maintain, test and understand. Things like React were created to solve the problems that arose when developers built complex client-side applications with jQuery, and are therefore a good fit for bigger applications. The complex setup does mean they have a threshold below which it’s not worth the bother of using them, but past that threshold they result in better, more maintainable, more testable and more reusable code.

Now React is cool, you hate jQuery, you hipster…

Don’t be a prat. Bitter experience has taught me that for a lot of my own personal use cases, jQuery is insufficient. It doesn’t suck, it’s just insufficient. If for your use case, jQuery is sufficient, then that’s fine. All I’m saying is that when a web app becomes sufficiently complex, jQuery can begin to cause more problems than it solves, and that for a sufficiently complex web app you should consider other solutions.

I currently maintain a legacy application that includes thousands of lines of Javascript. Most of it is done with jQuery and some plugins, and it’s resulted in some extremely repetitive jQuery callbacks that are hard to maintain and understand, and impossible to test. Recently I was asked to add a couple of modals to the admin interface, and rather than continuing to add them using jQuery and adding more spaghetti code, I instead opted to build them with React. During the process of building the first modal, I produced a number of components for different elements of the UI. Then, when I built the second one, I refactored those components to be more generic, and moved some common functionality into a higher-order component so that it could be reused. Now, if I need to add another modal, it will be trivial because I already have those components available, and I can just create a new component for the modal, import those components that I need, wrap it in the higher-order component if necessary, and that’s all. I can also easily test those components in isolation. In short, I’ve saved myself some work in the long run by writing it to use a library that was a better fit.

It’s not like using jQuery inevitably results in unmaintainable code, but it does require a certain amount of discipline to avoid it. A more opinionated library such as React makes it far, far harder to create spaghetti code, and makes code reuse natural in a way that jQuery doesn’t.

8th October 2018 11:20 am

An Approach to Writing Golden Master Tests for PHP Web Applications

Apologies if some of the spelling or formatting on this post is off - I wrote it on a long train journey down to London, with sunlight at an inconvenient angle.

Recently I had to carry out some substantial changes to the legacy web app I maintain as the lion’s share of my current job. The client has several channels that represent different parts of the business that would expect to see different content on the home page, and access to content is limited first by channel, and then by location. The client wanted an additional channel added. Due to bad design earlier in the application’s lifetime that isn’t yet practical to refactor away, each type of location has its own model, so it was necessary to add a new location model. It also had to work seamlessly, in the same way as the other location types. Unfortunately, these branch types didn’t use polymorphism, and instead used large switch statements, and it wasn’t practical to refactor all that away in one go. This was therefore quite a high-risk job, especially considering the paucity of tests on a legacy code base.

I’d heard of the concept of a golden master test before. If you haven’t heard of it before, the idea is that it works by running a process, capturing the output, and then comparing the output of that known good version against future runs. It’s very much a test of last resort since, in the context of a web app, it’s potentially very brittle since it depends on the state of the application remaining the same between runs to avoid false positives. I needed a set of simple “snapshot tests”, similar to how snapshot testing works with Jest, to catch unexpected breakages in a large number of pages, and this approach seemed to fit the bill. Unfortunately, I hadn’t been able to find a good example of how to do this for PHP applications, so it took a while to figure out something that worked.

Here is an example base test case I used for this approach:

<?php
namespace Tests;
use PHPUnit_Framework_TestCase as BaseTestCase;
use Behat\Mink\Driver\GoutteDriver;
use Behat\Mink\Session;
class GoldenMasterTestCase extends BaseTestCase
{
protected $driver;
protected $session;
protected $baseUrl = 'http://localhost:8000';
protected $snapshotDir = "tests/snapshots/";
public function setUp()
{
$this->driver = new GoutteDriver();
$this->session = new Session($this->driver);
}
public function tearDown()
{
$this->session = null;
$this->driver = null;
}
public function loginAs($username, $password)
{
$this->session->visit($this->baseUrl.'/login');
$page = $this->session->getPage();
$page->fillField("username", $username);
$page->fillField("password", $password);
$page->pressButton("Sign In");
return $this;
}
public function goto($path)
{
$this->session->visit($this->baseUrl.$path);
$this->assertNotEquals(404, $this->session->getStatusCode());
return $this;
}
public function saveHtml()
{
if (!$this->snapshotExists()) {
$this->saveSnapshot();
}
return $this;
}
public function assertSnapshotsMatch()
{
$path = $this->getPath();
$newHtml = $this->processHtml($this->getHtml());
$oldHtml = $this->getOldHtml();
$diff = "";
if (function_exists('xdiff_string_diff')) {
$diff = xdiff_string_diff($oldHtml, $newHtml);
}
$message = "The path $path does not match the snapshot\n$diff";
self::assertThat($newHtml == $oldHtml, self::isTrue(), $message);
}
protected function getHtml()
{
return $this->session->getPage()->getHtml();
}
protected function getPath()
{
$url = $this->session->getCurrentUrl();
$path = parse_url($url, PHP_URL_PATH);
$query = parse_url($url, PHP_URL_QUERY);
$frag = parse_url($url, PHP_URL_FRAGMENT);
return $path.$query.$frag;
}
protected function getEscapedPath()
{
return $this->snapshotDir.str_replace('/', '_', $this->getPath()).'.snap';
}
protected function snapshotExists()
{
return file_exists($this->getEscapedPath());
}
protected function processHtml($html)
{
return preg_replace('/<input type="hidden"[^>]+\>/i', '', $html);
}
protected function saveSnapshot()
{
$html = $this->processHtml($this->getHtml());
file_put_contents($this->getEscapedPath(), $html);
}
protected function getOldHtml()
{
return file_get_contents($this->getEscapedPath());
}
}

Because this application is built with Zend 1 and doesn’t have an easy way to get the HTML response without actually running the application, I was forced to use an actual HTTP client to fetch the content while the web server is running. I’ve used Mink together with Behat many times in the past, and the Goutte driver is fast and doesn’t rely on Javascript, so that was the best bet for a simple way of retrieving the HTML. Had I been taking this approach with a Laravel application, I could have populated the testing database with a common set of fixtures, and passed a request object through the application and captured the response object’s output rather than using an HTTP client, thereby eliminating the need to run a web server and making the tests faster and less brittle.

Another issue was CSRF handling. A CSRF token is, by definition, generated randomly each time the page is loaded, and so it broke those pages that had forms with CSRF tokens. The solution I came up with was to strip out the hidden input fields.

When each page is tested, the first step is to fetch the content of that page. The test case then checks to see if there’s an existing snapshot. If not, the content is saved as a new snapshot file. Otherwise, the two snapshots are compared, and the test fails if they do not match.

Once that base test case was in place, it was then straightforward to extend it to test multiple pages. I wrote one test to check pages that did not require login, and another to check pages that did require login, and the paths for those pages were passed through using a data provider method, as shown below:

<?php
namespace Tests\GoldenMaster;
use Tests\GoldenMasterTestCase;
class GoldenMasterTest extends GoldenMasterTestCase
{
/**
* @dataProvider nonAuthDataProvider
*/
public function testNonAuthPages($data)
{
$this->goto($data)
->saveHtml()
->assertSnapshotsMatch();
}
public function nonAuthDataProvider()
{
return [
['/login'],
];
}
/**
* @dataProvider dataProvider
*/
public function testPages($data)
{
$this->loginAs('foo', 'bar')
->goto($data)
->saveHtml()
->assertSnapshotsMatch();
}
public function dataProvider()
{
return [
['/foo'],
['/bar'],
];
}
}

Be warned, this is not an approach I would advocate as a matter of course, and it should only ever be a last resort as an alternative to onerous manual testing for things that can’t be tested in their current form. It’s extremely brittle, and I’ve had to deal with a lot of false positives, although that would be easier if I could populate a testing database beforehand and use that as the basis of the tests. It’s also very slow, with each test taking three or four seconds to run, although again this would be less of an issue if I could pass through a request object and get the response HTML directly. Nonetheless, I’ve found it to be a useful technique as a test of last resort for legacy applications.

Recent Posts

Decorating Service Classes

Simplify Your Tests With Anonymous Classes

Adding React to a Legacy Project

Do You Still Need Jquery?

An Approach to Writing Golden Master Tests for PHP Web Applications

About me

I'm a web and mobile app developer based in Norfolk. My skillset includes Python, PHP and Javascript, and I have extensive experience working with CodeIgniter, Laravel, Django, Phonegap and Angular.js.