One of the big things to notice is the constructor. First of all, I take in a bunch of options. If you know anything about Backbone.Marionette’s Application initializers, you’ll know that these options are passed around to everything that is created in the initializers. All you really need to know is that these are global configuration options that pretty much everything in the app knows about.
Within these options is a bit about Socket.IO in the
io property. These options are used for connecting to the server correctly. I also have some default settings and I let the options argument override these settings. You’ll notice that I have the default option for ‘auto connect’ set to
false. This allows me to create a
new Socket() without it necessarily connecting before I need it to.
The other option that I care about is
vent, which is the event hub. I’ve talked about passing this around before when I talked about Dependency Injection. Then, in the constructor, I use my little utility function to bind to the “connect” and “disconnect” events on the socket so that I can use the event hub to alert the rest of the app of the state of the connection.
The rest of
Socket is pretty much just wrapper functions, except
on, which, as I described earlier, will immediately execute a “connect” handler if the socket is already connected.
Because I made this wrapper, I actually had to test it. Normally, there is no reason to actually test third party libraries, unless they weren’t properly tested already. However, you do need to test the functionality of your own code, and you should be sure that the third party library is properly integrated into your system.
Here’s my spec for my Socket wrapper:
The first thing we do is a bit of setup. We create a mock for the event hub, which extends
Backbone.Events so that we actually have the core functionality of the event hub. Then we put together the options. Notice the “force new connection” option. Normally, when you call
io.connect(...) with the same URL as a previous call to it, it’ll return the same old socket that you had before. This is a problem because we want to be able to refresh which events are attached to the socket for each test. That’s where “force new connection” comes in. It forces Socket.IO to create a new socket each time so we don’t have duplicate event handlers registered.
Then we move on to the main
describe block. Inside we create our setup and teardown activities with
beforeEach we instantiate
vent, spy on its main methods, and put it into the options. Then we create a new instance of our
afterEach we use Socket.IO’s synchronous method for disconnecting. We keep it synchronous because that makes it simpler.
I will only go over a few of the specs; you can look through the rest yourself if you want to. If you have any questions about a spec (especially one I didn’t go over), you can just leave a comment below the post.
In the specs that actually connect and/or disconnect from the server, I checked to see when it (dis)connected by calling my own
appSocket.isConnected() method (as you can see on lines 51, 65, 76, 153, etc.) rather than querying the actual socket via
appSocket.socket.connected. This is because I chose to trust that
isConnected works unless the spec for that method told me otherwise. When I was testing
isConnected (lines 81-127), I went to the actual socket to get my information (lines 94, 110, and 120).
If you look through the specs of
isConnected, you’ll see how the asynchronous work is really done. As I described in my Jasmine tutorial, you call
runs. In the first
runs call, you call the asynchronous method (
disconnect in this case). Then in
waitsFor, you run the checks to detect if that operation finished. Finally, the second time you call
runs, you can test to make sure the spec passed.
In the case of the spec starting at line 102, I need to disconnect, but in order to disconnect I need to connect first. So that is two asynchronous functions being run in the same spec. In these instances, you can continue to chain
runs on to the end until you’ve completed all of your asynchronous tasks. So I connect, wait for it to finish connecting, then disconnect and wait for that to finish, and then test to see if the spec passes.
When I tested
on (lines 129-198) you’ll notice that I didn’t actually test to see if Socket.IO would call the handlers after an event from the server came back. This is because I have no control over the server (with the exception of connecting and disconnecting, which I do test to make sure the handlers are called). This is also because I would be testing the third party library, rather than my own code, which I already said was unnecessary. In this case, I just made sure that the event handlers were properly attached to the true socket that I got from Socket.IO. You may also notice that the tests for
emit, on lines 200-210, don’t actually test to see if anything was sent to the server. There are two reasons for this: 1) I didn’t connect to the server for that example, so I know nothing would have been sent and 2) my code doesn’t send anything to the server; Socket.IO does. Once again, I just need to make sure things are properly delegated to Socket.IO by using a spy to make sure that IO’s
socket.emit was called correctly.
My final point today is about the 1500 millisecond timeout I have set on the calls to
waitsFor when I connect or disconnect. I originally had this set to 750, which worked flawlessly because I was only testing in Firefox. Later I began to test in Chrome and it was timing out. I had to double the time to wait in order for it not to time out in Chrome. I found this odd considering Chrome is touted as the faster browser. It seems like they may not have maximized their WebSocket performance though. I haven’t yet tested this thoroughly, but believe me when I say that I will. I will try to find this bottleneck, and if possible, I’ll find a way to work around it. In any case, expect me to report my findings in a future post. For now, I just wanted you to know that you may need to have some unusually high timeouts, even with local servers.
That’s about all the interesting stuff I could think to show you. I don’t claim to be an expert at this stuff. After all, I’ve only been unit testing for about a month and a half. But I did want to share what I know. If you see problems with some of the things in my tests and you know you’re right about it, go ahead and let me know.
Also, I wanted to let you all know that there really aren’t any reasons to unit test Socket.IO unless you are using some kind of wrapper like I did here, or if you’re testing a module that relies on Socket.IO in its methods, in which case it’d probably be better to mock your sockets if you can. If you’re testing to make sure things come back from the server correctly, that it integration testing, not unit testing, and should be handled in a different testing environment.