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学习-简单测试环境的更多相关文章

  1. Javascript的简单测试环境

    在<JavaScript忍者秘籍>2.4测试条件基础知识中,作者给出了一个精简版的assert和assert组的实现,对于初学者而言,这无疑是一个很好的例子,既让我们得到了一个好用的小工具 ...

  2. iOSIPV6简单测试环境搭建

    应苹果官方要求,iOS应用必须适配IPV6才能通过审核,这里分享一个简单的ipv6测试方法 一.工具原料 1.1 Mac电脑一台 1.2 iPhone手机两部 1.3 数据线一根 二.步骤方法 2.1 ...

  3. kubernetes实战之consul篇及consul在windows下搭建consul简单测试环境

    consul是一款服务发现中间件,1.12版本后增加servicemesh功能.consul是分布式的,可扩展的,高可用的根据官方文档介绍,目前已知最大的consul集群有5000个节点,consul ...

  4. kubernetes实战之consul简单测试环境搭建及填坑

    这一节内容有点长,我们将介绍如何基于docker搭建一client一server的consul测试环境,以及如何搭建多server consul测试集群.在基于docker搭建多server的cons ...

  5. Django学习——Django测试环境搭建、单表查询关键字、神奇的双下划线查询(范围查询)、图书管理系统表设计、外键字段操作、跨表查询理论、基于对象的跨表查询、基于双下划线的跨表查询

    Django测试环境搭建 ps: 1.pycharm连接数据库都需要提前下载对应的驱动 2.自带的sqlite3对日期格式数据不敏感 如果后续业务需要使用日期辅助筛选数据那么不推荐使用sqlite3 ...

  6. javascript学习笔记 - 执行环境及作用域

    一 执行环境(环境) 1.每个执行环境都有一个关联的全局变量对象.例如:web浏览器中,window对象为全局变量对象.环境中定义的所有变量和函数都保存在该对象中.全局执行环境是最外围的环境. 2.执 ...

  7. javascript学习-目录

    Javascript学习 第1章 Javascript简介 1.1 Javascript简史 1.2 Javascript实现 1.3 Javascript版本 第2章 Javascript入门 2. ...

  8. Linux测试环境搭建的学习建议

    随着Linux应用的扩展许多朋友开始接触Linux,根据学习Windwos的经验往往有一些茫然的感觉:不知从何处开始学起.这里介绍学习Linux测试环境搭建的一些建议. 一.Linux测试环境搭建从基 ...

  9. Python+selenium测试环境成功搭建,简单控制浏览器(firefox)接下来,继续学习其他浏览器上的测试环境搭建;学习Python语言,利用Python语言来写测试用例。加油!!!

    Python+selenium测试环境成功搭建,简单控制浏览器(firefox)接下来,继续学习其他浏览器上的测试环境搭建:学习Python语言,利用Python语言来写测试用例.加油!!!

随机推荐

  1. JS简单实现分页显示

    完整代码源码可以在这里下载 1.在 HTML文件建立列表目标节点和翻页器目标节点 <body> <!--页面控制器 --> <div id="nav" ...

  2. World Finals 2018 感想

    垃圾比赛卡我读入卡我输出毁我青春.

  3. 根据浏览器判断是下载IOS还是其它的手机安装包

    http://tiao.67767.cn/DownLoad.aspx?cid=3509           <!DOCTYPE html>   <html>   <hea ...

  4. myeclipse 无法启动Tomcat(程序未设置断点)This kind of launch is configured to open the Debug perspective ...

    myeclipse 中在新建一个项目之后想要运行一下,可是却提示This kind of launch is configured to open the Debug perspective,下面是我 ...

  5. js JQuery 获取元素和遍历

    用户名<input class="yonghu" type="text" id="user" name="u" / ...

  6. chrome浏览器上传图片反应很慢,延迟很久才弹出窗口

    chrome浏览器上传图片反应很慢,延迟很久才弹出窗口 一个上传图片的控件,点击按钮上传图片,用chrome(谷歌浏览器)时,点击按钮后,要等好几秒才弹出文件选择窗口 可以试一下,把网络断开,这个问题 ...

  7. laravel整合workerman做聊天室

    测试工具  http://www.blue-zero.com/WebSocket/ 2018年8月6日17:28:24 <?php namespace App\Console\Commands; ...

  8. 小甲鱼零基础python课后题 P21 020函数:内嵌函数和闭包函数

    测试题 0.如果希望在函数中修改全局变量的值,应该使用什么关键字? 答:globe 1.在嵌套函数中,如果希望在内部函数修改外部函数的局部变量,应该使用什么关键字? 答:nonlocal 2.pyth ...

  9. 【C++】括号匹配

    #include<iostream> #include<cstring> #include<cstdlib> #include<queue> using ...

  10. NI Vision ClampRake修改

            也可以直接在C盘模板中直接修改,模板路径: C:\Program Files (x86)\National Instruments\Vision Assistant\CG\Visual ...