什么是可测试代码

选中文字可对指定文章内容进行评论啦,→和←可快速切换按钮,绿色背景文字可以点击查看评论额。

什么是可测试代码

我们理解的可测试代码指的是:

1、松耦合

2、短小的

3、可隔离的

我们会依照这三个原则来分析,怎么样编写可测试的代码?

一般来说,一个函数或者一个功能,如果越复杂,那么实现此功能需要的代码可能就会越多,代码量就会越多,出现潜在Bug的概率就越大。

因此,编写可测试代码的第一步就是让函数或者功能保持最小代码量,而保持最小代码量的方法就是让命令(Command)和查询(Query)分离。

我们首先需要理解的是什么是命令,什么是查询?

命令函数表示做什么(do something);而查询函数表示返回什么(return something);命令表示setter而查询表示getter。命令函数使用mock进行测试,而查询函数使用stub进行测试。

下面我们看一个例子:

function configure(values){
	var fs = require('fs');
	var config = { docRoot : '/somewhere'};
	var key;
 	var stat;
 	for (key in values) {
 		config[key] = values[key];
 	}
 	try {
 		stat = fs.statSync(config.docRoot);
		if (!stat.isDirectory()) {
			throw new Error('Is not valid');
 		}
 	} catch(e) {
		console.log('** '+ config.docRoot+ ' does not exist or is not a directory.');
		return ;
	}
 	//check other values ......
	return config;
}

这个函数的主要功能是将参数values的值复制到config对象,然后检查config.docRoot是否是一个目录,然后检查其他参数,最后返回config对象。

另外还有一点就是检查的代码,如果config.docRoot不是一个目录,会抛出一个错误,并且返回一个undefined这个逻辑虽然没啥不对,但是有点奇怪。

我们首先思考一下这个问题?这个函数的功能会不会太多?这个功能里面哪些属于命令而哪些又属于查询?

我们看看这个函数的测试代码:

describe('configure test', ()=>{
	it('undef if docRoot does not exist', function(){
		expect(configure({ docRoot: '/xxx'})).toBeUndefined();
	});
	it('not undef if docRoot does exist', function(){
		expect(configure({ docRoot: '/tmp'})).not.toBeUndefined();
	});
	it('adds values to config hash', function(){
		var config = configure({ docRoot: '/xxx' , zany:'crazy'});
		expect(config).not.toBeUndefined();
		expect(config.zany).toEqual('crazy');
		expect(config.docRoot).toEqual('/xxx');
	});
	it('varifies value1 good ...', function(){
	});
	it('varifies value1 bad ...', function(){
	});

	/** 其他验证测试 */
});

注意到没有:这个测试中,需要测试的功能太多,就有设置值,也有验证值。导致如此复杂的一个原因就是没有将命令和查询分离。

我们首先理解什么是命令,以及什么是查询

/**
 * 没有返回值,属于命令
 *
 */
function warn(thing) {
	console.log(['WARN:',thing].join(' '));
}

/**
 * 没有返回值,属于命令
 *
 */
function fail(thing) {
	throw new Error(thing);
}

/**
 * 有返回值,属于查询
 *
 */
function add(a,b) {
	return a + b;
}

结论:一个函数有返回值就是查询,一个函数没有返回值就是命令。

有了命令和查询的基础知识,我们再来看看如何将第一个例子中的命令部分和查询部分隔离出来:

我们下一节在进行分析。欢迎讨论。

 

版权声明:著作权归作者所有。

相关推荐

jquery检查元素是否可见

在jquery里,隐藏或显示元素可以分别使用.hide()和.show()。$("#myid").show(); $("#myid").hide(); 有时我们需要检查元素是否可见,从而根据元素的可见性做出处理。jquery里可以使用is(":visible")可见:$(element).is(":visible");

Java 8使用parallel Stream测试StringBuilder线程安全

我们知道StringBuilder不是线程安全的,但如何证明它非线程安全呢?测试StringBuilder是否线程安全一个简单的思路如下:模拟多个线程并发向StringBuilder实例添加字符,最后检测builder.toString().length()的值是否为添加字符的次数,如果非线程安全会出现builder.toString().length()的值与添加字符的次数不一致。Java&nb

使用Robolectric写Android单元测试

Robolectric是一个单元测试框架,运行在jvm上。相对于在Android模拟器或设备上运行测试需要花费一分甚至更长时间,Robolectric只需要几秒钟。这里简单介绍下使用Robolectric做单元测试。添加依赖首先在build.gradle添加Robolectric依赖,并设置android的测试选项unitTests.includeAndroidResources为truetest

Linux检测命令是否存在

兼容POSIX的command如果要兼容POSIX的话可以使用command:command -v <the_command>使用示例:if ! [ -x "$(command -v git)" ]; then   echo 'Error: git

Ubuntu检测包是否已经安装

在Ubuntu可以使用dpkg或者dpkg-query来检测软件包是否已经安装了。dpkg:dpkg -s <packagename> dpkg-query:dpkg-query -l <packagename> 如果要检测命令是属于哪个包,可以:dpkg -S `which <command>

使用Kotlin Spek 2来做Android的行为驱动测试

Spek是Kotlin一个很强大的行为驱动测试(behavior-driven testing)的框架。在android里使用spek2,我们可以很容易地定义一个清晰的,可读性高的测试用例。地址:https://www.spekframework.org/Spek2是在JUnit 5上运行的一个框架。在使用之前需要安装JUnit5.安装JUnit 5打开project级别的build.gradle

SpringBoot 打包跳过单元测试的几种方法

SpringBoot打包时要跳过单元测试有几种方法。方法一:在properties定义<skipTests>,设置其值为true。<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEnc