您可能知道测试很好,但是在尝试为客户端代码编写单元测试时要克服的第一个障碍是缺少任何实际的单元。JavaScript代码是为网站的每个页面或应用程序的每个模块编写的,并与后端逻辑和相关的HTML紧密混合。在最坏的情况下,代码会与HTML完全混合在一起,作为内联事件处理程序。
当没有使用用于某些DOM抽象的JavaScript库时,可能会出现这种情况;编写内联事件处理程序比使用DOM API绑定那些事件要容易得多。越来越多的开发人员正在使用诸如jQuery之类的库来处理DOM抽象,从而使他们可以将这些内联事件移动到同一页面甚至单独的JavaScript文件中的不同脚本中。但是,将代码放入单独的文件并不意味着它可以作为一个单元进行测试。
单位是什么?在最好的情况下,它是一个纯函数,您可以通过某种方式进行处理-对于给定的输入,该函数始终会为您提供相同的结果。这使单元测试非常容易,但是大多数时候您需要处理副作用,这在这里意味着DOM操作。弄清楚我们可以将代码构建到哪些单元中并相应地构建单元测试,仍然很有用。
建筑单元测试
考虑到这一点,我们显然可以说,从头开始时,从单元测试开始要容易得多。但这不是本文的目的。本文旨在帮助您解决更棘手的问题:提取现有代码并测试重要部分,潜在地发现和修复代码中的错误。
在不修改其当前行为的情况下提取代码并将其放入其他形式的过程称为重构。重构是一种改进程序代码设计的出色方法。并且由于任何更改实际上都可能会修改程序的行为,因此在进行单元测试时最安全的做法是。
这个“鸡与蛋”问题意味着要将测试添加到现有代码中,您必须承担破坏程序的风险。因此,除非您对单元测试有足够的了解,否则需要继续手动测试以最大程度地降低这种风险。
就目前而言,这应该已经足够了。让我们看一个实际的示例,测试一些当前与页面混合并连接到页面的JavaScript代码。该代码查找具有title
属性的链接,并使用这些标题显示发布时间(例如“ 5天前”)作为相对时间值:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Mangled date examples</title>
<script>
function prettyDate(time){
var date = new Date(time || ""),
diff = (((new Date()).getTime() - date.getTime()) / 1000),
day_diff = Math.floor(diff / 86400);
if ( isNaN(day_diff) || day_diff < 0 || day_diff >= 31 )
return;
return day_diff == 0 && (
diff < 60 && "just now" ||
diff < 120 && "1 minute ago" ||
diff < 3600 && Math.floor( diff / 60 ) +
" minutes ago" ||
diff < 7200 && "1 hour ago" ||
diff < 86400 && Math.floor( diff / 3600 ) +
" hours ago") ||
day_diff == 1 && "Yesterday" ||
day_diff < 7 && day_diff + " days ago" ||
day_diff < 31 && Math.ceil( day_diff / 7 ) +
" weeks ago";
}
window.onload = function() {
var links = document.getElementsByTagName("a");
for ( var i = 0; i < links.length; i++ ) {
if ( links[i].title ) {
var date = prettyDate(links[i].title);
if ( date ) {
links[i].innerHTML = date;
}
}
}
};
</script>
</head>
<body>
<ul>
<li class="entry">
<p>blah blah blah...</p>
<small class="extra">
Posted <span class="time">
<a href="#2008/01/blah/57/" title="2008-01-28T20:24:17Z">
<span>January 28th, 2008</span>
</a>
</span>
by <span class="author"><a href="#john/">John Resig</a></span>
</small>
</li>
</ul>
</body>
</html>
|
如果运行该示例,将会看到一个问题:所有日期都不会被替换。该代码有效。它遍历页面上的所有锚,并title
在每个锚上检查属性。如果存在,则将其传递给prettyDate
函数。如果prettyDate
返回结果,则使用结果更新innerHTML
链接的。
使事物可测试
问题在于,对于任何早于31天的日期,它prettyDate
只会返回未定义的(隐式地,只有一条return
语句),而锚点的文本保持不变。因此,要了解应该发生什么,我们可以对“当前”日期进行硬编码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Mangled date examples</title>
<script>
function prettyDate(now, time){
var date = new Date(time || ""),
diff = (((new Date(now)).getTime() - date.getTime()) / 1000),
day_diff = Math.floor(diff / 86400);
if ( isNaN(day_diff) || day_diff < 0 || day_diff >= 31 )
return;
return day_diff == 0 && (
diff < 60 && "just now" ||
diff < 120 && "1 minute ago" ||
diff < 3600 && Math.floor( diff / 60 ) +
" minutes ago" ||
diff < 7200 && "1 hour ago" ||
diff < 86400 && Math.floor( diff / 3600 ) +
" hours ago") ||
day_diff == 1 && "Yesterday" ||
day_diff < 7 && day_diff + " days ago" ||
day_diff < 31 && Math.ceil( day_diff / 7 ) +
" weeks ago";
}
window.onload = function() {
var links = document.getElementsByTagName("a");
for ( var i = 0; i < links.length; i++ ) {
|