我经常听到开发人员说测试代码应该“丑陋”,尽可能简单。
原因在于,测试中的任何逻辑都需要自我测试,并且会产生鸡蛋和蛋悖论。
我发现自己通过使用一些简单的逻辑使我的测试更具可读性,结构化和测试代码更加可重复使用。
问题是那些有效的单元测试吗?
我使用karma作为测试运行器,这个特定项目使用带有connect-asset-manager的节点服务器,以及前端的bower包。
如何使代码更加模块化(AMD?browserify?),而无需从头开始实现所有内容或引入框架。
目前,全局状态在许多文件中都有变化,而且很多闭包,我不喜欢它。 如何使闭包内的代码仍可测试?也许this package?
示例代码:
utils.js
function getRealTemplatValues (inputs, templateFn, outerId, innerClass) {
var i, res;
for (i = 0; i < inputs.length; i++) {
res = templateFn(inputs[i]);
$('#' + outerId).append(res);
}
return $('.' + innerClass).map(function(){
return $(this).html();
}).get();
};
function assertEqual (done, expect, inputs, outputs, fn, decorator) {
function iter (input, output, cb) {
var i;
for (i = 0; i < inputs.length; i++) {
cb(input[i], output[i]);
}
};
function cb (input, output) {
output = !!decorator ? decorator(output) : output;
expect(fn(input, decorator)).toBe(output);
done && done();
}
iter(inputs, outputs, cb, decorator);
};
helper.scenario.js
describe('helpers', function () {
var inputs = ["#string#string#string", "string#string", "string#string#string#", "#", "###"],
outputs = ["#string #string #string", "string #string", "string #string #string#", "#", "###"],
decorString = 'magic!',
outerId = "container",
innerClass = "inner",
decorator = function(input) {return input + decorString};
describe('breakHashtags - unit', function(done) {
it('should break hashtags with prefixed with spaces - non decorated', function (done) {
return assertEqual(done, expect, inputs, outputs, breakHashtags);
});
it('should break hashtags with prefixed with spaces - decorated', function () {
return assertEqual(done, expect, inputs, outputs, breakHashtags, decorator);
});
});
describe('handle bars integration', function () {
var outerId = "container",
outerClass = "inner",
fixture = '<div id="' + outerId + '"></div>',
template = Handlebars.compile('<div class="' + outerClass + '">{{breakHashtags hashtags}}</div>'),
decoratedTemplate = Handlebars.compile('<div id="inner">{{breakHashtags hashtags decoratorHelper}}</div>');
beforeEach( function () {
addFixture(fixture);
});
afterEach( function () {
clearMyFixtures();
Handlebars.helpers['decoratorHelper'] && delete Handlebars.helpers['decoratorHelper'];
});
it('should have the breakHashtags function registered as a helper', function () {
expect(Handlebars.helpers['breakHashtags']).toEqual(breakHashtags);
});
it('should replace hashtags with hashtags prefixed with spaces', function(){
var realValues = getRealTemplatValues(inputs, template, outerId, outerClass);
assertEqual(done, expect, inputs, realValues, breakHashtags);
});
it('should replace hashtags with hashtags prefixed with ' +
'spaces and invoke the decorator on theo put', function(){
Handlebars.registerHelper('decoratorHelper', decorator);
var realValues = getRealTemplatValues(inputs, decoratedTemplate, outerId, outerClass);
assertEqual(done, expect, inputs, realValues, breakHashtags, decorator);
});
});
});
helpers.js:
function breakHashtags (text, decorator) {
var pattern = /\w(#).+/i,
p1, p2, idx = text.search(pattern), prefix = '';
while (idx > 0) {
if (idx === 1) {
text = text.substring(idx);
prefix = text.substring(0, 1);
}
else{
p1 = text.substring(0, idx + 1);
p2 = text.substring(idx + 1);
text = p1 + ' ' + p2;
console.log(p1, p2, text)
}
idx = text.search(pattern);
}
return !!decorator ? decorator(prefix + text) : prefix + text;
}
Handlebars.registerHelper('breakHashtags', breakHashtags);
答案 0 :(得分:2)
我的意见: 我觉得你太过分了。例如:
it('should replace hashtags with hashtags prefixed with spaces', function(){
var realValues = getRealTemplatValues(inputs, template, outerId, outerClass);
assertEqual(done, expect, inputs, realValues, breakHashtags);
});
它可能对您更具可读性,但仅限于您。编程是团队游戏。当其他人第一次看到该代码时,他不知道发生了什么。 该测试中有描述,但它与您实际测试的内容无关。不知道你的代码究竟做了什么,但测试看起来应该是这样的:
it('should replace hashtags with hashtags prefixed with spaces', function(){
var result = testedFunction("#hashtag1#hashtag2#hashtag3");
assertThat(result).isEqualTo("#hashTag1 #hashTag2 #hashTag3");
});
现在每个测试都是一个完整的测试。没有人必须检查不同的文件来理解它。如果有人改变你的助手功能怎么办?你会注意到吗?
如何使代码更加模块化(AMD?browserify?),而不必 从头开始实现一切或引入框架。
框架有什么问题?毕竟你刚开始写自己的。最好使用第三方框架,因为团队中的每个人都知道它究竟是做什么的。当你写自己的助手时,每个人都必须从头开始学习。 此外,最好编写帮助程序,使您的测试在一般方面更强大。随意为参数化测试或通用断言创建简单的框架。它非常清楚它是如何工作的,它做什么,何时以及如何改变它。所以你可以在所有测试中使用它们
但是当你创建像assertEqual (done, expect, inputs, outputs, fn, decorator)
这样的函数时,没有人知道它的作用。为什么地狱assertEquals
有这么多参数?它应该至多2-3(实际的,预期的,错误消息)。我可以在需要时更改断言等级吗?或者我应该保留它,因为它对某人很重要,只需复制粘贴它?这样的测试是维护的噩梦
是的,他们应该是明白的,不,不应该是丑陋的。这是你的代码所以重构它。但重构的方式是使它们成为可读的文档。每一项测试都应该是它自己的文档的一小部分。适合所有人,不只是为了你。无需在其他帮助文件或函数中查找任何内容我经常听到开发人员说测试代码应该“丑陋”,并且 尽可能简单。