在运行测试套件时,length 断言会测试失败,但是这里很难弄清它为什么会失败。问题在于上一次测试中 dataStore 的状态留了下来。如果只是给测试重新排序的话 length 测试会通过,但是会有红色标志标明某处出现了问题。我们当然可以使用 setup 或者 teardown 方法,用恢复 dataStore 的状态来修复此问题,但那也同时代表着我们需要在 dataStore 模块的实现改动了以后经常维护这样的测试模板。更好的做法如下:
  JavaScript
  function newDataStore() {
  var data = [];
  return {
  push: function (item) {
  data.push(item);
  },
  pop: function() {
  return data.pop();
  },
  length: function() {
  return data.length;
  }
  };
  }
  var dataStore = newDataStore();
  function newDataStore() {
  var data = [];
  return {
  push: function (item) {
  data.push(item);
  },
  pop: function() {
  return data.pop();
  },
  length: function() {
  return data.length;
  }
  };
  }
  var dataStore = newDataStore();
  现在,测试套件看起来如下:
  JavaScript
  module("dataStore");
  test("pop", function() {
  var dataStore = newDataStore();
  dataStore.push("foo");
  dataStore.push("bar")
  equal(dataStore.pop(), "bar", "popping returns the most-recently pushed item");
  });
  test("length", function() {
  var dataStore = newDataStore();
  dataStore.push("foo");
  equal(dataStore.length(), 1, "adding 1 item makes the length 1");
  });
  module("dataStore");
  test("pop", function() {
  var dataStore = newDataStore();
  dataStore.push("foo");
  dataStore.push("bar")
  equal(dataStore.pop(), "bar", "popping returns the most-recently pushed item");
  });
  test("length", function() {
  var dataStore = newDataStore();
  dataStore.push("foo");
  equal(dataStore.length(), 1, "adding 1 item makes the length 1");
  });
  这让我们的全局 dataStore 和以前的行为保持一致,同时避免了测试之间的相互污染。每项测试都有自己的DataStore 实例对象,都会在测试完成时进入垃圾回收。
  避免基于闭包的私有形式
  我过去所推崇的另一个模式是 在 JavaScript 中建立真正的私有成员。这样做的好处是,可以保持全局可访问的命名空间免受不必要的,私有实现引用细节的侵扰。然而过度使用这种模式会导致代码无法测试。这是因为你的测试套件将无法访问到闭包中隐藏的私有函数,也无法进行测试了。考虑以下的代码:
  JavaScript
  function Templater() {
  function supplant(str, params) {
  for (var prop in params) {
  str.split("{" + prop +"}").join(params[prop]);
  }
  return str;
  }
  var templates = {};
  this.defineTemplate = function(name, template) {
  templates[name] = template;
  };
  this.render = function(name, params) {
  if (typeof templates[name] !== "string") {
  throw "Template " + name + " not found!";
  }
  return supplant(templates[name], params);
  };
  }
  function Templater() {
  function supplant(str, params) {
  for (var prop in params) {
  str.split("{" + prop +"}").join(params[prop]);
  }
  return str;
  }
  var templates = {};
  this.defineTemplate = function(name, template) {
  templates[name] = template;
  };
  this.render = function(name, params) {
  if (typeof templates[name] !== "string") {
  throw "Template " + name + " not found!";
  }
  return supplant(templates[name], params);
  };
  }