11月阅读周·编写可测试的JavaScript代码:生成输出、聚合和隐藏的文件篇
背景
去年下半年,我在微信书架里加入了许多技术书籍,各种类别的都有,断断续续的读了一部分。
没有计划的阅读,收效甚微。
新年伊始,我准备尝试一下其他方式,比如阅读周。每月抽出1~2个非连续周,完整阅读一本书籍。
这个“玩法”虽然常见且板正,但是有效,已经坚持阅读十个月。
已读完书籍:《架构简洁之道》、《深入浅出的Node.js》、《你不知道的JavaScript(上卷)》、《你不知道的JavaScript(中卷)》、《你不知道的JavaScript(下卷)》、《数据结构与算法JavaScript描述》、《WebKit技术内幕》、《前端架构:从入门到微前端》、《秒懂算法:用常识解读数据结构与算法》、《JavaScript权威指南》、《JavaScript异步编程设计快速响应的网络应用》。
当前阅读周书籍:《编写可测试的JavaScript代码》。
生成输出
大多数(如果不是全部的话)的构建工具都识别JUnit XML格式,但YUI的JSON覆盖格式却不是。还好,有一个简单的步骤可以将YUI JSON转换为行业且开源标准的LCOV格式(如下命令都在一行上):
java-jar yuitest-coverage-report.jar --format LCOV -o <output_directory> coverage.json
在前面的代码中,coverage.json是来自coverageResults POST参数中的JSON文件(或集成测试中直接从_yuitest_coverage中获取)。<output_directory>是表示LCOV格式的文件输出,适用于lcov和genhtml(同LCOV一起)命令。Hudson / Jenkins本身接受该文件格式,并可以将其自动生成漂亮的HTML格式的覆盖率文件,或者可以使用genhtml命令完成自定义格式的HTML。
genhtml命令有很多选项可用于定制化HTML输出,如果不满意默认输出,可以对其进行修修补补。
聚合
不管是单元测试还是集成测试,我们都可以对单个测试或测试套件的覆盖率信息进行持久化,但如何能够整体地查看这些信息呢?对此,lcov命令可以完成此项重任!
lcov -a命令可以将多个lcov文件聚合组合成一个大文件。命名如下:
lcov -a test.lcov -a test1.lcov -a test2.lcov ... -o total.lcov
上述命令会将多个LCOV文件合并在一起。该命令可用于将所有的单元测试或所有的集成测试合并在一起。
要合并在一起,所有的LCOV文件必须都在同一个根目录下。对所有单元测试覆盖率结果或所有集成测试覆盖率结果进行单独合并的话,并没有问题,但要让单元测试和集成测试合并在一起,就会有问题。
查看LCOV文件的格式,每个新文件覆盖率部分的第一行都是以源文件(本例中是JavaScript文件)的完整路径开始。如果两个根目录不匹配,lcov将无法合并它们。
我们需要一个简单的shell脚本,以确保所有的根目录都是一样的(同样,发生合并的机器在该位置也必须有源代码树)。很有可能的是,根目录下的其中一个测试套件是正确的,而另外一个是错误的。因为LCOV文件只是纯测试文件,要匹配实际源代码所在目录,纠正错误的LCOV文件是很简单的事情。
如果要生成HTML覆盖率文件的源代码的目录是/a/b/c,但生成LCOV文件的集成测试所用的源码却在/d/e/f的话,用自己喜欢的语言编写一个脚本,将/d/e/f转换为/a/b/c。Perl脚本的示例如下:
my $old ='/a/b/c';
my $new ='/d/e/f';
open(F, "wrong.lcov");
open(G, ">right.lcov");
while(<F>) {
s#^$old#$new#;
print G;
}
close(G);
close(F);
在上述代码中,转换后的wrong.lcov位于/d/e/f目录,将会合并在另外一个LCOV文件中。一旦有了一个满意的总LCOV文件,就可以很容易地生成漂亮的HTML:
genhtml -o /path/to/docRoot/coverage total.lcov
此时,使用浏览器访问该主机上的/coverage目录,将会看到覆盖率数字在增长!
生成输出——单个或聚合后的LCOV格式文件——然后可以很轻松将其集成到像Hudson/Jenkins这样的自动化构建工具中。十分方便,老版本的Hudson原始支持LCOV格式的文件,在构建输出中,只需要指定一个指向聚合文件地址的指针即可。
Hudson负责HTML生成,并在每个构建中持久化覆盖率数据,以便可以很轻松地随时跟踪代码覆盖率。
隐藏的文件
没有任何代码覆盖率数据的文件会怎么样呢?这些文件对于总报告来说是隐藏的,不占总报告的比例。我们需要对这些模块做一些“假的”空LCOV文件,将其和其他LCOV文件聚合在一起,从而更清晰地展示测试覆盖率。
处理这些隐藏文件的最简单方法就是确保所有应用程序JavaScript文件至少都有一个空的测试。然后对coveraged版本的文件运行空测试以生成(0%)的LCOV输出。现在,可以照常持久化并处理该LCOV数据了,所有的代码都会被聚合汇总。
如下是所有应用程序文件都必须拥有并且带有空测试类型的HTML文件。一旦准备为代码编写测试,就可以删除该HTML文件,并用真实的测试取代它:
<html lang="en">
<body class="yui3-skin-sam">
<div id="log"></div>
<h3>Running dummy unit test for app file</h3>
<script src="http://yui.yahooapis.com/3.4.0/build/yui/yui.js"></script>
<script src="/path/to/coveraged/file/without/tests.js"></script>
<script>
YUI().use('test', function (y) {
Y.Test.Runner.add(
new Y.Test.Suite('no test').add(
new Y.Test.Case({
name: 'dummy_NOTEST',
testFile: function () {
Y.Assert.areEqual(true, true);
},
}),
),
);
Y.TestRunner.go();
});
</script>
</body>
</html>
上述空测试足以装载JavaScript文件的coveraged版本文件,并可以持久化含有假覆盖率数字的空覆盖率信息,并将其聚合到汇总里。
查看总报告时,可以很明显地看出哪些文件任何测试都没有覆盖到。
在大型环境中,这些假测试文件应该是通过比较应用程序中包含代码的HTML,并判断差别后再进行自动生成的。该文件的自动生成版本将会被动态创建,并包含在所运行的测试中。
完成该项工作的一种很好的方式是遍历所有测试的HTML文件,看看哪个JavaScript文件被加载了,然后将其和项目中所有的JavaScript文件的列表进行比较。
总结
一旦找到一个文件名,就将其从%src_files哈希中删除,标记该文件的覆盖率计算已经完成了。
最后%src_files哈希只会包含没有单元测试的JavaScript文件。现在,可以很简单地使用自己喜欢的模板系统对这些文件生成一个空的单元测试了。通过自动化单元测试工具,可以将这些空测试保存在test目录树中的某个地方,稍后再运行,所以,现在不管JavaScript文件有没有单元测试,都可以生成整个项目的代码覆盖率。
用这些空测试计算这种文件的代码覆盖率,将会遭遇尴尬的处境(很可能),因为整体覆盖率会非常接近0%(可能不完全是0%,因为函数外放置的代码,在文件本身被加载的时候肯定会被执行的)。
作者介绍
非职业「传道授业解惑」的开发者叶一一。
《趣学前端》、《CSS畅想》等系列作者。华夏美食、国漫、古风重度爱好者,刑侦、无限流小说初级玩家。
如果看完文章有所收获,欢迎点赞👍 | 收藏⭐️ | 留言📝。
- 点赞
- 收藏
- 关注作者
评论(0)