在单元测试中实现逻辑,以缩短语法并提高测试代码的可重用性
Implementing logic within unit tests to make syntax shorter and test code more re-usable
我经常听到开发人员说测试代码应该是"丑陋的",并且尽可能简单。
原因是,测试中的任何逻辑都需要自己测试,这造成了鸡和蛋的悖论。
我发现通过使用一些简单的逻辑,我的测试变得更加可读、结构化,测试代码也更加可重用。
问题是,这些单元测试有效吗?
我使用karma作为测试运行程序,这个特定的项目使用了一个带有连接资产管理器的节点服务器,以及前端的bower包。
如何使代码更加模块化(AMD?browserfy?),而不必从头开始实现所有内容或引入框架。
目前,全局状态在许多文件和许多闭包中都发生了变化,我不喜欢这样。如何使闭包中的代码仍然是可测试的?也许是这个包裹?
示例代码:
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);
我的意见:我觉得你太过分了。例如:
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?browserfy?),而不需要从头开始实现一切,或者引入一个框架。
框架出了什么问题?毕竟你才刚刚开始写自己的东西。最好使用第三方框架,因为团队中的每个人都知道它的确切作用。当你编写自己的助手时,每个人都必须从头开始学习。此外,最好编写帮助程序,使测试在一般情况下更加强大。可以为参数化测试或通用断言创建简单的框架。它是如何工作的,它做什么,何时以及如何改变都非常清楚。这样你就可以在所有的测试中使用它们
但是当您创建像assertEqual (done, expect, inputs, outputs, fn, decorator)
这样的函数时,没有人知道它的作用。为什么assertEquals
有这么多参数?它最多应该有2-3条(实际的、预期的、错误消息)。所以我可以在需要的时候更改assertEquals吗?还是我应该因为它对某人很重要而保持原样,然后复制粘贴?这样的测试是的维护噩梦
我经常听到开发人员说测试代码应该是"丑陋的"尽可能简单。
是的,它们应该是朴素的,不,它们不应该是丑陋的。这是你的代码,所以重构它。但重构的方式要使它们成为可读的文档。每一个测试都应该是文档中的一小部分。为每个人,而不仅仅是为你。而无需在其他辅助文件或函数
- 模糊事件的Javascript测试
- 我的单元测试选项是什么
- 如何在JUnit测试中测试wicket上的附加javascript ajaxBehavior
- Jasmine 测试在测试运行中、Firefox/Chrome 之间以及检查器开/关时的结果不一致
- 有没有办法在我的单元测试中测试一个对象是否“是”Backbone.Model
- 单元测试相关测试
- 使用 javascript 准备自动化测试以测试模块
- Spotify应用程序的推荐单元测试框架/测试运行程序
- 使用Karma和Jasmine从JS测试文件测试编译的TypeScript
- Ember:在集成测试中测试组件的WillDestroyElement逻辑
- 内部测试框架-测试iframe
- 在javascript单元测试中测试单个行
- 有办法运行烬吗.用假计时器测试验收测试
- 测试Jasmine测试是否失败
- 在e2e测试中测试路由Karma时出错
- Grunt测试:只测试特定的功能
- Angular 2测试:异步测试的正确方法是什么?
- 如何在异步测试中测试一个返回承诺的存根
- 单元测试-如何测试JavaScript缩小输出
- 为什么星号金字塔 JSPerf 测试的测试用例 2 需要这么长时间