martes, 21 de enero de 2014

Writing sequential test scripts with node

Today I was trying to create a node.js script to test a HTTP service but the test required multiple steps.   I gave it a try by using async module to "symplify" that code and that's the ugly code I came up with.

I'm not an expert in js/node, feel free to comment if I'm doing something wrong, I'm more than happy to learn.

(inflightSession and create are two helper functions that I have)

Test using node + jasmine: 

it("should accept valid sessionId", function(done) {
      async.waterfall([
          inflightSession,

          function(sessionId, callback) {
            create({ 'sessionId':sessionId }, callback);
          }
      ], function(error, response) {
          expect(response.statusCode).to.equal(200);
          done()
    });
  });


Same test using python + unittest:

def create_ok_test():
    session_id = inflight_session()
    response = create({ 'sessionId': session_id })

    assert_equals(200, response.status_code)


Same test using node ES6 generators (yield keyword):

it("should accept valid sessionId", function*() {
      var sessionId = yield inflightSession();

      var response = yield create({ 'sessionId': sessionId });
      expect(response.statusCode).to.equal(200);
  });


Honestly the code in python is way more readable than the existing node code, and still better even when comparing it with the new node generators.    Anway definitely looks like a promising way to move forward in the node community.   Some comments:

ES6 generators are available under a flag in node 0.11 and are supposed to be included in 0.12.

yield is a common keyword in other languages (i.e. python, C#) to exit from a function but keeping the state of that function so that you can resume the execution later.

function* is the syntax to define a generator function (a function using yield inside).

You need a runner supporting those generator functions (in this example jasmine needs to add support for it), basically calling the generator.next and waiting for the result (the result should be a promise or similar object) before calling generator.next again.

UPDATE: As I´m somehow forced to use node, I ended up creating a helper function and my tests are now like this

itx("should accept valid sessionId", inflightSession, function(sessionId, done) {
      create({ 'sessionId':sessionId }, function(error, response) {
          expect(response.statusCode).to.equal(200);
          done()
      });

});


3 comentarios:

  1. With promises you can do something like this (pseudocode):

    inflightSession
    .then(function(sessionId) {
    create({sessionId:sessionId});
    )
    .then(function(response) {
    expect(response.statusCode.to.equal(200));
    })
    .then(done);

    It is clearer than async, but not as clear/compact as its sequential alternative in python/java... and most people/libraries still use the callback style.

    These two are good resources:
    - http://strongloop.com/strongblog/promises-in-node-js-with-q-an-alternative-to-callbacks/
    - https://github.com/kriskowal/q

    ResponderEliminar
  2. With async is slightly more compact IMO, but I agree that promises can be easier to read. Thx for the comment, very useful.

    ResponderEliminar
  3. This is also interesting, based on coroutines https://github.com/visionmedia/co

    ResponderEliminar