Backbone default attribute inheritance by reference

Posted on by bika
Reply

As the following fiddle shows, Backbone passed the default values to models by reference, thus if the default value is an object, then changing the object in one model instance, changes the given attribute in all model instances.

Posted in Magyar cikkek | Tagged backbone, example | Leave a reply

Creating a JSON-only browser using Tobi

Posted on by bika

Tobi is a great tool to mimick requests and helps a lot in testing. We use it for the Estisia game to test routing and basically the top layer of our server implementation. As the client and the server mostly communicates using JSON, we were looking for a simple to use wrapper to create JSON requests.

Basically, what we wanted is to put Accept and X-Requested-With headers into our tobi requests, and to do it easily. The normal approach would be the following.

var tobi = require('tobi'),
    browser = tobi.createBrowser(8000, "localhost");
 
browser.get('/mypage', {headers:
    {
    "Accept": "application/json",
    "X-Requested-With": "XMLHttpRequest"
    }
  }, function(res, $) { ... });

But how could we easily add a wrapper around all the browser.request methods to become as lazy as we programmers truly are?

Our solution was using a neat clojure:

var tobi = require('tobi'),
     browser = tobi.createBrowser(8000, "localhost");

json_browser = (function(){
  var json_header = {headers:
    {
    "Accept": "application/json",
    "X-Requested-With": "XMLHttpRequest"
    }
  };
  var json_method = function(method){
    return function(path, options, callback){
      if(!callback) {
        callback = options;
        options = {};
      }
      browser[method](path, _.defaults(options, json_header), callback);
    };
  };
  return {
    get: json_method('get'),
    post: json_method('post'),
    put: json_method('put'),
    delete: json_method('delete')
  };
}());

view raw file1.js This Gist is brought to you using Simple Gist Embed.
Posted in Testing | Tagged testing

Connecting to MongoDB using a mongodb:// uri

Posted on by bika

The node-mongodb-native module is quite well documented, but there is a missing piece of information: how would you connect to a mongo database using a mongodb uri?

Such connections are inevitable if you use a shared db hosting plan, like the ones offered by mongohq. So, here is the solution:

var Db = require('mongodb').Db;

//...

options.noOpen = true;
myDb = Db.connect(mongouri, options);

// or

options.noOpen = false;
Db.connect(mongouri, options, callback);

view raw file1.js This Gist is brought to you using Simple Gist Embed.
Posted in Tutorials | Tagged mongodb, nodejs

Unit testing Node.JS applications

Posted on by bika

I was looking for the tools for weeks to write easy to run & write tests for node. Finally we’ve settled with Vows + Should + Tobi.

Vows is an asynchronous testing framework for Node.JS. Should is an extension on top of Node’s assert module. While Tobi is a really nice and easy to use request-faking library for Node.

How Mocha failed?

A very serious contender to Vows was Mocha, but it failed at a ridiculous stage, we could not make it wait for the database connection event. We have tried many things, but they simply did not work. The problem was that due to the async nature, our script’s `beforeEach` call was handled before the database connection was set up. On the other hand, if we’ve tried to put all or part of our tests into an event handler, then the tests were simply skipped as `make test` could not find any calls to `describe` and its friends before it finished running.

Why not node-request?

Actually, Tobi had a contender, node-request. We’ve found both libraries relatively easy to use, but Tobi seemed to be more developer friendly. It has a really nice API, it extends should.js.

How vows overcame our problem?

With vows we could easily overcome our problem. The only important thing was to really understand what batches and context are meant to mean, and what parallel and sequential processing they involve.

As you should know, batches contain context, that contain tests to be run. Batches are run sequentially, one after the other. This way we can isolate the database environment. On the other hand, inside a batch contexts are run in parallel, thus you should not rely on anything coming from outside of your function under test.

An example for batches and contexts

Our first test was about the user registration. Here is its code

var tobi = require('tobi'),
 vows = require('vows'),
 mainapp = require('../server'),
 server = mainapp.app,
 should = require('should'),
 Db = require('backbone-mongodb/lib/db'),
 _connection = null,
 browser = tobi.createBrowser(server);
 
var db_callback = function(callback) {
 mainapp.db.on('database', function(status){
 var error = status == 'open' ? null : status;
 if (error) callback(new Error('Could not connect to database'));
 else {
 _connection = Db.getConnection();
 callback(error, _connection);
 }
 })
};
 
vows.describe('Registration')
.addBatch({
 'database': {
 topic: function() { db_callback(this.callback); },
 'is available': function(err, db) {
 should.not.exist(err);
 should.exist(db);
 }
 }
})
.addBatch({
 'clear database': {
 topic: function() {
 var callback = this.callback;
 mainapp.statusapp.Users._withCollection(function(err, collection){
 if(err) callback(err);
 else collection.remove({}, function(err, result) {
 if(err) callback(err);
 else collection.count(callback);
 });
 });
 },
 'worked': function(err, length) {
 should.not.exist(err);
 length.should.equal(0);
 }
 }
})
.addBatch({
 'is working': {
 topic: function() {
 browser.post(
 '/register',
 {
 login: 'mylogin',
 password: 'password',
 password2: 'password'
 },
 this.callback
 );
 },
 'without errors': function(res, $){
 console.log('ok')
 res.should.have.status(303);
 mainapp.statusapp.Users.length.should.equal(1);
 }
 }
})
.addBatch({
 'if username exists': {
 topic: function() {
 var callback = this.callback;
 request.post({
 uri: baseurl + '/register',
 json: {
 login: 'mylogin',
 password: 'password',
 password2: 'password'
 }},
 function(err, res, body) {
 callback(err, [res, body]);
 });
 },
 'should fail': function(err, data) {
 mainapp.statusapp.Users.length.should.equal(1);
 should.exist(err);
 }
 },
 "if passwords don't match": {
 topic: function() {
 var callback = this.callback;
 request.post({
 uri: baseurl + '/register',
 json: {
 login: 'newuser',
 password: 'password',
 password2: 'otherpass'
 }},
 function(err, res, body) {
 callback(err, [res, body]);
 });
 },
 'should fail': function(err, data) {
 should.not.exist(err);
 var res = data[0],
 body = data[1];
 mainapp.statusapp.Users.length.should.equal(1);
 res.should.have.status(200);
 }
 }
})
.export(module);

As you can see, we first wrote the code to connect to the database. This was really easy with vows, unlike with Mocha. Then we went on to set up the very basic test environment, and we’ve deleted all our users from the database. (Yeah, you should never run these tests on a production server .:)) After these we’ve started to write the real tests.

In the first batch we are creating a user, thus later batches check that the user should exist. A nice way of seeing what batches and contexts mean is when the first batch passes, but one of the later con