为了处理这些问题,留意下面的代码,它是同个插件的另一种实现:


var dateUtil = {};
(function () {
 var units = {
 second: 1000,
 minute: 1000 * 60,
 hour: 1000 * 60 * 60,
 day: 1000 * 60 * 60 * 24,
 week: 1000 * 60 * 60 * 24 * 7,
 month: 1000 * 60 * 60 * 24 * 30
 };
 function format(num, type) {
 return num + " " + type + (num > 1 ? "s" : "");
 }
 dateUtil.differenceInWords = function (date) {
 // return correct string
 };
 jQuery.fn.differenceInWords = function () {
 this.each(function () {
 var datetime = this.getAttribute("datetime");
 this.innerHTML = dateUtil.differenceInWords(new Date(datetime));
 });
 };
}());


  这个代码和之前是一样的,只不过是进行了一次重构。上面的代码含有两个公用的函数:jQuery插件和新的dateUtil.differenceInWords,这个函数调用了一个日期,然后返回一个对人有好的字符串,用于表示日期是多久之前的。还是不够完美,但是我们已经有两个分开的关注点。现在jQuery插件只负责替换元素的innerHTML为给定的友好字符串,新的函数则负责计算新的字符串。老的测试用例依然能检测通过,但是为新的接口写测试用例会变的更容易:


TestCase("TimeDifferenceInWordsTest", {
 setUp: function () {
 this.date8DaysAgo = new Date(new Date() - 8 * day);
 var diff = 3 * day + 2 * hour + 16 * minute + 10 * second;
 this.date3DaysAgo = new Date(new Date() - diff);
 },
 "test 8 day difference should result in '1 week ago'": function () {
 assertEquals("1 week ago", dateUtil.differenceInWords(this.date8DaysAgo));
 },
 "test should display difference with days, hours, minutes and seconds": function () {
 assertEquals("3 days, 2 hours, 16 minutes and 10 seconds ago",
 dateUtil.differenceInWords(this.date3DaysAgo));
 }
});


  现在我们的测试中已经不包含任何DOM元素,然后我们可以更加高效地测试生成字符串的逻辑。和之前的类似,测试jQuery插件只是一项确保文本内容是否正确替换的工作。

  为什么要为测试修改代码?

  每一次我给一些人介绍测试并解释可测试性的原理时,我总会听到一些质疑声,比如“你不仅仅想让我花时间来写测试,而且你还想为这些测试修改代码?”

  看看我们在时间差只代码中所做的。改变的目的是减轻测试的工作量,但是仅仅是对测试有帮助么?相反,我们的改变还能让代码更容易从不相关的行为中分离出来。现在,如果我们在后来决定在页面中实现比如一个Twitter的订阅功能,我们可以直接使用differenceInWords函数而不是通过DOM元素和jQuery插件寻找一个笨拙的方式。

  可测试性是一个好的设计的固有特点。你可以同时保证可测试性和不好的设计,但是当可测试性差的时候,一定不是一个好的设计。回想那些作为小例子的测试——使用你的代码的例子——如果测试比较困难,那么这也意味着你的代码难以使用。