敲碎时间的人的个人专栏
上一篇

什么是可测试代码

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

什么是可测试代码

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

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;
}

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

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

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

 

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

X

欢迎加群学习交流

联系我们