Faye 0.8: the refactoring

I’m pleased to finally announce the release of Faye 0.8 after a few months of reorganising the 0.7 codebase to make it more modular, and splitting parts of it out into separate projects. Before I get to what’s changed, I’m going to get the API changes out of the way: this is the stuff you need to know if you’re upgrading.

I hate introducing API changes but I’m afraid these really couldn’t be avoided. They’re really configuration changes so you shouldn’t need to change a lot of code. Please get on the mailing list if you have problems.

First, if you’re running the Ruby server you need to tell it which web server you’re using. Faye now supports the Thin, Rainbows and Goliath web servers, and you need to tell the WebSocket layer which set of adapters to load. For a ‘hello world’ app this looks like this:

# config.ru
require 'faye'
Faye::WebSocket.load_adapter('thin')

app = Faye::RackAdapter.new(:mount => '/faye', :timeout => 25)

run app

Depending on whether you run your application with rackup or thin, the load_adapter call might not be strictly necessary, but better to have it in there just in case. See the Faye Ruby docs and the faye-websocket documentation on how to run your app with different Ruby servers.

Second, if you use the Redis backend, you need to install a new library and change the engine.type setting in your server. Instead of specifying the name of the engine, this field now takes a reference to the engine object. In Ruby that looks like this:

# config.ru
# First run: gem install faye-redis

require 'faye'
require 'faye/redis'

bayeux = Faye::RackAdapter.new(
  :mount   => '/',
  :timeout => 25,
  :engine  => {
    :type  => Faye::Redis,
    :host  => 'redis.example.com',
    # more options
  }
)

And on Node:

// First run: npm install faye-redis

var faye  = require('faye'),
    redis = require('faye-redis');

var bayeux = new faye.NodeAdapter({
  mount:    '/',
  timeout:  25,
  engine: {
    type:   redis,
    host:   'redis.example.com',
    // more options
  }
});

Apart from this, Faye 0.8 should be backward-compatible with previous releases.

Having got the administrivia out of the way, what’s new? Well, the main focus of the 0.8 release is modularity. Two major components of Faye – the WebSocket support and the Redis engine – have been split off into their own packages. I’ve been blogging here already about faye-websocket (rubygem, npm package), and the major work over the last few months has gone into making this a really solid WebSocket and EventSource implementation. Because of its new-found freedom outside the main Faye project, it’s been adopted by other projects, notably SockJS, Cramp and Poltergeist. This adoption, particularly from SockJS, has meant more feedback, bug fixes and performance improvements and resulted in a really solid WebSocket library that improves Faye’s performance.

This new library has had a beneficial impact on Faye’s transport layer. faye-websocket is much faster than Faye’s 0.7 WebSocket code, supports EventSource and new WebSocket features, and runs on more Ruby servers: Faye 0.7 was confined to Thin whereas it now also runs on Rainbows and Goliath. On top of this, Faye 0.8 adds a new EventSource-based transport to support Opera 11 and browsers where proxies block WebSocket, and improves how it uses WebSocket. Previously, Faye’s WebSocket transport used the same polling-based /meta/connect cycle as other transports. It was faster than HTTP, but not optimal. Faye 0.8 now breaks out of this request/response pattern and pushes messages to WebSocket and EventSource connections as soon as they arrive at the server, without returning the /meta/connect poll. This results in lower latency, particular when delivering messages at high volume.

The second major change is that the Redis engine is now a separate library, faye-redis (rubygem, npm package). This has two important benefits. First, the main Faye package no longer depends on Redis clients, and in particular the Node version no longer depends on packages with C extensions, so no compiler is needed to install it. Second, it means Faye’s backend is now totally pluggable and third parties can implement their own engines: the API is thoroughly documented for Ruby and Node. The engine is a small piece of code (for example here’s the Ruby in-memory engine) but it really defines Faye’s behaviour. This layer is not concerned with transport negotiation (HTTP, WebSocket, etc) or even the Bayeux protocol format, it just implements the messaging business logic and stores the state of the system. You can easily implement your own engine to run on top of another messaging/storage stack, or change the messaging semantics if you like. Faye has a complete set of tests you can run to check your engine – see the Ruby and Node projects for examples.

Finally, there have been a couple of changes to the client. We’ve switched from exponential-backoff to fixed-interval for trying to reconnect after the client loses its connection to the server, and this interval is configurable using the retry setting:

// Attempts to reconnect every 5 seconds
var client = new Faye.Client('http://example.com/bayeux', {
  retry: 5
});

You can also set headers for long-polling requests on the client; this is useful for talking to OAuth-protected servers, for example:

client.setHeader('Authorization', 'OAuth ' + accessToken);

And finally, there’s a new server-side setting, ping, that controls how often the server sends keep-alive data over WebSocket and EventSource connections. This data is ignored by the client, but helps keep the connection open through proxies that like to kill idle connections.

// Sends pings every 10 seconds

var bayeux = new faye.NodeAdapter({
  mount: '/bayeux',
  ping:  10
});

And that just about wraps things up for this release. Since 0.7.1, the main Faye codebase has shed over 2,000 lines of code into other projects that we can easily ship incremental updates to without affecting Faye itself. It’s more performant, leaner and more modular and I know there are already projects doing cool things with it. If you’re using Faye for interesting projects, I’d love to hear from you on the mailing list.

faye-websocket 0.3: EventSource support, and two more Ruby servers

The latest iteration of faye-websocket has just been released for Ruby and Node, probably the last release before I get back to making progress on Faye itself. It contains two major new features: EventSource support, and support for the Rainbows and Goliath web servers for the Ruby version.

EventSource is a server-push protocol that’s supported by many modern browsers. On the client side, you open a connection and listen for messages:

var es = new EventSource('http://example.com/events');
es.onmessage = function(event) {
  // process event.data
};

This sends a GET request with Content-Type: text/event-stream to the server and holds the connection open, and the server can then send messages to the client via a streaming HTTP response. It’s a one-way connection so the client cannot send messages to the server over this connection; it must use separate HTTP requests. However, EventSource uses a much simpler protocol than WebSocket, and looks more like ‘normal’ HTTP so it has less trouble getting through proxies.

On the server side, supporting this requires a lot of the same code as WebSocket, and I might use it in Faye later on, so I decided to add support for it in faye-websocket. In your Rack app, you can now easily handle EventSource connections using an API similar to WebSocket:

require 'faye/websocket'

App = lambda do |env|
  if Faye::EventSource.eventsource?(env)
    es = Faye::EventSource.new(env)

    # Periodically send messages
    loop = EM.add_periodic_timer(1) { es.send('Hello') }
    
    es.onclose = lambda do |event|
      EM.cancel_timer(loop)
      es = nil
    end

    # Async Rack response
    es.rack_response
  
  else
    # Normal HTTP
    [200, {'Content-Type' => 'text/plain'}, ['Hello']]
  end
end

Just like WebSocket, EventSource is designed as a convenient wrapper around the Rack environment and underlying TCP connection that deals with the wire protocol and connection details for you. It tries not to make any assumptions or force constraints on your application design. There are a lot of WebSocket libraries around whose interfaces look more like Rails controllers; black-box Rack components or full-stack servers that hide the socket object and force you respond to WebSockets on one endpoint, and normal HTTP on another. Faye needs to be able to speak many different transport protocols over a single endpoint, which is why this library is designed to be usable inside any Rack adapter while leaving routing decisions up to you.

The benefit of interacting with the sockets as first-class objects is that you can pass them to other parts of your application, which can deal with them as a simple abstraction. For example, if your application just needs to push data to the client, as many WebSocket apps do, you can maintain a socket pool full of objects that respond to send(). When the application wants to push data, it selects the right connection and calls send() on it, without worrying whether it’s a WebSocket or EventSource connection. This gives you some flexibility around which transport your client uses.

The Node API to this is very similar and both are fully documented on GitHub: Ruby docs, Node docs.

The final big change is that the Ruby version now works under a broader range of web servers; it now supports Rack apps running under Thin, Rainbows and Goliath. Hopefully, providing a portable socket implementation that’s easy to drop into any Rack app will open up possibilities for more portable async app frameworks, decoupling the application from the network transport just as Rack did for HTTP. faye-websocket extracted its initial Thin and Rainbows adapters from the Cramp project, and there’s a chance Cramp can now remove its WebSocket code and rely on a simple abstraction for binding the app framework to the web.

As usual, download from Rubygems and npm, and get on the Faye mailing list if you have problems.

Black-box criteria

Tim Bray recently published an article called Type-System Criteria, in which he makes the argument that Java, or statically-typed languages in general, is better-suited to mobile development than the dynamically-typed languages that are more prevalent in web development circles. The reason he gives for this boils down to API surface size:

Another observation that I think is partially but not entirely a consequence of API scale is testing difficulty. In my experience it’s pretty easy and straightforward to unit-test Web Apps. There aren’t that many APIs to mock out, and at the end of the day, these things take data in off the wire and emit other data down the wire and are thus tractable to black-box, in whole or in part.

On the other hand, I’ve found that testing mobile apps is a major pain in the ass. I think the big reason is all those APIs. Your average method in a mobile app responds to an event and twiddles APIs in the mobile framework. If you test at all completely you end up with this huge tangle of mocks that pretty soon start getting in the way of seeing what’s actually going on.

The argument goes that, as the API surface you need to integrate with becomes larger, so static type systems become more attractive. I don’t disagree, in part because I don’t have nearly enough experience with static languages to have an informed opinion on them. But at a gut level I believe this to be true, in fact I’d be willing to bet that a majority of the bugs I’ve written while refactoring software could have been caught by a static type checker (and not even a very sophisticated one, at that).

But the excerpt I quoted above contains a code smell, and it points to another reason why mobile development is difficult. It’s not the size of the APIs that’s the big problem: it’s the nature of the application.

Web application servers are comparatively easy to test because the tests can be written by talking to an encapsulated black box. You throw a request (or several) at a web server, you read what comes back, and check it looks like what you expected. On the other hand, testing web application clients is much more complex: instead of doing simple call/response testing, you have to initiate events within the application’s environment, and then monitor changes to that environment that you expect the events to cause. The core difference here is that client-side programs tend to be what I’m going to refer to as ‘stateful user interfaces’, and mobile (and desktop) software falls into the same category.

What exactly do I mean by ‘stateful user interface’? When you call a web server, you don’t need to hold onto any state on your end: you ask the server a question by sending it a request, and it sends back a fully-formed, self-contained response. When you’ve checked that response, you throw it away and start the next test. In contrast, stateful user interfaces are long-running processes in which incremental changes are made to what the user sees. Instead of getting a fresh new page, just a part of the view is changed, or a sound is emitted, or a notification generated, or a vibration initiated. The programming paradigm in a server environment emphasises call/response, statelessness and immutability; in a client environment you have side effects, state and incremental change. Testing in such environments is hard.

I think this, rather than large API surface, is the real problem. Large API surfaces are only a problem if your application code talks to them directly, and this is much more common in side-effect-heavy applications. Unit tests in these environments tend to be messy for several reasons:

  • Application code responds to events triggered by the host environment
  • Business logic produces its output by modifying the host environment rather than returning values
  • It is hard or impossible to reset the environment to a clean state between tests

The third reason is a particular problem when unit testing client-side JavaScript, and I’ve seen plenty of tests where the state of the page or the implementation of event listeners is such that it becomes very difficult to keep each test independent of the others. You also have the problem that anything that causes a page refresh will cause your test runner to vanish. (I wrote about this exact problem in Refactoring towards testable JavaScript.)

So if side-effect-heavy programs cause large API surfaces to be a problem, what should we do about it? The answer comes down to something I think of as ‘avoiding framework-isms’. This means that any time you have a framework or host environment in which user input or third-party code drives your application, the sooner you can dispatch to something you control the better. The classic example of this is the ‘fat model, skinny controller’ mantra popular in the Rails community: rather than dump lots of code in a controller that’s only invoked by the host server and framework, turn the request into calls to models. This way, the bulk of the logic is in objects that you control the interface to, and that are easy to create and manipulate, properties that also make them easy to test.

In client-side JavaScript and other stateful user interfaces, this means keeping event listeners small. Ideally an event listener should extract all the necessary data from the event and the current application state, and use this to make a black-box call to a module containing the real business logic. It means making sure orthogonal components of a user interface do not talk to each other directly, but publish data changes via a message bus. And it means writing business logic that returns results rather than causes side-effects; the side-effects again being dealt with by thin bindings to the host environment.

I’ll finish up with a small but illustrative example. Say you’re writing a WebSocket implementation, and the protocol mandates that when you call socket.send('Hello, world!') then the bytes 81 8d ed a3 88 c3 a5 c6 e4 af 82 8f a8 b4 82 d1 e4 a7 cc should be written to the TCP socket. You could write a test for it by mocking out the whole network stack (which I’ve probably glossed over considerably here):

describe WebSocket do
  before do
    @tcp_socket = mock('TCP socket')
    TCP.should_receive(:connect).with('example.com', 80).and_return @tcp_socket
    @web_socket = WebSocket.new('ws://example.com/')
  end
  
  it "writes a message to the socket" do
    @tcp_socket.should_receive(:write).with [0x81, 0x8d, 0xed, 0xa3, 0x88, 0xc3, 0xa5, 0xc6, 0xe4, 0xaf, 0x82, 0x8f, 0xa8, 0xb4, 0x82, 0xd1, 0xe4, 0xa7, 0xcc]
    @web_socket.send("Hello, world!")
  end
  
  # More mock-based protocol tests...
end

Or you could test it by implementing a pure function that turns text into WebSocket frames, leaving the code that actually deals with networking doing only that and nothing else:

describe WebSocket::Parser do
  before do
    @parser = WebSocket::Parser.new
  end
  
  it "turns text into message frames" do
    @parser.frame("Hello, world!").should == [0x81, 0x8d, 0xed, 0xa3, 0x88, 0xc3, 0xa5, 0xc6, 0xe4, 0xaf, 0x82, 0x8f, 0xa8, 0xb4, 0x82, 0xd1, 0xe4, 0xa7, 0xcc]
  end
  
  # More protocol implementation tests...
end

describe WebSocket do
  before do
    @tcp_socket = mock('TCP socket')
    TCP.should_receive(:connect).with('example.com', 80).and_return @tcp_socket
    
    @parser = mock('parser')
    WebSocket::Parser.should_receive(:new).and_return @parser
    
    @web_socket = WebSocket.new('ws://example.com/')
  end
  
  it "converts text to frames and sends them" do
    frame = mock('frame')
    @parser.should_receive(:frame).with("Hello, world!").and_return frame
    @tcp_socket.should_receive(:write).with(frame)
    @web_socket.send("Hello, world!")
  end
  
  # And we're done here
end

This separates the business logic (implementing the WebSocket protocol) away from the side effects to the host environment (writing to network connections). This results in code that’s more modular, much easier to test, and less coupled to the API surface of the host environment. If a static type system helps you with that then have at it, but recognize when it’s a symptom of a deeper problem.

faye-websocket 0.2: big performance boost, and subprotocol support

I’ve just released the 0.2 version of faye-websocket for Ruby and Node. This release benefits from the fact that the SockJS project is now using faye-websocket to handle WebSocket connections; my thanks to them for finding the performance bugs and missing features that went into making this release.

The biggest difference in this release is performance. In 0.1, the Node version had a rather interesting performance profile and was pretty slow. We’ve now made a bunch of optimisations that give it a more predictable performance profile across message sizes, and increases performance across the range by orders of magnitude.

The following benchmarks were produced using the ws client to send 1000 messages of various sizes to an echo server:

Benchmarks for Node 0.6.6

On Ruby, the change is not so dramatic but for most message sizes performance is improved by at least a factor of 2. This is in part achieved by writing part of the parser in C; this being my first Ruby C extension there may be problems with it so please get on the mailing list if you find any.

Benchmarks for Ruby 1.9.3

There is one new feature in the form of Sec-WebSocket-Protocol support. With the latest WebSocket protocol, the one currently shipping in Chrome and Firefox, you can specify which application protocol(s) you want to use over the socket, for example:

// client-side
var ws = new WebSocket('ws://example.com/', ['irc', 'xmpp']);

On the server side, you can specify which protocols the server supports and the first of these that matches a protocol supported by the client will be selected and sent back to the client as part of the handshake.

// server-side
var ws = new WebSocket(request, socket, head, ['bayeux', 'irc']);
ws.protocol // -> 'irc'

I’m really pleased with the progress on this project since decoupling it from Faye, and stoked that SockJS has adopted it. They’ve helped me improve things a great deal just in the last few days and it’s great to know these changes will go into making Faye faster.

Announcing faye-websocket, a standards-compliant WebSocket library

While announcing last week’s release of Faye 0.7, I mentioned a couple of things: first, Faye now exposes its WebSocket APIs for everyone to use, and second that I planned to extract some components in order to slim Faye down for the 0.8 release.

Well the first step in that process is now done, and faye-websocket packages are now available for Node and Ruby, installable using npm install faye-websocket and gem install faye-websocket respectively. This process has already shrunk the Faye codebase by over 2,300 LOC but I suppose you’re still wondering why we need another WebSocket library.

It was never my intention to turn Faye into a plain-WebSocket project. I just implemented enough of the protocol to get Faye working, and left it at that. But recently I’ve taken a look around at the other packages for both Node and Ruby, and it turns out pretty much all of them suffer from at least one of the following problems:

The first is a simple matter of maintenance; I could contribute patches to projects to make them work. And it turns out you can’t take a piecemeal approach to implementing wire protocols, you need to deal with whatever might come your way. This is the Internet, after all. I found several libraries where sending them totally valid WebSocket data causes them to crash, simply because they are incomplete.

The second is somewhat trickier, depending on the nature of the project. Some projects are simple WebSocket implementations. Some are transport abstractions aiming to provide a socket-like interface on top of other protocols. And some are full-blown messaging systems with semantics over and above those provided by WebSockets. Faye falls into the latter camp, although its WebSocket code has always been decoupled from the rest of the codebase.

The third is harder to fix, though. If you change the API for a project, you affect all its users. Its author probably has reasons for the API they picked, reasons you weren’t privy to when examining potential use cases. Patches that change the user-facing API of a library are much less likely to be accepted.

But the problem posed by non-standard APIs is, to me, quite serious. The Web Standards movement came about because web developers wanted to write portable code, and have a reasonable expectation of that code working when delivered to users. The WebSocket API is part of that same effort, to provide Web authors with standard interfaces to code against in order to produce portable software. As I have ranted about at length before, ignorance of code portability constitutes a missed opportunity for anyone developing Node programs.

Every time somebody invents a new API for a standard piece of Web infrastructure, they make everyone else’s code less portable. The aim with faye-websocket is to keep the standard WebSocket API so that code developed for the browser can be reused on the server. For example, the Faye pub/sub messaging client uses a ‘transport’ object to send messages to the server; there’s a transport based on XMLHttpRequest, one based on JSON-P, one based on Node’s HTTP library, and one based on WebSockets. If using an HTTP transport, one must pick whether to use XMLHttpRequest or Node HTTP, based on the environment. With WebSockets, we can reuse the same transport class on the server and in the browser, because Faye’s WebSockets have the same API you get in the browser. This means less code to write, and more code you can automatically test from the command line.

And this doesn’t just apply to WebSocket clients. After the handshake, the WebSocket protocol is symmetric: both peers can send and receive messages with the same protocol operating in both directions. It seems to me that any code based on WebSockets ought to be equally happy handling a server-side connection or a client-side one, since both ends of the connection have exactly the same capabilities. For this reason, faye-websocket wraps server-side connections with the standard API:

var WebSocket = require('faye-websocket'),
    http      = require('http'),
    server    = http.createServer();

server.addListener('upgrade', function(request, socket, head) {
  var ws = new WebSocket(request, socket, head);
  
  ws.onmessage = function(event) {
    ws.send(event.data);
  };
  
  ws.onclose = function(event) {
    console.log('close', event.code, event.reason);
    ws = null;
  };
});

server.listen(8000);

Server-side connections transparently select between draft-75, draft-76 or hybi-07-compatible protocols and handle text, binary, ping/pong, close and fragmented messages. The client is hybi-08, but has been tested up to version 17.

Of course, nothing is ever perfect on the first release but faye-websocket does have one of the most complete protocol implementations around, and I’d like to see more implementations adopt the standard API. The standard interfaces might not be to your taste, but they benefit the ecosystem by giving everyone a predictable playing field. If you maintain a WebSocket project, please test it using the Autobahn test suite (here are Faye’s server and client results) and consider exposing the standard API to your users.

Faye 0.7: new event APIs and an open WebSocket stack

I’m very excited to announce the release of Faye 0.7, available now through gem and npm. This release focuses on two main areas: new event APIs for hooking into the framework, and a polished stand-alone WebSocket implementation.

Let’s deal with the new APIs first. The main one is an API for listening to what’s going on inside the engine. People have historically used extensions for various sorts of monitoring but there are certain events extensions can’t catch. For example, when a client session ends due to inactivity rather than because the client sent a /meta/disconnect message, there’s no way to detect that. Well now you can:

var bayeux = new Faye.NodeAdapter({mount: '/faye', timeout: 45});

bayeux.bind('disconnect', function(clientId) {
  // event listener logic
});

There’s a complete API for listening to all the major events in Faye’s pub/sub model, and I encourage you to use it over extensions if all you want is a little monitoring.

The second new event API is on the client side. Faye will always attempt to reconnect for you if the network connection is dropped, but often it’s useful to signal the connection loss to the user. You can now do this using these events on the client:

client.bind('transport:down', function() {
  // Fires when the connection is lost
});
client.bind('transport:up', function() {
  // Fires when the connection is established
});

You don’t need to reconnect the client yourself, these events are just there to notify you of what’s going on. And to give you even more feedback, publish() now supports the same Deferrable API as subscribe().

Now onto the WebSocket changes. The first of which is that Faye now includes a WebSocket client, and will use this instead of HTTP for server-side clients. The server-side HTTP transports now support cookies, and all transports support SSL. The WebSocket stack has received a lot of bug fixes based on the Autobahn test suite, and is now one of the most complete WebSocket implementations available for Node or Ruby. (Want proof? Here’s the server and client test report.)

Faye’s WebSocket stack also has the advantage of having been reasonably decoupled from the rest of the codebase since it was first introduced in version 0.5, and now I’ve taken the decision to open it up for everyone to use. It makes adding WebSocket support to existing Node and Rack apps very easy indeed; you can drop sockets right into your existing application and they expose the same API you use in the browser:

var http = require('http'),
    faye = require('faye');

var server = http.createServer();

server.addListener('upgrade', function(request, socket, head) {
  var ws = new faye.WebSocket(request, socket, head);

  ws.onmessage = function(event) {
    ws.send(event.data);
  };

  ws.onclose = function(event) {
    console.log('close', event.code, event.reason);
    ws = null;
  };
});

server.listen(8000);

The sockets implement the standard WebSocket API on both the server- and client-side. They handle text and binary data, and transparently handle ping/pong, close, and fragmented messages for you. The server supports a wide range of protocol versions and the client should be able to talk to any modern WebSocket server.

If, however, you don’t want to use WebSockets for your Faye client, you can easily switch them off:

client.disable('websocket');

I think the WebSocket tools are important because while there are plenty of messaging frameworks and transport abstractions around (e.g. libraries that present the WebSocket API on top of other wire transports), the state of pure WebSocket implementations seems to be somewhat lacking at the moment. Hopefully this can improve the situation as we move towards broader socket support across the web.

So that’s about it for the 0.7 release. Work is already underway on the 0.8 release, and the current plan is to focus on refactoring. The Faye distribution contains several components, such as the WebSocket code and the Redis backend, that can be broken off into separate projects. Indeed, work is already underway on extracting the WebSocket code for Ruby. As usual, this release should be backward compatible but if you have problems don’t hesitate to post on the mailing list.

Of objects and operators

I was bitten this week by some unexpected behaviour in RSpec. I can’t remember the specifics of the problem but here’s a very similar one: when a Faye client connects to the server, it must perform what’s called a handshake. It sends the server a message and the server responds by sending a randomly generated client ID back. We don’t care what the ID is, we just care about the format of the response. So we do something like this (greatly simplified of course):

describe Faye::Server do
  include Rack::Test::Methods
  let(:app) { Faye::Server.new }
  
  describe :handshake do
    before do
      post '/bayeux', :message => '{"channel":"/meta/handshake"}'
    end
    
    it "returns a clientId" do
      json = JSON.parse(last_response.body)
      json.should == hash_including('clientId' => instance_of(String))
    end
  end
end

Let’s assume our server works properly, and returns a response body of {"clientId":"abc123"}. Our test will end up doing this:

{'clientId' => 'abc123'}.should == hash_including('clientId' => instance_of(String))
RSpec::Expectations::ExpectationNotMetError:
expected: #<RSpec::Mocks::ArgumentMatchers::HashIncludingMatcher:0x00000001be5ab8 @expected={"clientId"=>#<RSpec::Mocks::ArgumentMatchers::InstanceOf:0x00000001be65f8 @klass=String>}>
     got: {"clientId"=>"abc123"} (using ==)
Diff:
@@ -1,6 +1,2 @@
-#<RSpec::Mocks::ArgumentMatchers::HashIncludingMatcher:0x00000001be5ab8
- @expected=
-  {"clientId"=>
-    #<RSpec::Mocks::ArgumentMatchers::InstanceOf:0x00000001be65f8
-     @klass=String>}>
+{"clientId"=>"abc123"}

What happens if we swap the operands of == around?

hash_including('clientId' => instance_of(String)).should == {'clientId' => 'abc123'}
#=> true

So on the one hand, we’re told that A != B, but somehow B == A. What’s going on here? Well, you no doubt recognise hash_including and instance_of from RSpec’s mocking framework. It lets you write stubs like this:

o = Object.new
o.stub(:foo).with(instance_of String).and_return :chunky
o.stub(:foo).with(hash_including 'clientId' => 'abc123').and_return :bacon

o.foo('hello') #=> :chunky
o.foo('clientId' => 'abc123') #=> :bacon

These methods create matchers: objects that are used to pattern-match incoming arguments and dispatch the correct return value. They work by implementing custom equality methods that RSpec uses to tell which argument list matches the arguments passed to the method.

instance_of(String) == 'hello'
#=> true
hash_including('clientId' => 'abc123') == 'hello'
#=> false
instance_of(String) == {}
#=> false
hash_including('clientId' => 'abc123') == {'clientId' => 'abc123'}
#=> true

But if you try placing these matchers on the right-hand-side of an equality expression, something weird happens:

'hello' == instance_of(String)
#=> false

These things ought to be equal, but they’re not, and the reason they’re not is quite simple: == is implemented as a method.

In object-oriented programming, a method is just a function attached to some object. When it is invoked, it has access to the object it is attached to, and all its state, as well as the arguments passed into the method. In Ruby, the equality method looks like this:

class MyClass
  def ==(other)
    # return true or false
  end
end

Now broadly speaking, I like programming this way. I find objects to be a useful way of organising the concepts in my programs into thematically linked groups. Methods let you tell objects what to do, and they let you ask objects questions. But what they don’t do terribly well is let you implement operators.

On the surface, it seems like a natural fit: invoking an operator could be seen as asking an object, “Are you equal to this other object?”. Letting your custom object types implement this question seems like a good idea, as it lets the language’s built-in idea of equality be extended. But the equality operator should not work this way: the question of whether two objects are equal is related to the concept of equality, not to the whims of either of the operands. By implementing equality as a method, we make one object entirely responsible for the question of whether two objects are equal, and it turns out this is not easily extensible.

Many operators have properties that are hard to get right if implemented as methods, and the most troublesome of them all is commutativity. Operators such as equality, addition and scalar multiplication have the property that the order of the operands does not matter. A + B = B + A and if A = B then B = A. It is not hard to see how, as we introduce more and more types of objects into a system, each type needs to know how to interact with every other type. In our example above, RSpec matchers know how to compare themselves to other Ruby objects, but the objects have no idea what an RSpec matcher is and so declare themselves not equal. Ruby provides a mechanism for working around this with the coerce method, but even this has a problem. Users need to remember to defer to it if given an unknown object type, it’s easy to create an infinite loop if both parties defer responsibility, and clearly many built-in Ruby objects don’t use it to defer equality decisions.

This little story highlights two key problems. First, given the semantics of operators in Ruby, RSpec’s assertion mechanism is unreliable. By placing the value under test on the left-hand-side of the a.should == b expression, we make it responsible for deciding whether it is a valid value or not when really that decision should be up to b, the expected value. This is one reason that I prefer Test::Unit‘s assert_equal, which places the expected value first and invokes == on it rather than on the value under test. I’ve been told that custom RSpec matchers can solve this problem but really that feels like far too much ceremony when a simple function call will suffice.

Second, the more insidious problem that we’ve seen repeated in Dart this week: operators should not be methods. Making one operand responsible for the whole operation might seem convenient but it does not scale as your program grows. A more sensible approach is to adopt a dispatch table where you can dynamically register new type signatures for existing operators; the closest thing I’ve seen to this is Clojure’s multimethods. I won’t say they’re the answer as I’ve not really used Clojure in anger, but they do look like a better solution than jamming operators into the ‘everything is an object’ model.

Unfortunately, many things that are bad ideas in theory turn out to be useful in practise, and until we come up with convenient ways of doing the right thing, we’ll be lumbered with bad design decisions in our programming languages.

Faye 0.6.4 keeps your WebSockets healthy

Update: Please skip past this version and use 0.6.5, which fixes a bunch of WebSocket string encoding problems.

With the release of Firefox 6, and the pending release of Chrome 14, we’re beginning to see people using browsers that move past the now-expired draft 76 of the WebSocket protocol and use one of the latest revisions of the protocol, which is now expected to be finalized without too many more adjustments. Those of you using Faye 0.6.3 or earlier will see the connection falling back to plain HTTP long-polling in these newer browsers since the new protocol is very different to the old one and their attempts to connect to Faye’s old socket server fail.

Faye 0.6.4 is a bug fix release, although fixing the ‘bug’ involves quite a lot of new code. It includes support in both the Node and Ruby versions for the WebSocket protocol in Chrome 14 and Firefox 6, so users of new browsers should continue to see minimal latency from your Faye apps.

If you want to test it out, you can grab it using gem install faye or npm install faye. There are no API changes but the introduction of new protocols means stuff might break. If you find bugs, please report them on GitHub and roll back to 0.6.3. Faye will continue to fall back to HTTP in cases where WebSockets are not usable, so your users shouldn’t notice any problems.

Terminus 0.3: control multiple browsers with Ruby

As you’ll have noticed if you made it to the end of my last post, there is a new release of Terminus. Terminus is a Capybara driver that is designed to let you control your app in any browser on any device, by sending all driver instructions to be executed on the client side in JavaScript.

This release is the first since Capybara 1.0, and supports the entire Capybara API. This includes:

  • Reading response headers and status codes
  • Handling cookies
  • Running JavaScript and receiving the results
  • Resynchronizing XHR requests (jQuery only)
  • Switching between frames and windows
  • Detecting infinite redirects

This is a superset of the supported features of the Rack::Test and Selenium drivers, and has the added bonus of letting you switch between browsers. When you have multiple browsers connected to your Terminus server, you can select which one you want to control by matching on the browser’s name, OS, version and current URL, for example:

Terminus.browser = {:name => /Safari/, :current_url => /pitchfork.com/}

You can select any browser that is ‘docked’, i.e. idling on the Terminus holding page:

Terminus.browser = :docked

Or simply by selecting one browser from the list:

Terminus.browser = Terminus.browsers.first

All this lets you control multiple browsers at once, for example I’ve been using it to automate some of the Faye integration tests:

#================================================================
# Acquire some browsers and log into each with a username

NAMES = %w[alice bob carol]
BROWSERS = {}
Terminus.ensure_browsers 3

Terminus.browsers.each_with_index do |browser, i|
  name = NAMES[i]
  puts "#{name} is using #{browser}"
  BROWSERS[name] = browser
  Terminus.browser = browser
  visit '/'
  fill_in 'username', :with => name
  click_button 'Go'
end

#================================================================
# Send a message from each browser to every other browser,
# and check that it arrived. If it doesn't arrive, send all
# the browsers back to the dock and raise an exception

BROWSERS.each do |name, sender|
  BROWSERS.each do |at, target|
    next if at == name
    
    Terminus.browser = sender
    fill_in 'message', :with => "@#{at} Hello, world!"
    click_button 'Send'
    
    Terminus.browser = target
    unless page.has_content?("#{name}: @#{at} Hello, world!")
      Terminus.return_to_dock
      raise "Message did not make it from #{sender} to #{target}"
    end
  end
end

#================================================================
# Re-dock all the browsers when we're finished

Terminus.return_to_dock

So what’s not supported? Internet Explorer is still not supported because I cannot find a decent way to run XPath queries on it. I was working on Pathology to solve this but I can’t get it to perform well enough for the workload Capybara throws at it. It might be possible to work around this by monkey-patching Capybara to pass through CSS selectors instead of compiling them to XPath, though. File attachments are not supported for security reasons, and there are still some bugs that show up if you do stuff you’re not supposed to, like using duplicate element IDs. These are particularly apparent on Opera. And finally visiting remote hosts outside your application is supported but is not particularly robust as yet.

You can find out more and see a video of it in action on its new website.

Refactoring towards testable JavaScript, part 3

This article is one in a 3-part series. The full series is:

We finished up the previous article having separated the business logic from the DOM interactions in our JavaScript, and adjusted our unit tests to take advantage of this. In the final part of this series, we’ll take a look at how to take the tests we have and run them across a range of browsers automatically to give us maximum confidence that our code works.

To automate cross-browser testing, I use a much-overlooked tool called TestSwarm. Developed by John Resig for testing jQuery, it takes care of tracking your test status in multiple browsers as you make commits to your project.

To set it up, we need to clone it from GitHub and create a directory within to host revisions of our project.

$ git clone git://github.com/jquery/testswarm.git
$ cd testswarm
$ cp config/config-sample.ini config.ini
$ mkdir -p changeset/jsapp

You’ll need to create a MySQL database for it:

CREATE USER 'testswarm'@'localhost' IDENTIFIED BY 'choose-a-password';
CREATE DATABASE testswarm;
GRANT ALL ON testswarm.* TO 'testswarm'@'localhost';

Then import the TestSwarm schema:

$ mysql -u testswarm -p testswarm < config/testswam.sql
$ mysql -u testswarm -p testswarm < config/useragents.sql

Once you’ve added the database details to config.ini and set up an Apache VHost, you can visit your TestSwarm server and click ‘Signup’. Once you’ve filled in that form you’ll be able to grab your auth key from the database:


$ mysql testswarm -u testswarm -p
mysql> select auth from users;
+------------------------------------------+
| auth                                     |
+------------------------------------------+
| a962c548c22a591e8f150b9d9f6b673b6f212d08 |
+------------------------------------------+

Keep that auth code somewhere as you’ll need it later on. Now before we go any further, to show how TestSwarm works I want to deliberately break our application so that it doesn’t work in Internet Explorer. Do this I’m going to replace jQuery#bind with HTMLElement#addEventListener, and when we push our code to TestSwarm we should see it break.

To get our tests running on TestSwarm, we need a config file. I just grabbed one of the standard Perl scripts from the TestSwarm project and added my own configuration. This tells the script where your TestSwarm server is, where your code should be checkout out, any build scripts you need to run, which files to load, etc. JS.Test includes TestSwarm support baked in so we don’t need to modify our tests at all to make them send reports to the TestSwarm server, we just need to load the same old spec/browser.html file we’ve been using all along. You should configure the Perl script to clone your project into the changeset/jsapp directory we created earlier: this is in TestSwarm’s public directory so web browsers will be able to load it from there. You’ll need to include the auth key we created earlier to submit jobs to the server.

Having created this file, we clone the project on our server somewhere and create a cron job to periodically update our copy and run the TestSwam script: this means that new test jobs will be submitted whenever we commit to the project.

# crontab
* * * * * cd $HOME/projects/jsapp && git pull && perl spec/testswarm.pl

If you now open a couple of browsers and connect to the swarm, you’ll see tests begin to run. If you inspect the test results for our project you should see this:

The green box is Chrome reporting 5 passed tests, and the black box is IE8 reporting 7 errors. If we click through we see what happened:

Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)

  • Error:
    FormValidator with valid data displays no errors:
    TypeError: Object doesn’t support this property or method
  • Error:
    FormValidator with an invalid name displays an error message:
    TypeError: Object doesn’t support this property or method
  • Error:
    FormValidator with an invalid email displays an error message:
    TypeError: Object doesn’t support this property or method
  • Error:
    FormValidator with an invalid argument displays an error message:
    TypeError: Object doesn’t support this property or method
  • Error:
    FormValidator with an invalid name displays an error message:
    TypeError: Object doesn’t support this property or method
  • Error:
    FormValidator with an invalid name displays an error message:
    TypeError: ‘submit’ is undefined
  • Error:
    FormValidator with an invalid name displays an error message:
    TypeError: ‘error’ is undefined

5 tests, 4 assertions, 0 failures, 7 errors

“Object doesn’t support this property or method” is IE’s way of saying you’re calling a method that doesn’t exist, in our case addEventListener(). If we make some changes so that we use attachEvent() instead in IE, when TestSwam picks up the change and runs our tests they go green in IE.

You can leave any number of browsers connected to the swarm and they will run tests automatically as you make commits to the project. This is the great advantage of sticking to portable unit tests for your JavaScript, it makes this kind of automation much easier, you run tests in real browsers and don’t need a lot of additional tooling to set up fake environments. I run the JS.Class tests this way and it’s great for making sure my code works across all platforms before shipping a release.

The final big win, having set all this up, is that we can now delete some of our full-stack tests since they just duplicate our unit tests. When we’re testing a full-stack integration, we really just want to test at the level of abstraction the integration works at, i.e. test the glue that binds the pieces together, rather than testing all the different cases of every piece of business logic. In our case, this means testing two broad cases: either the form is valid or it is not valid. We have unit tests that cover in detail what ‘valid’ and ‘invalid’ mean, and we don’t need to duplicate these. We just need to test their effect on the application as a whole: either the form submits or it doesn’t. We can then instantly discard all the integration tests that cover the validation details, leaving the broad cases covered. Doing this rigorously will keep your integration tests to a minimum and keep your build running quickly.

To wrap up this series, I thought I’d mention a couple other things we can do with our tests to get extra coverage. The first is the JS.Test coverage tool. If we write our code using JS.Class, we can make the test framework report which methods were called during the test just by adding cover(FormValidator) to the top of the spec. When we run the tests we get a report:

<code>$ node spec/console.js 
Loaded suite FormValidator

Started
....

  +-----------------------------------+-------+
  | Method                            | Calls |
  +-----------------------------------+-------+
  | Method:FormValidator.validate     | 4     |
  | Method:FormValidator#handleSubmit | 0     |
  | Method:FormValidator#initialize   | 0     |
  +-----------------------------------+-------+

Finished in 0.005 seconds
4 tests, 4 assertions, 0 failures, 0 errors</code>

If any of the methods are not called, the process exits with a non-zero exit status so you can treat your build as failing until all the methods are called during the test.

Finally, I have an ongoing experimental Capybara driver called Terminus that lets you run your Capybara-based tests on remote machines like phones, iPads and so on. If we change our Capybara driver as required, we can open a browser on a remote machine, connect to the Terminus server and run the tests on that machine, or on many machines at once if your tests involve communication between many clients.

Here’s the full list of software we’ve used in this series:

  • Sinatra – Ruby web application framework used to create our application stack
  • jQuery – client side DOM, Ajax and effects library used to handle from submissions
  • JS.Class, JS.Test – portable object system and testing framework for JavaScript
  • Cucumber – Ruby acceptance testing framework used for writing plain-text test scenarios
  • Capybara – web scripting API that can drive many different backends
  • Rack::Test – Capybara backend that talks to Rack applications directly with no wire traffic, suited to doing fast, in-process testing, does not support JavaScript
  • Selenium – Capybara backend that runs using a real browser, slower but supports JavaScript
  • Terminus – Capybara backend that can drive any remote browser using JavaScript
  • PhantomJS – headless distribution of WebKit, scriptable using JavaScript
  • TestSwam – automated cross-browser CI server for tracking JavaScript unit tests across the project history

I’ll leave you with a few points to bear in mind to keep your JavaScript unit-testable:

  • Minimize DOM interaction – write your business logic in pure JavaScript, test it server-side, and use a ‘controller’ layer to bind this logic to your UI.
  • Keep controllers DOM focused – in JavaScript, ‘controllers’ in MVC parlance are basically your event handlers. They should handle user input, trigger actions in your business logic, and update the page as appropriate.
  • If you need a browser, use a real one – in my experience, given how easy it is to test on real browsers and minimize integration tests, fake DOM environments are often more pain than they’re worth. The important thing is to keep your code as portable as possible so you can adapt if you spot more suitable tools.