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学习-简单测试环境 在<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. [软件测试]Linux环境中简单清爽的Google Test (GTest)测试环境搭建(初级使用)

    本文将介绍单元测试工具google test(GTEST)在linux操作系统中测试环境的搭建方法.本文属于google test使用的基础教程.在linux中使用google test之前,需要对如 ...

  6. 【Head First Servlets and JSP】笔记6:什么是响应首部 & 快速搭建一个简单的测试环境

    搭建简单的测试环境 什么是响应首部 最简单的响应首部——Content-Type 设置响应首部 请求重定向与响应首部 在浏览器中查看Response Headers 1.先快速搭建一个简单的测试环境, ...

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

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

  8. Linux测试环境简单使用教程

    0. 本blog 简单说明一下 Linux测试环境尤其是 CentOS测试环境的开发测试使用, 教程可能不会很长, 主要是入门. 0.1 Linux简介: Linux 的历史基本上不用阐述, linu ...

  9. Linux实现树莓派3B的国密SM9算法交叉编译——(一)环境部署、简单测试与eclipse工程项目测试

    这篇文章主要介绍了交叉编译的实现,包括环境部署,并简单测试交叉编译环境是否安装成功. 一.交叉编译 在一个平台上生成另一个平台上的可执行代码.为什么要大费周折的进行交叉编译呢?一句话:不得已而为之.有 ...

随机推荐

  1. linux下安装部署环境:jdk、tomcat、nginx

    一.安装jdk 一.查看Linux自带的JDK是否已安装 1.查看已经安装的jdk: # rpm -qa|grep jdk                ← 查看jdk的信息或直接执行 或 # rpm ...

  2. ios 自定义NSError

    from:[object-c错误处理]http://www.androiddev.net/objective-c%E5%AD%A6%E4%B9%A0%E4%B9%8B%E9%94%99%E8%AF%A ...

  3. Python中下划线---完全解读

    Python 用下划线作为变量前缀和后缀指定特殊变量 _xxx 不能用'from module import *'导入 __xxx__ 系统定义名字 __xxx 类中的私有变量名 核心风格:避免用下划 ...

  4. ftp_get_file_and_directory

    class DirectoryItem { public Uri BaseUri; public string AbsolutePath { get { return string.Format(&q ...

  5. 三星note4,微信公众号开发,页面闪退

    在做微信公众号开发时,使用三星note4测试,有一个select框闪退的问题出现. 出现该问题的原因是该选择框,设置了appearance:none;属性.查找了官方解释是: 所有主流浏览器都不支持 ...

  6. mybatis sql中if判断传入Integer类型,传入0时,判断没有执行

    数据库中字段 isDisable 0:否:1:是 xml中判断 <if test="isDisable != null and isDisable !='' "> AN ...

  7. UVa 10306 - e-Coins

    题目大意:现在有一种新型货币,它的价值分为传统价值x和IT价值y,价值计算方式为sqrt(x*x+y*y),现给一些类型的货币和要达到的目标价值,计算达到目标所需的最少货币数目.注意计算方法是sqrt ...

  8. 写了一个复杂的sql语句

    $sp_sql = "select sp_ProductNo, sp_ProductName,sp_Standard,sp_Unit,sum(sp_Amount) as amount fro ...

  9. java系列--MD5加密

    方案一: /** * 1.对文本进行32位小写MD5加密 * @param plainText 要进行加密的文本 * @return 加密后的内容 */ public static String te ...

  10. sqlite3API函数

    回顾: DDL 表的创建.修改.删除 create table 表名(字段名 字段类型 [约束],...); alter table 表名 {rename to 新名字 | add column 字段 ...