Viktor Fröberg
Viktor Fröberg

Reputation: 112

What could I do to improve my testing?

Lately I've tried to get my head around good test driven development in Node JS with Mongoose as MongoDB wrapper.

My question is am I doing it right. Am I testing the right stuff, should I do more test that cover empty values etc. What of the things do you think I could improve? Feel free to criticize as much as you like.

Test:

var assert = require("assert"),
    mongoose = require("mongoose"),
    sugar = require("sugar"),
    Factory = require("factory-lady"),
    Timekeeper = require("timekeeper"),
    Link = require("./../app/models/link");

mongoose.connect("mongodb://localhost/link_test");

Factory.define("link", Link, {
  createdAt: new Date("2012-04-04"),
  title: "Example",
  updatedAt: new Date("2012-04-05"),
  url: "http://www.example.com"
});

describe("Link", function() {

  describe("validation for", function() {

    ["url", "title"].forEach(function(property) {
      it("must have a " + property, function(done) {
        Factory.build("link", function(link) {
          link[property] = null;
          link.validate(function(err) {
            assert.notEqual(err, null);
            assert.notEqual(err.errors[property], null);
            done();
          });
        });
      });
    });

    it("should default the title to url if missing", function(done) {
      Factory.build("link", function(link) {
        link.title = undefined;
        link.validate(function(err) {
          assert.equal(link.title, link.url);
          done();
        });
      });
    });

    describe("url", function() {

      ["http://example.com", "http://www.example.com",
       "http://nodejs.org/api/assert.html#assert_assert_deepequal_actual_expected_message"].forEach(function(url) {
        it(url + " should be valid url", function(done) {
          Factory.build("link", { url: url }, function(link) {
            link.validate(function(err) {
              assert.equal(err, null);
              done();
            });
          });
        });
      });

      ["Invalid url", "example.com"].forEach(function(url) {
        it(url + " should be invalid url", function(done) {
          Factory.build("link", { url: url }, function(link) {
            link.validate(function(err) {
              assert.notEqual(err, null);
              assert.notEqual(err.errors.url, null);
              done();
            });
          });
        });
      });

    });

    describe("missing createdAt or updatedAt", function() {

      beforeEach(function() {
        Timekeeper.freeze(new Date());
      });

      afterEach(function() {
        Timekeeper.reset();
      });

      ["createdAt", "updatedAt"].forEach(function(property) {
        it("should default the " + property + " to now if missing", function(done) {
          Factory.build("link", function(link) {
            link[property] = undefined;
            link.validate(function(err) {
              assert.deepEqual(link[property], new Date());
              done();
            });
          });
        });
      });

      ["createdAt", "updatedAt"].forEach(function(property) {
        it("should leave the " + property + " unchanged if it's given", function(done) {
          Factory.build("link", function(link) {
            var date = new Date("2012-01-01");
            link[property] = date;
            link.validate(function(err) {
              assert.deepEqual(link[property], date);
              done();
            });
          });
        });
      });

      it("should default the updatedAt to now if missing when performing an update", function(done) {
        Factory("link", function(link) {
          link.validate(function(err) {
            assert.equal(link.updatedAt, new Date());
            done();
          });
        });
      });

      it("should leave the updatedAt unchanged if it's given when performing an update", function(done) {
        Factory("link", function(link) {
          var date = new Date("2012-01-01");
          link.updatedAt = date;
          link.validate(function(err) {
            assert.deepEqual(link.updatedAt, date);
            done();
          });
        });
      });

    });

  });

});

Model:

var mongoose = require("mongoose"),
    Schema = mongoose.Schema;

var linkSchema = new Schema({
  createdAt: { type: Date, required: true },
  title: { type: String, required: true },
  updatedAt: { type: Date, required: true },
  url: { type: String, required: true, validate: [validUrl, "invalid url"] }
});

function validUrl(url) {
  if ((url instanceof String || typeof url == 'string') && url.length) {
    return url.match(/^\b((?:https?:\/\/|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))$/i); 
  }
}

linkSchema.pre("validate", function (next) {
  if (typeof this.title === "undefined" && typeof this.url !== "undefined") {
    this.title = this.url;
  }
  if (typeof this.createdAt === "undefined") {
    this.createdAt = new Date();
  }
  if (typeof this.updatedAt === "undefined" || !this.isModified("updatedAt")) {
    this.updatedAt = new Date();
  }
  next();
});

module.exports = mongoose.model("Link", linkSchema);

Upvotes: 2

Views: 403

Answers (1)

oferei
oferei

Reputation: 1826

All in all it seems that your tests cover the code. Perhaps you could add a few more URLs to better test the validUrl function.

Generally the amount of testing to be done depends on your needs. Whether you need more tests or not depends on many things, such as: your confidence in your code, the number of people that may touch your code and "break" it, the size and complexity of the overall system, the users of your code (are they part of your team or outsiders?), the source of the data (is it validated before it reaches your code?) and your level of paranoia. There's no one-fits-all answer.

Upvotes: 1

Related Questions