Javascript的简单测试环境
Javascript学习-简单测试环境
在《JavaScript忍者秘籍》2.4测试条件基础知识中,作者给出了一个精简版的assert和assert组的实现,对于初学者而言,这无疑是一个很好的例子,既让我们得到了一个好用的小工具,又让我们看到了用javascript实现这个工具是如此的简单。
这里主要是从代码角度最2.4章节做一些补充和说明,包括原有代码中的一些bug及其修正。当然了,既然涉及到了代码解析,这就不能说是初学者的范畴了,至少要多javascript中的函数声明,函数实现,函数闭包等内容有了基本的了解后,才能看懂这篇文章。
1.assert
先来说说assert,应用代码是这个样子的:
<script type="text/javascript">
assert(1 + 1 === 2, "1 + 1 = 2");
assert(1 + 1 === 3, "1 + 1 = 3");
</script>
assert就是一个javascript函数,有两个参数,第一个参数用来判断表达式是true或false,第二个参数用来对测试做一些说明,测试结果直接显示在html中,这里的测试结果是这个样子的:
还挺酷的吧。好了,那么我们就来看看如何实现这个assert?
首先新建一个html文件
然后在body中加入一个id为rusults的ul节点:
<body>
<ul id="results"></ul>
</body>
后面所有的assert结果列表都要加到这个节点下。
assert执行完成后的html结果是这样的:
看起来挺简单的吧,就是在ul节点下对每个assert测试用li节点来表现。对于测试为true的li节点的class被赋值为pass,对于测试为false的li节点的class被赋值为fail。
原理清楚了,那么这个assert函数的代码看起来就不复杂了:
function assert(value, desc) {
// 创建li节点
var li = document.createElement("li");
// 如果value为true,li的class为pass
// 如果value为false,li的class为fail
li.className = value ? "pass" : "fail";
// 根据desc创建text节点,然后添加到li节点下
li.appendChild(document.createTextNode(desc));
// 找到document中id为results的节点元素,就是那个body下的ul,
// 然后把新建的li节点添加到ul节点下
document.getElementById("results").appendChild(li);
}
剩下的就是添加一些css,美化一下html了,既然已经学习javascript了,一般的html和css的内容就不在这说了,完整的html如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>assert</title>
<style>
body {
font-family: sans-serif;
font-size: 12pt;
} #results {
background-color: #e0e0e0;
border-radius: 1em;
padding: 1em;
list-style-position: inside;
} ul {
list-style-type : circle;
} #results li {
margin-bottom: 0.2em;
} #results li.fail {
color: red;
text-decoration: line-through;
} #results li.pass {
color: green;
}
</style>
<script type="text/javascript">
function assert(value, desc) {
// 创建li节点
var li = document.createElement("li");
// 如果value为true,li的class为pass
// 如果value为false,li的class为fail
li.className = value ? "pass" : "fail";
// 根据desc创建text节点,然后添加到li节点下
li.appendChild(document.createTextNode(desc));
// 找到document中id为results的节点元素,就是那个body下的ul,
// 然后把新建的li节点添加到ul节点下
document.getElementById("results").appendChild(li);
}
</script>
</head>
<body>
<ul id="results"></ul> <script type="text/javascript">
assert(1 + 1 === 2, "1 + 1 = 2");
assert(1 + 1 === 3, "1 + 1 = 3");
</script>
</body>
</html>
2.asserts
asserts代表一个测试组,应用代码是这个样子的:
<script type="text/javascript">
asserts(); test("数值计算测试", function() {
assert(1 + 1 === 2, "1 + 1 = 2");
}); test("字符串测试", function() {
assert("a" + "b" === "ab", '"a" + "b" = "ab"');
assert("a" + "b" === "abc", '"a" + "b" = "abc"');
});
</script>
这段代码是说,先创建一个描述为“数值计算测试”的测试组,里面加一组assert;再创建一个描述为“字符串测试”的测试组,里面加一组assert。两个测试组之间是平级的关系。
每个测试组里的一组assert都是不同的,因此需要一个function包装起来。这个函数可以叫做“测试组assert组装函数”。
这里的测试结果是这个样子的:
看起来更酷了吧。你注意到了没有,这里有一个需求点:如果测试组里面有一个assert测试为false,那么整个测试组也要标记为false。
这个html的结构如下:
每个测试组用li节点表现,而li节点下又内嵌了一个ul节点,在这个内嵌的ul节点下才是测试组内所有assert的li节点表现。
当然了,有图有真相,这里很明显有一个小bug,"a" + "b" === "ab"明明是正确的,怎么显示的li节点也被画红线了?或许你也可以辩解为既然是整个测试组失败了,那么为这个正确的li节点画红线也说得过去吧?谁让它属于失败的测试组呢?既然选择了猪一样的队友,那就得认命。可是那你又怎么解释这个正确的li节点被一边画了红线,一边却显示为绿色的文字?这明显自相矛盾嘛。
好了,先不理这个小bug了,稍后我们会解决这个bug。现在还是让我们老老实实的来看看这个测试组的功能是如何实现的吧?
html相关的部分都不改,body里还是那个孤零零的id为rusults的ul节点,css也完全不用动,需要修改的只有javascript代码。
注意测试组的代码中先调用了一个asserts函数,这个函数负责初始化测试组的一些环境,简单的说它是这个样子的:
// 测试组的整体初始化
function asserts() {
// 声明一个results变量,
// 作为assert函数闭包和test函数闭包的一部分
var results; // 创建assert表现节点
assert = function(value, desc) {
} // 创建测试组表现节点
test = function(name, fn) {
}
}
这里这里对assert重新进行了赋值,当然我们首先需要知道这种assert之前没有var声明的,说明这个变量已经在全局的window中,或者将在这句代码执行处被加入到了全局的window中,而我们上面在说单个assert的时候不是已经有了一个assert函数的实现了吗?那个assert也是在全局的window中的。毫无疑问,在调用asserts函数后,原有的assert函数就被覆盖掉了。test变量也是类似的,在调用asserts函数后,将被加入到全局的window中。
注意asserts函数开始的那个results变量,因为asserts函数调用后会在全局的window增加两个函数assert和test,而这个results变量就必然的成为了这两个函数闭包的一部分。
我们先看看这个test函数是如何实现的:
test = function(name, fn) {
// 找到document中id为results的ul节点元素,就是那个body下的ul
results = document.getElementById("results");
// 创建一个ul节点
var ul = document.createElement("ul");
// 创建一个测试组节点,就象创建普通assert节点一样直接调用assert
// 毫无意外,这个测试组节点被加到了id为results的ul节点元素下,
// 初始默认这个测试组节点的测试结果是true。
// 在测试组节点下添加内嵌的ul节点,该测试组下的所有assert表现节点都会被
// 加入到这个内嵌的ul节点中。
// 既然如此,那么我们就让results变量指向这个内嵌的ul节点
results = assert(true, name).appendChild(ul); // 调用"测试组assert组装函数",构建各个assert表现节点
fn();
};
在test函数执行的开始,results被指向了body下的ul节点,并在此基础上完成了测试组表现节点的创建,然后results被指向了测试组内嵌的ul节点上,"测试组assert组装函数"被调用,新的assert表现节点就会被加入到results节点下。
下面我们来看看新的assert函数是如何实现的:
assert = function(value, desc) {
// 创建li节点
var li = document.createElement("li");
// 如果value为true,li的class为pass
// 如果value为false,li的class为fail
li.className = value ? "pass" : "fail";
// 根据desc创建text节点,然后添加到li节点下
li.appendChild(document.createTextNode(desc));
// 把新建的li节点添加到results下,至于这个rusults是啥?
// 在test执行前是body下的ul节点
// 在test执行后是测试组表现节点下的ul节点
results.appendChild(li); if (!value) {
// 如果有一个assert测试结果是false,
// 那么就找到li节点的父节点的父节点,
// 也就是测试组表现节点了,然后设置class为fail
li.parentNode.parentNode.className = "fail";
} // 返回li节点
// 在test执行前是测试组表现节点
// 在test执行后是assert表现节点
return li;
};
好了,搞定,完整的html如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>assert</title>
<style>
body {
font-family: sans-serif;
font-size: 12pt;
} #results {
background-color: #e0e0e0;
border-radius: 1em;
padding: 1em;
list-style-position: inside;
} ul {
list-style-type : circle;
} #results li {
margin-bottom: 0.2em;
} #results li.fail {
color: red;
text-decoration: line-through;
} #results li.pass {
color: green;
}
</style>
<script type="text/javascript">
// 测试组的整体初始化
function asserts() {
// 声明一个results变量,
// 作为assert函数闭包和test函数闭包的一部分
var results; assert = function(value, desc) {
// 创建li节点
var li = document.createElement("li");
// 如果value为true,li的class为pass
// 如果value为false,li的class为fail
li.className = value ? "pass" : "fail";
// 根据desc创建text节点,然后添加到li节点下
li.appendChild(document.createTextNode(desc));
// 把新建的li节点添加到results下,至于这个rusults是啥?
// 在test执行前是body下的ul节点
// 在test执行后是测试组表现节点下的ul节点
results.appendChild(li); if (!value) {
// 如果有一个assert测试结果是false,
// 那么就找到li节点的父节点的父节点,
// 也就是测试组表现节点了,然后设置class为fail
li.parentNode.parentNode.className = "fail";
} // 返回li节点
// 在test执行前是测试组表现节点
// 在test执行后是assert表现节点
return li;
}; test = function(name, fn) {
// 找到document中id为results的ul节点元素,就是那个body下的ul
results = document.getElementById("results");
// 创建一个ul节点
var ul = document.createElement("ul");
// 创建一个测试组节点,就象创建普通assert节点一样直接调用assert
// 毫无意外,这个测试组节点被加到了id为results的ul节点元素下,
// 初始默认这个测试组节点的测试结果是true。
// 在测试组节点下添加内嵌的ul节点,该测试组下的所有assert表现节点都会被
// 加入到这个内嵌的ul节点中。
// 既然如此,那么我们就让results变量指向这个内嵌的ul节点
results = assert(true, name).appendChild(ul); // 调用"测试组assert组装函数",构建各个assert表现节点
fn();
};
}
</script>
</head>
<body>
<ul id="results"></ul> <script type="text/javascript">
asserts(); test("数值计算测试", function() {
assert(1 + 1 === 2, "1 + 1 = 2");
}); test("字符串测试", function() {
assert("a" + "b" === "ab", '"a" + "b" = "ab"');
assert("a" + "b" === "abc", '"a" + "b" = "abc"');
});
</script>
</body>
</html>
3.修正测试组为false的bug
之所以有这个bug,是因为这里的测试组表现太简单了,直接在li节点上设置class,使得css的可控性不高。学过css列表部分的应该都清楚,对列表的控制应该使用行内文本span嘛。
我们希望的显示效果应该是:
相应的html结构应该是:
既然只是将测试组表现节点和测试表现节点多加一层span,那么test函数是完全不用变的,只是assert函数需要做一点小的修改:
assert = function(value, desc) {
// 创建li节点
var li = document.createElement("li");
// 创建sapn节点
var span = document.createElement("span");
// 根据desc创建text节点
var text = document.createTextNode(desc); // 在li下添加span节点
li.appendChild(span);
// 在span下添加text节点
// 完成li>span>text的三层关系
span.append(text); // 如果value为true,span的class为pass
// 如果value为false,span的class为fail
span.className = value ? "pass" : "fail"; // 把新建的li节点添加到results下,至于这个rusults是啥?
// 在test执行前是body下的ul节点
// 在test执行后是测试组表现节点下的ul节点
results.appendChild(li); if (!value) {
// 如果有一个assert测试结果是false,
// 那么就找到li节点的父节点的父节点,
// 也就是测试组表现节点了
var liGroup = li.parentNode.parentNode;
// 不能直接在测试组表现节点设置class了
// 必须在测试组表现节点下的span节点设置class
// 也就是测试组表现节点下的第一个子元素
liGroup.childNodes[0].className = "fail";
} // 返回li节点
// 在test执行前是测试组表现节点
// 在test执行后是assert表现节点
return li;
};
相应的css也是需要做些小的修改的,不是直接在li节点上做效果了,而是在span节点上做效果。这些小地方都很容易理解,那么就直接上修改后的完整html吧:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>assert</title>
<style>
body {
font-family: sans-serif;
font-size: 12pt;
} #results {
background-color: #e0e0e0;
border-radius: 1em;
padding: 1em;
list-style-position: inside;
} ul {
list-style-type : circle;
} #results li {
margin-bottom: 0.2em;
} #results span.fail {
color: red;
text-decoration: line-through;
} #results span.pass {
color: green;
}
</style>
<script type="text/javascript">
// 测试组的整体初始化
function asserts() {
// 声明一个results变量,
// 作为assert函数闭包和test函数闭包的一部分
var results; assert = function(value, desc) {
// 创建li节点
var li = document.createElement("li");
// 创建sapn节点
var span = document.createElement("span");
// 根据desc创建text节点
var text = document.createTextNode(desc); // 在li下添加span节点
li.appendChild(span);
// 在span下添加text节点
// 完成li>span>text的三层关系
span.append(text); // 如果value为true,span的class为pass
// 如果value为false,span的class为fail
span.className = value ? "pass" : "fail"; // 把新建的li节点添加到results下,至于这个rusults是啥?
// 在test执行前是body下的ul节点
// 在test执行后是测试组表现节点下的ul节点
results.appendChild(li); if (!value) {
// 如果有一个assert测试结果是false,
// 那么就找到li节点的父节点的父节点,
// 也就是测试组表现节点了
var liGroup = li.parentNode.parentNode;
// 不能直接在测试组表现节点设置class了
// 必须在测试组表现节点下的span节点设置class
// 也就是测试组表现节点下的第一个子元素
liGroup.childNodes[0].className = "fail";
} // 返回li节点
// 在test执行前是测试组表现节点
// 在test执行后是assert表现节点
return li;
}; test = function(name, fn) {
// 找到document中id为results的ul节点元素,就是那个body下的ul
results = document.getElementById("results");
// 创建一个ul节点
var ul = document.createElement("ul");
// 创建一个测试组节点,就象创建普通assert节点一样直接调用assert
// 毫无意外,这个测试组节点被加到了id为results的ul节点元素下,
// 初始默认这个测试组节点的测试结果是true。
// 在测试组节点下添加内嵌的ul节点,该测试组下的所有assert表现节点都会被
// 加入到这个内嵌的ul节点中。
// 既然如此,那么我们就让results变量指向这个内嵌的ul节点
results = assert(true, name).appendChild(ul); // 调用"测试组assert组装函数",构建各个assert表现节点
fn();
};
}
</script>
</head>
<body>
<ul id="results"></ul> <script type="text/javascript">
asserts(); test("数值计算测试", function() {
assert(1 + 1 === 2, "1 + 1 = 2");
}); test("字符串测试", function() {
assert("a" + "b" === "ab", '"a" + "b" = "ab"');
assert("a" + "b" === "abc", '"a" + "b" = "abc"');
});
</script>
</body>
</html>
Javascript的简单测试环境的更多相关文章
- Javascript学习-简单测试环境
Javascript学习-简单测试环境 在<JavaScript忍者秘籍>2.4测试条件基础知识中,作者给出了一个精简版的assert和assert组的实现,对于初学者而言,这无疑是一个很 ...
- iOSIPV6简单测试环境搭建
应苹果官方要求,iOS应用必须适配IPV6才能通过审核,这里分享一个简单的ipv6测试方法 一.工具原料 1.1 Mac电脑一台 1.2 iPhone手机两部 1.3 数据线一根 二.步骤方法 2.1 ...
- kubernetes实战之consul篇及consul在windows下搭建consul简单测试环境
consul是一款服务发现中间件,1.12版本后增加servicemesh功能.consul是分布式的,可扩展的,高可用的根据官方文档介绍,目前已知最大的consul集群有5000个节点,consul ...
- kubernetes实战之consul简单测试环境搭建及填坑
这一节内容有点长,我们将介绍如何基于docker搭建一client一server的consul测试环境,以及如何搭建多server consul测试集群.在基于docker搭建多server的cons ...
- [软件测试]Linux环境中简单清爽的Google Test (GTest)测试环境搭建(初级使用)
本文将介绍单元测试工具google test(GTEST)在linux操作系统中测试环境的搭建方法.本文属于google test使用的基础教程.在linux中使用google test之前,需要对如 ...
- 【Head First Servlets and JSP】笔记6:什么是响应首部 & 快速搭建一个简单的测试环境
搭建简单的测试环境 什么是响应首部 最简单的响应首部——Content-Type 设置响应首部 请求重定向与响应首部 在浏览器中查看Response Headers 1.先快速搭建一个简单的测试环境, ...
- Python+selenium测试环境成功搭建,简单控制浏览器(firefox)接下来,继续学习其他浏览器上的测试环境搭建;学习Python语言,利用Python语言来写测试用例。加油!!!
Python+selenium测试环境成功搭建,简单控制浏览器(firefox)接下来,继续学习其他浏览器上的测试环境搭建:学习Python语言,利用Python语言来写测试用例.加油!!!
- Linux测试环境简单使用教程
0. 本blog 简单说明一下 Linux测试环境尤其是 CentOS测试环境的开发测试使用, 教程可能不会很长, 主要是入门. 0.1 Linux简介: Linux 的历史基本上不用阐述, linu ...
- Linux实现树莓派3B的国密SM9算法交叉编译——(一)环境部署、简单测试与eclipse工程项目测试
这篇文章主要介绍了交叉编译的实现,包括环境部署,并简单测试交叉编译环境是否安装成功. 一.交叉编译 在一个平台上生成另一个平台上的可执行代码.为什么要大费周折的进行交叉编译呢?一句话:不得已而为之.有 ...
随机推荐
- POJ3255次短路
POJ3255 题意:给定一个图,求从1到n的次短路 分析:我们需要在dijkstra上作出一些修改,首先,到某个顶点v的次短路要么是到其他某个顶点u的最短路在加上u到v的边,要么是到v的次短路再加上 ...
- 14、手把手教你Extjs5(十四)模块字段和Grid列的定义[2]
model和columns生成好了,下面要修改一下Module.js和Grid.js中的代码,使其能够协同工作. /** * 一个模块的主控界面的容器,用来安放各个模块控件以及协调他们之间的关系 */ ...
- css3快速复习
选择器边框.阴影 border-radius: 50%; 设置正圆形背景的改变CSS3重要的新东西: ● transition 过度,让一个元素从一个样式,变为另一个样式,不再是干蹦了,而是有动画,均 ...
- 微信小程序之----问题
1.如何定义全局数据 在app.js的App({})中定义的数据或函数都是全局的,在页面中可以通过var app = getApp(); app.function/key的方式调用,不过我们没有必要 ...
- ZOJ 1025 Wooden Sticks
题目大意:有n个木棍,分别具有长度li和重量wi.对于木棍s1和s2,若l1<=l2且w1<=w2,则s1.s2可构成单调递增序列.求n个木棍中这样序列的个数. 最先的想法是,先排序,然后 ...
- HTML5学习笔记 二:article和section
在HTML5中,article可以看做特殊种类的section,它比section更强调独立性. section元素强调分段或分块,而article强调独立性: 如果一块内容相对独立.完整,应该使用a ...
- Arcgis js API使用wmts方式加载GeoWebCache中的切片地图(转载)
使用GeoWebCache的主要目的是其独立安装版能够发布arcgis的切片.我们知道,使用openlayer是调用geoserver最方便的方法,那么在发布完arcgis的切片后,怎么用arcgis ...
- 导航栏项目滑过时子菜单显示/隐藏jquery代码
;(function(window){ $('.menuitem').hover(function(){ $('>a',this).css('background-color ...
- Struts2 属性驱动、模型驱动、异常机制
模型驱动使用单独的VO(值对象)来封装请求参数和处理结果,属性驱动则使用Action实例来封装请求参数和处理结果. 一.使用模型驱动 1.login.action采用模型驱动模式实现,采用模型驱动时必 ...
- 常用的.net开源项目
Json.NET http://json.codeplex.com/ Json.Net 是一个读写Json效率比较高的.Net框架.Json.Net 使得在.Net环境下使用Json更加简单.通过Li ...