|--lib |--index.js |--test |--test.js
index.js是
var myFuncs = {}; myFuncs.func1 = function () { myFuncs.func2(); return 200; }; myFuncs.func2 = function(data) { }; module.exports = myFuncs;
test.js从以下开始
var assert = require('assert'); var sinon = require('sinon'); var myFuncs = require('../lib/index.js'); var spyFunc1 = sinon.spy(myFuncs.func1); var spyFunc2 = sinon.spy(myFuncs.func2);
不可否认,这是非常人为的,但是就目前而言,我想测试任何对func1的调用都会导致调用func2,所以我会使用
describe('Function 2',function(){ it('should be called by Function 1',function(){ myFuncs.func1(); assert(spyFunc2.calledOnce); }); });
我还想测试func1将返回200,所以我可以使用
describe('Function 1',function(){ it('should return 200',function(){ assert.equal(myFuncs.func1(),200); }); });
但我也看到了在这种情况下使用存根的例子,例如
describe('Function 1',function(){ var test = sinon.stub().returns(200); assert.equal(myFuncs.func1(test),200); }); });
这些有何不同?存根提供了什么简单的断言测试没有?
我最难以理解的是,一旦我的程序变得更复杂,这些简单的测试方法将如何发展.假设我开始使用MysqL并添加一个新功能
myFuncs.func3 = function(data,callback) { connection.query('SELECT name FROM users WHERE name IN (?)',[data],function(err,rows) { if (err) throw err; names = _.pluck(rows,'name'); return callback(null,names); }); };
我知道当谈到数据库时,有人建议为此目的使用测试数据库,但是我的最终目标可能是包含许多表的数据库,并且复制它以进行测试可能会很麻烦.我已经看到了使用sinon模拟数据库的参考,并尝试遵循this answer,但我无法弄清楚什么是最好的方法.
解决方法
>使用两个函数测试myFuncs.
Sinon是一个具有广泛功能的模拟库. “模拟”意味着你应该用模拟或存根替换将要测试的部分内容. Sinon documentation中有一篇很好的文章很好地描述了这种差异.
当你在这种情况下创建间谍时……
var spyFunc1 = sinon.spy(myFuncs.func1); var spyFunc2 = sinon.spy(myFuncs.func2);
……你刚刚创造了一个观察者. myFuncs.func1和myFuncs.func2将替换为spy-function,但它将用于记录调用参数并在此之后调用实际函数.这是一种可能的情况,但请注意myFuncs.func1 / func2的所有可能复杂的逻辑将在测试中被调用后运行(例如:数据库查询).
2.1. describe(‘Function 1’,…)测试套件看起来对我来说太吝啬了.
你究竟指的是哪个问题并不明显.
返回常量值的函数不是现实生活中的例子.在大多数情况下会有一些参数,被测函数会实现一些转换输入参数的算法.因此,在您的测试中,您将部分实现相同的算法以检查该函数是否正常工作.这就是TDD到位的地方,它实际上假设你从测试开始实现并采用部分单元测试代码来实现被测试的方法.
2.2.存根.单元测试的第二个版本在给定的示例中看起来没用. func1不接受任何参数.
var test = sinon.stub().returns(200); assert.equal(myFuncs.func1(test),200);
即使用100替换返回部分,测试也会成功运行.
有意义的是,例如,用存根替换func2以避免在测试中启动繁重的计算/远程请求(数据库查询,http或其他API请求).
myFuncs.func2 = sinon.spy(); assert.equal(myFuncs.func1(test),200); assert(myFuncs.func2.calledOnce);
单元测试的基本规则是单元测试应尽可能简单,检查代码的最小可能片段.在这个测试中,func1正在测试中,所以我们可以忽略func2的逻辑.哪个应该在另一个单元测试中测试.
请注意,进行以下尝试是没用的:
myFuncs.func1 = sinon.stub().returns(200); assert.equal(myFuncs.func1(test),200);
因为在这种情况下你用一个存根屏蔽了真正的func1逻辑,你实际上正在测试sinon.stub().return().相信我,它运作良好! :d
>模拟数据库查询.
模拟数据库一直是个障碍.我可以提供一些建议.
3.1.拥有良好的零散环境.即使对于小型项目,也更好地存在开发,阶段和生产完全独立的环境.包括数据库.这意味着您可以自动创建数据库:脚本或ORM.
在这种情况下,您可以使用before()/ beforeEach()轻松地在测试引擎中维护测试数据库,以便为测试提供干净的结构.
3.2.有很好的碎片代码.最好存在几个层次.最低(DAL)应与业务逻辑分开.在这种情况下,您将编写业务类的代码,只需模拟DAL.要测试DAL,您可以使用您提到的方法(sinon.mock整个模块)或某些特定的库(例如:使用sqlite替换db引擎进行测试,如here所述)
>结论. “一旦我的程序变得更复杂,这些简单的测试方法将如何发展”.
除非您在考虑测试的情况下开发应用程序,因此很难维护单元测试.坚持主要规则 – 保持每个单元测试尽可能小.否则你是对的,它最终会变得凌乱.因为应用程序的不断发展的逻辑将涉及您的测试代码.
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。