Completing an integration test with the Pusher JavaScript Library

With so many of us (developers) involved in agile, TDD, BDD and continuous integration and delivery it’s no surprise that we’ve had a few questions posed of us about testing best practices and one question in particular caught my attention and got me thinking about how to complete a web apps integration test with the Pusher JavaScript API. One solution I’ve come up with is to create a Test Stub-type Pusher object and can then be used to simulate events being triggered and pushed into your app.

As with anything in software engineering best practices for testing are not set in stone and opinions will differ considerably. My view of testing is that the types of test can be broken down as follows:

  • Unit tests – test single components
  • Integration tests – test how components interact and in some cases components might involve IO and HTTP calls
  • Acceptance tests – full end to end testing against a full running application including automated UI tests

You may have seen the various incarnations of the test pyramid, some of which specifically mention integration tests.

Integration tests can provide you with confidence that two or more components work and communicate together as expected. The tests might run against a Data Access Layer that talks directly to the database, a File Management System that manages files across a network or a third party asynchronous evented API. In all cases it can be argued to be very valuable to have a suite of tests that ensure things function as expected. The Pusher JavaScript Library is an example of such a third party library.

The Problem

The problem with doing integration testing with the Pusher JavaScript library is that you either need to the full Pusher stack (your server code, an Internet connection, Pusher, through to your client code) to be in place and exercised as part of the test or you could mock out the WebSocket object. The problem with doing the latter is that you need to write logic that understands the Pusher Protocol and that a Pusherinstance has state (connection state, subscriptions) so for each test there would need to be a bit of set up. Also the logic about state may change in the future meaning that the logic of the test framework would need to change and potentially the test code.

A Solution – Use the Pusher Test Stub for an Integration Test

One solution to this problem is to use a Pusher Test Stub object which replaces the real Pusher object and offers the same interface but provides additional functionality to allow Pusher events to be simulated. The test stub can be used without you needing to update your existing application code at all, as far as it’s concerned it is still integrating with the real Pusher JavaScript library.

The functionality it offers is:

  • A constructor – creates a stub Pusher instance
  • Subscribe to channels – returns a stub channel object
  • Bind to events on channels including presence events
  • Bind to events on the Pusher.connection object
  • Access channels created though calls to subscribe
  • Trigger events on a channel including trigger presence events
  • Trigger events on the connection object

Also, because it’s possible that your code tries to create a new Pusher instance, and because JavaScript is super-funky, it can replace the Pusher reference, and therefore definition, so that whenever new Pusher(...) is called a new Pusher Test Stub object is created.

How to use it

Using the Pusher Test Stub is really simple. You just need to include the script tag and then use some test methods to simulate events. For example, the connection to Pusher can be simulated as follows:

<script src="pusher-test-sub.js"></script>
<script>
  Pusher.instances[0].connection.emit('connected');
</script>

If you know your application has a private-messages channel and that as soon as the app loads the channel is subscribed to you can access this channel and trigger an event on it and supply event data too:

var stub = Pusher.instances[0];
var channel = stub.channel('private-messages');

channel.emit(‘message-updated’, {from: ‘Phil’, text:’Just testing’});

There’s also a static helper function that can be used. It assumes that the event is being triggered from the first Pusher instance but that is normally the case anyway. It allows you to just pass the channel name, the event and the event data:

Pusher.emit('private-messages', 'message-updated', {from: 'Phil', text:'Just testing'});

There are no restrictions on the types of test frameworks that the Pusher Test Stub can be used with but here are two simple examples:

QUnit

If you use something like QUnit to test your code and your app then you can also use the Pusher Test Stub within your tests. Simply trigger events using the stub within your tests and assert your expectations have been met. A simple example might be to ensure that a connection state indicator updates in the UI whenever a state_change event fires on the pusher.connection. The application code for this could look as follows:

var pusher = new Pusher('006c79b1fe1700c6c10d');
pusher.connection.bind('state_change', function() {
  $("#connection_status .status").text(pusher.connection.state);
});

And the QUnit test:

test("UI is updated with connection state", function() {

  var expectedState = 'connected';
  Pusher.instances[0].connection.state = expectedState;
  Pusher.instances[0].connection.emit('state_change');

  var connectionState = $("#connection_status .status").text();
  equal( connectionState, expectedState, "We expect connection state to be '" + expectedState + "'" );
});

You can see this QUnit test along with a few others here.

Selenium

Whether you are writing simple tests using the Selenium IDE or have a full blown Selenium Web Driver suite of tests if you include the Pusher Test Stub (injecting it into the page would be ideal) you can remove the reliance on the full Pusher Stack and just automate the testing of your UI. You can trigger events using the runScript method providing the event triggering JavaScript as shown above and then assert the UI has changed as expected.

You can see an example Selenium test script here.

Are there any drawbacks/alternatives?

One minor drawback is that the Pusher Test Stub API needs to keep inline with the Pusher API. This isn’t too big a deal since the Pusher API is pretty simple and it uses events extensively so new events can easily be triggered.

In Unit Testing Mock Objects are commonly used to test and assert expectations. A JavaScript mock library such as JSMock, jsmocktool, jqMock or Mock4JS could be used but I’d argue that in this integration testing scenario that a Test Stub which can be easily controlled is a good solution.

Get the code

You can find the Pusher Test Stub code in github.

Feedback

I found the Pusher Test Stub quite useful whilst on the train the other day when I had no Internett connection. It let me write tests but also run my application. But, is it of use to you? Would you prefer a different solution. Please let me know by sending an email to support@pusher.com or by leaving a comment.

Ready to begin?

Start building your realtime experience today.

From in-app chat to realtime graphs and location tracking, you can rely on Pusher to scale to million of users and trillions of messages