Why Test Engineers Should Learn Node.js

Lately I’ve found myself wanting to develop test solutions that essentially require a simple web server along with a lot of client-side logic. For example, a Gherkin repository interface is something I’ve been longing to do. I don’t want to learn Pylons (in the Python world) right now and while my focus is Ruby, I’m not too keen on the overhead and bloat of Rails. I’ve done a Sinatra app (Dialogic). Sinatra is sort of Rails-lite, but that doesn’t really help me with an optimized solution that is going to use a lot of JavaScript. Thus did I come across Node.js.

Beyond the fact that you can do some cool stuff with Node.js, particularly in terms of using a common language (JavaScript) on the server and the client, another good reason for testers to learn Node is that many organizations are adopting Node as part of their application stack, which leads into a lot of ancillary tools and frameworks, like Angular.js, Express, Yadda, Mocha, and so on.

Here I’ll cover some of my own experiences learning Node.js. I’ll assume here that if you want to follow along, you are perfectly capable of downloading and installing Node.js for your operating system of choice.

So the first thing I wanted to do was be able to serve up content. That is, after all, a large part of what Node.js is going to be helping me do. The most common web server tasks are part of the http module. So you have to require that, then set up a web server using that module. Here’s a file I called server.js:

You can run this with the command:

node server.js

The HTTP server object is the foundation of all Node web applications. The object itself sits very close to the HTTP protocol and that means it won’t hurt to have some knowledge of how the protocol works if you are going to use this object. However, you can also use certain application frameworks that will hide the HTTP protocol details from you. The createServer() function has only one argument, which is a callback that will execute whenever a request is received.

The callback in createServer() is listening for a request event, which is a built-in event type defined by the http module. The event handler receives two arguments: the request object and the response object. The response object is used to build up the response to be sent to the client. I do that above with the writeHead() and end() functions. The writeHead() function creates a header for the file that I’m sending to the client, indicating what the browser should do with it. The end() function does not only signifies the end of the response, but you can use it to write the final response.

The listen() function is, not surprisingly, what tells the server to start listening for requests from clients. Behind the scenes, this function sets up means to dispatch an event for every request arriving from a client, like the web browser. As you can see, I just provide the port and the host IP as arguments to this function.

It is possible to serve up actual HTML rather than plain text. Here’s an example:

This is not the way you would want to do this, of course, but this was how I learned some simple examples. Incidentally, I will say that most of the examples you’ll find out there do not have the structure like I do above. More specifically, here’s the above example in terms of how you would most likely find it:

Here I just call createServer() directly on the http module and call listen() directly on the whole construct. Whether or not it makes a difference which approach you use, I have no idea, to be honest. In any event, now that I had the basics down I figured I’d try to do something more akin to how an actual web server would work. Specifically, rather than hard-coding my HTML content, I would have my server actually serve up a static HTML page.

So I first created a simple index.html page. Basically I just used the contents of my res.end() function call above:

Where do I put this file, though? Well, since I’m effectively creating the server I can decide what directories to use. For now I’ll stick to a oft-used convention of storing my static resources in a directory called public. Here’s a very simple way to handle this with my server:

Here I require the path module to work with file paths and the fs module to allow me to access the file system.

Ideally I would have my server actually check for the request that is coming in and make sure that it is a request for an HTML page. Then I should make sure that the page requested actually does exist. Only then should I load that page and return the contents of the page as a part of the response. Ideally I should also have index.html be the default. You get that out of the box with servers like Apache, but with Node, you have to create the server pretty much from the ground up. Finally, it wouldn’t hurt to throw an error if there is some problem that occurs when serving up the content. Here’s an example of a modified version of the above that handles some of what I just talked about:

If you are not used to JavaScript, you might be starting to think that this looks pretty ugly. It can get that way. Node in particular is evented and relies on callbacks. It’s quite possible to get pretty extreme when nesting these callbacks. This suggests you learn quickly how to appropriately modularize your code.

Another thing I’ll need is to be able to serve up other kinds of static content, such as images, stylesheets, and JavaScript. This essentially means I need to modify my code above so that it can serve more resources to the client. To test this out, within my public directory, I created a css and js directory. I then modified my index.html to look like this:

You can put whatever content you want in your site.js and site.css files. Here then is what my server code become, with a few highlights to indicate changes:

You can see that I create a variable for resource extensions. Then, in line 14, I store any directories in the requested URL, so that if my resources are organized into directories (as they are, with my css and js directories), I can still use my existing code to access them. The substring(1) part just removes the first character, since that should always be a slash. In line 17 you can see that instead of just checking for an .html extension, I now check for any valid resource extensions.

Line 19 shows a small change needed because of the addition of line 18, where I add the directory to my path if the file is in a subdirectory, as would be the case for css and js resources. Finally, because I listed the MIME types with my resources, I can now use logic like that of lines 23 to 26, where I send the correct Content-Type for whatever kind of file resource was returned to the client.

With this code, which is admittedly ugly and not showcasing the best of coding style, I can add and serve client resources from anywhere under the public directory.

Looking at this, you might think: “Wow, using Node.js really sucks.”

After all, you do have to set up just about everything, from the default page that will be served, the acceptable resources and their MIME types. And I haven’t even done anything here with permissions, session tracking, data persistence and so on. Frameworks like Django or Rails handle all this stuff for you. (Well, sort of.) On the other hand, this has definitely given me a feeling of being in full control of what I’m doing. I also know that if I can learn these basics and step up to some more advanced material, I’m poised to enter into the wider Node ecosystem, in which there are thousands of modules and numerous frameworks (like Connect and Express) that make a lot of this easier.

I plan on learning as much of Node.js as I can and my personal belief is this could be a very nice platform that testers can use to build various test solutions, particularly when there is a desire to not be locked into Rails’ opinions, the boilerplate and overhead of Java’s Struts, or the lack of effective reusability with Django. Node.js slaps you smack in the center of JavaScript and gaining proficiency with how JavaScript-based applications work can only serve a tester well.

About Jeff Nyman

Anything I put here is an approximation of the truth. You're getting a particular view of myself ... and it's the view I'm choosing to present to you. If you've never met me before in person, please realize I'm not the same in person as I am in writing. That's because I can only put part of myself down into words. If you have met me before in person then I'd ask you to consider that the view you've formed that way and the view you come to by reading what I say here may, in fact, both be true. I'd advise that you not automatically discard either viewpoint when they conflict or accept either as truth when they agree.
This entry was posted in Career, Learning. Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *