Learning Node.js, Part 4

In this post I’ll take you through what some people consider the harder to learn aspects of JavaScript testing, which is incorporating a JavaScript test framework and applying it against your site. More and more testers are going to be coming up against these technologies and it never hurts to get some understanding.

It will help you to have gone through my other posts in the Node.js Learning series but everything here is self-contained. Just be aware that the further you get into the ecosystem, the more supporting knowledge you need to have. And the harder it is for someone like me to keep providing that supporting knowledge while also staying on target. I’m striving for a balance here and it’s questionable whether I’m achieving it but I’m hoping a steady presentation of ideas is effective.

Set Up the App

We’ve got a bit of setup to do in order to get to where we can test. Bear with me, though, as this will make sure we have a good foundation in place. Further, it will reinforce some of what you have seen in previous posts. First, let’s create the setup for an application. Run the following commands:

$ mkdir test_app
$ cd test_app
$ npm init

You can accept the defaults for the npm init or add in values of your own. Just keep the entry point as index.js for now. Make sure you have express available locally:

$ npm install --save express

Now create the entry point file called index.js and put the following in it:

You’ve seen all this in my previous posts. If you run this now:

$ nodemon index.js

If you’ve read my previous posts, you’ll know that I use a monitoring solution like nodemon so that I don’t have to constantly restart the server. I recommend this approach but you can simply use Node to start up the app with node index.js if such is your preference.

And go to http://localhost:3000, you’ll see a message like this:

Error: No default engine was specified and no extension was provided.

Set Up the Template Engine

In my previous post, I showed you how to use Jade but essentially just as a means of serving up HTML. There was no logic in the pages. This time around, however, there is going to need to be logic in the pages: specifically test logic. Now you’ll see why I have to use a templating engine. I need to put logic inside of my pages that will allow me to test them. I can’t do that with just markup alone. I need an engine that will interpret logic inside the markup. Jade is the default engine enabled in Express applications and thus usually considered the easiest to use. However, Jade does abstract away much of the HTML which some people don’t like and is often not the most helpful when learning.

There are some great posts out there about template engines. See Node.js Template Showdown as just one example. A lot of people recommend using a library called Handlebars. Handlebars is a superset of the Mustache template library. (I really wish people would pick better names for this stuff.) So that’s what I’m going to use as my rendering engine.

First let’s install it:

$ npm install --save express-handlebars

Change index.js so it looks like this:

Line 6 sets up an engine that I’m calling “handlebars” and then I’m using the reference (hbs) that points to an instance of express-handlebars to set up what name Express should look for in terms of the layout file. I’ll explain more about the layout file in a bit. Line 7 then tells Express to use the “handlebars” engine I just set up as the rendering engine for this application. This is pretty much how you do this for any rendering engine. For example, if I was using the ejs template engine, I would have a line like this:

app.set('view engine', 'ejs');

If you now visit http://localhost:3000, you’ll see a message like this:

Error: Failed to lookup view "home"
  in views directory "c:\Users\Jeff\Desktop\Crypto\test_app\views"

This shows you that Express and Express-Handlebars are following a basic MVC (Model-View-Controller) pattern by saying that any pages to be displayed are considered views and will be in a views directory. Since I’m using a specific engine (express-handlebars), there will be a convention for the file extension of those handlebars. Specifically any views will have a *.handlebars extension. If you were using ejs, any files in your views directory would have *.ejs extensions. If you were using Jade, any views would have .jade extensions.

At this point, create a directory called views. Within that directory we’re going to create some simple files that match the routes above. Create home.handlebars with the following content:

Create about.handlebars with the following content:

Create 404.handlebars with the following content:

We’re going to create one more file before moving on. Remember earlier when I mentioned the layout file and said Express would look for a file named “main” since that’s what we specified for the defaultLayout parameter. You’ll notice in the files above I specify nothing about the generic boilerplate markup that you would find in a normal HTML document. That boilerplate will go in the “main” file. So create a directory called layouts within the views directory. Within that directory create a file called main.handlebars and put the following in it:

The key thing to notice there is the {{{body}}} portion. That’s used by Express-Handlebars as a placeholder for where page content should be rendered. If you’ve worked with templating systems before, this will all be second nature to you. If not, just realize that when {{{body}}} is encountered it will be replaced by the “body” of the page being loaded. So if someone goes to http://localhost:3000/about, the common layout (“main.handlebars”) will be rendered first. When {{{body}}} is processed, it will be replaced with the contents of the about.handlebars page.

Now we can finally get into testing.

Testing the Application

There are various tutorials that attempt to take you through using these kinds of tools. One is Testing Front End JavaScript Code. Yet another is Testing in Node.js. It’s probably just me but I find those a little cumbersome in terms of initial learning. So first I’ll cover the tools I’m going to use but please note that I’m not providing a comprehensive tutorial for each here. I just want to expose you to the ecosystem.

Page Testing

For my purposes here, I’m planning using Mocha. There are other such tools that people use, like Jasmine, Vows, and Sinon.js. I choose Mocha simply because in work environments, I find more professional JavaScript developers want to use it.

Assertion Library

You’ll also want a library that provides convenient helpers to perform various types of assertions against your JavaScript code. I plan on using Chai.js but there are numerous others; two popular ones being Should.js and Must.js.

Installing Mocha

Mocha is interesting in that it runs in the browser. What this means is that you embed your tests in the pages of your application. This gets into some interesting logistics, however, since it means I have to serve up test logic as part of my application logic. We’ll get to that. First, let’s add the Mocha package to the project:

$ npm install --save-dev mocha

The use of –save-dev here instead of –save tells npm to list this package in the development dependencies instead of the runtime dependencies in the package.json file. This means that someone who is just running our application will not need any development dependencies.

Every release of Mocha has primary files of mocha.js and mocha.css that you can use in the browser to support the execution and display of tests. To setup Mocha for use in your browser when viewing your site, you have to include that script and that stylesheet and then tell Mocha which interface you wish to use. The interface can be “TDD” or “BDD” or perhaps others that are defined. Will it make a difference? Yes because the methods that you are provided to actually run your tests are different. Specifically, the BDD interface provides describe(), it(), before(), after(), beforeEach(), and afterEach(). The TDD interface, on the other hand, provides suite(), test(), setup(), and teardown().

Since Mocha will be running in the browser, you need to put the Mocha resources — the script and stylesheet — in a public folder so it both will be served to the browser as static resources. So first create a directory called public. Now we need to add a reference that this is going to be our static folder. Modify index.js so you have this line:

Now, we could just put our files in there but let’s create another directory to hold them. Within the public directory, create a directory called testing. You can actually name this directory anything you want, and some people would suggest “lib” or “vendor”.

Now copy the relevant Mocha files from the local installation of Mocha in your node_modules directory to the public/testing directory:

$ cp node_modules/mocha/mocha.js public/testing
$ cp node_modules/mocha/mocha.css public/testing

Installing Chai

We’re going to follow roughly the same process for our assertions and/or expectations library. What we’re going to do is make it so that the assertions are directly available in the browser. Since I’m using the Chai assertion library, I have to install that:

$ npm install --save-dev chai

As with Mocha, Chai has a static resources (chai.js) that you will need to make available as a static resource for your pages. You can copy the script into the public/testing directory:

$ cp node_modules/chai/chai.js public/testing

Before going too much further, we’re going to be making a lot of small changes to various files. I would recommend running nodemon (or some other monitoring solution) so you don’t have to restart the server each time to see the changes. However, since we’re going to modify multiple files, and not just index.js, you’ll want to run nodemon with options to indicate what files it should monitor. For our purposes, you could do this:

$ nodemon -e js,css,html,handlebars

Create a Test Route

So now we hit one of those logistics I mentioned. Clearly the test logic is going to be available to my site and my pages can reference those JavaScript files and stylesheet. Yet equally clearly I don’t want any of that available as part of the released or deployed application. So I need some way to conditionalize whether or not tests actually show up in the browser and execute. There are various ways you can do this but the easiest I’ve seen from a tutorial perspective is simply making a query string available that will act as a “yes/no” flag for showing tests. So in your index.js file, add the following route above all of your other routes:

The idea here is that if test=show appears in the querystring for any page, then the property res.locals.showTests will set to be true. The res.locals object is part of what’s known as the “context” and it will be passed to any views. So essentially any view can have logic that checks the value of the showTests property.

Include the Test Logic

Now we need to modify the main.handlebars file (in views/layouts) to conditionally include the Mocha test framework. This means we have to reference that stylesheet and JavaScript file I mentioned earlier. Change your main.handlebars file so it looks like this:

First you can see that if the showTests property is true I link the Mocha stylesheet. Then, at the bottom of the page, if the showTests property is true I reference the Mocha script file as well as provide a div section where the Mocha content will go. I specify the interface as being “tdd”. Finally, I call the run() method on the Mocha instance. So now start up your application and just go to http://localhost:3000. You’ll see your basic home page but, if you view the source, you’ll see no test logic is included, which is exactly what I want.

Now go to this URL: http://localhost:3000/?test=show. You won’t see much different on the page — well, except for that little section in the top right that shows passes and fails. So it looks like something is getting incorporated. So let’s view the source. Ah! Now you see the test logic has been included.

So everything works at least as far as we’ve taken it. The only slight problem is we have no actual tests yet. Let’s fix that.

Add a Test

Let’s create a test that will run on every page in the application. We’ll do something simple here, which is basically check that each page has a title. Each of these tests is going to be a in a JavaScript file and thus must be served up as a static resource. So let’s create a directory called tests under the public directory. So now we have a “public/testing” directory that holds our testing framework files and we have a “public/tests” directory that will hold our actual tests.

In the public/tests directory, create a file called site-tests.js. Put the following logic in that file:

There I’ve created a suite() that provides a series of test() methods. In this case, I only have one test() method. As a reminder, I’m running with the TDD interface. If you were using Mocha’s BDD interface, your suite() would be describe() and your test() would be it().

Now we have to make sure that our layout page indicates that this site-tests.js file exists. Modify main.handlebars as such:

Now if you visit http://localhost:3000/?test=show you’re going to see a section for Site Tests and an error that says:

ReferenceError: assert is not defined

Whoops! We didn’t include Chai in the same way that we included Mocha. So let’s do that now. First, we have to modify main.handlebars to reference the Chai JavaScript file and also set up a variable that will use the Chai assertion library. Modify main.handlebars as such:

Visit http://localhost:3000/?test=show and now you should see that the test is passing! Congratulations. You have a working Mocha test using the Chai assertion library. Note that this test works on any page you view it on. For example, visit the about page: http://localhost:3000/about/?test=show.

Add a Page Specific Test

Let’s now make sure that we can add tests for specific pages. Let’s say that the About page should have a link to the Home page. We’ll first add the test logic and see that it fails. Then we’ll add the appropriate link and see that the test passes.

A key thing to note here is that we want the site tests to run no matter what. Any page should run the site tests. But if a page has specific tests, then those tests should also be run. So what this means is we need some property that will let us determine if a page has tests and where those tests are located. Our property will be hasPageTest. Modify your main.handlebars file to look for this property and reference the appropriate test script:

Okay, with that in place, let’s now create a test file for the about page. Specifically create about-page-tests.js in the public/tests directory. In that file, put the following:

However, there’s one thing that doesn’t add up yet. How will the this test get executed? In main.handlebars I provided a script tag to load up the script referenced by “hasPageTest”. But what associates the hasPageTest property with the file about-page-tests.js? We have to specify that in the route. Specifically, we have to say which page test file the view should be using. Modify the About page route in index.js:

View the page http://localhost:3000/about/?test=show and … you get an error. We expect an error since we didn’t add the Home page link yet. But this is not the error we were expecting Specifically, you’ll see:

ReferenceError: $ is not defined

I threw in a little curveball at you here. The $ can refer to many things but what I really want it to refer to is a DOM query and manipulation library. Something that can actively look at the DOM and provide references to specific elements on a page. jQuery is a nice one that happens to use the $ as a variable to stand in for the jquery() method. So let’s just use that for now. To do this, you have to provide a reference to jQuery in your main.handlebars file:

Here I’m just using a link to the CDN on the jQuery site. Feel free to use your CDN of choice or even a jQuery file that you download and make available locally.

With that in place, you now get another error:

AssertionError: Unspecified AssertionError

That is actually the error we expect. Essentially the test failed. Normally, of course, you would provide better assertions and assertion messages than I am doing here. So let’s get this test passing by modifying about.handlebars as such:

Now you should find that all tests on that page pass.Whew!

That’s a lot of work, right? It’s actually not so bad once you get used to it but as you can see there’s lots of little moving parts, from test frameworks, to test files, to assertion libraries, to DOM query/manipulation libraries. And all of it has to be put together so everything is served up correctly but also such that certain content is only served up under certain conditions. I’ve kept this post purposely simple: I haven’t done any cross-page testing, which is where you would test the interaction of components as the pages interact. For example, clicking the link on the About page and making sure it takes you to the Home page.

As with the slew of options I showed you in this post, there are also a slew of options for more robust testing. It’s really difficult to present that material in a way that’s helpful to newcomers. I’ll be looking at doing just that in another post.

Share

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 Node.js. Bookmark the permalink.

2 Responses to Learning Node.js, Part 4

  1. Douglas Lenz says:

    Hi Jeff,

    These articles about learning node.js have been the best introduction to node that I have found so far. Thank you for taking the time to write them and I hope there are lots more to come! I personally feel like you should author a book on this subject because you have the best method of teaching it I’ve come across.

    Thanks,

    Doug

    • Jeff Nyman says:

      Greetings Doug.

      Thank you very much for the comment. That sentiment is truly appreciated. I have some Capybara posts I’m planning to do first. But I definitely plan to cover more on Node.js fairly soon. I want to cover Zombie.js a bit and maybe a framework, such as Flatiron.

      The trick is that the JavaScript ecosystem is so huge that you run a risk of making everything hard to understand by presenting a slew of choices.

      So I’m finding a possible approach is to keep things as simple as possible with the working example, and then show how different technologies (Express, Handlebars, Mocha, etc) fit into that.

Leave a Reply

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