JS Evening

Node modules & testing

Node, modules, and npm

With Node, you can run JavaScript in the console instead of just in the browser. Once you've got it installed, you can paste this into a new file called fizzbuzz.js and then run "node fizzbuzz.js" in the terminal, and you'll get output just like you would in the browser console.

var limit = 100;

var output = "";
for(var i=1; i <= limit; i++) {
  if (i % 3 === 0) {
    output += 'fizz';
    if (i % 5 === 0) {
      output += 'buzz';
    }
  } else if (i % 5 === 0){
    output += 'buzz';
  } else {
    output += i;
  }
  output += '\n';
}

console.log(output);

Super short: js for(i=0;i<100;)console.log((++i%3?'':'fizz')+(i%5?'':'buzz')||i);

JavaScript files in the browser have access to any JS files loaded into the DOM. With Node, there is no DOM--in order to have access to other JS files, you have to require them. Given an external file in the same directory called data.js, you can do that like so:

var data = require('./data.js');

The file being required must have some data that it exports via module.exports = < whatever > You will then be able to make use of the value that is exported as though it were in the file doing the requiring.

data.forEach( function(item) {
  console.log(item.name);
});

Since neither require nor module.exports are used in standard JS, these files will not run in the browser: you'll get an error:

// Uncaught ReferenceError: require is not defined
// Uncaught ReferenceError: module is not defined

(note that only the one of the two errors will be thrown)

You can keep these errors from happening by detecting whether you're in Node or the browser and only using require or module.exports in Node. The easiest way to do this it to look for a variable that is only available in Node:

// if(typeof module !== 'undefined') {
//   var data = require('./data.js');
// }

You'll have to do a similar wrapping in your data file, and make sure the variable names match up.

Testing with Node

In addition to requiring the files that you've written, you can require any modules built in to node. In that case, you don't need to use the initial "./":

var assert = require('assert');

This will give you access to Node's built-in assert module, which is very similar to the assert function that you're familiar with already. It does have a few more options available to it, though: https://nodejs.org/api/assert.html

Notes on testing:

Why test? - Lets you know when you broke something - Serve as documentation for your code - You write more modular code if you know you need to test it - Many junior dev jobs will start you working with tests

How to test? - write up lists of what behaviors you expect to see - turn lists into tests - consider edge cases (remember week 1's tricky expressions?)

What doesn't need tests? - anything that isn't your code

Back in homework 3, you got introduced to the concept of writing assertions and using them to test both real arrays and simulated ones. We'll revisit that exercise now, this time writing tests with Node's built-in assert module. In the case of the simulated array, you'll also be splitting the tests and the code being tested into two different files--the tests will need to require the code they are testing, in that case, and the code being tested will need to be exported.

If you didn't finish that exercise, you can find a working solution here: https://github.com/portlandcodeschool/jse-fall15-3/blob/master/solutions/solution2-arrays.js

Note that if you're using assert.deepEqual to compare your results to an array you've created, your tests won't pass for simulated arrays!

The Mocha testing framework

Next up: looking at Mocha! Install it globally with: npm install -g mocha Mocha is not in charge of deciding if tests pass or fail--it's more an organizer for your tests. It helps you keep your tests separate from each other and can also display the results for you in easier- to-read ways. At first, we'll continue to use node's assert module to decide which tests pass and which fail:

var fakeArray = require('../fakeArray.js');
var assert = require('assert');

var reset = function() {
  fakeArray[0] = 1;
  fakeArray[1] = 2;
  fakeArray[2] = 3;
  fakeArray.length = 3;
};

describe('My fake array object', function() {
  describe('The pop method', function() {

    before(reset);

    it('should return the final element', function() {
      assert.equal(fakeArray.pop(), 3);
    });
    it('should reduce the length of the array', function() {
      assert.equal(fakeArray.length, 2);
    });
  });
});

Use the above sample to write the tests for push and join in mocha, then run the tests. Remember, the tests need to be in a subdirectory called test/ and can be run with the command mocha from the directory containing your code (not the one containing your tests).

The Chai assertion library

Just as mocha makes our test results easier to read, we can use a module called chai to make the tests themselves more readable. Chai is an assertion library, just like Node's assert library. You'll install it with npm install chai in your project directory, rather than globally with the -g flag.

Chai has a few more options than assert, starting with the kind of assertions you're using. We'll be using Chai's expect style. You'll require that like this:

var chai = require('chai');
var expect = chai.expect;

Assertions using chai.expect look like this:

expect(fakeArray.join()).to.equal('1,2,3');

There are many, many more options for your tests in chai; check them out at http://chaijs.com/api/bdd/

After browsing them, change your mocha tests to use chai's expect instead of node's assert. The mocha parts can all stay the same; the only parts that change are the test assertions.

Once you're done with that, go back to the sample cards from hw6 and write tests for an instance of the Card constructor, and for the Card constructor itself: https://github.com/portlandcodeschool/jse-fall15-6/blob/master/solution3-cards.js