Sinon – 何时使用间谍/嘲笑/存根或只是简单的断言?

我试图理解Sinon在一个节点项目中是如何正确使用的。 我已经通过例子和文档,但我还没有得到它。 我已经build立了一个具有以下结构的目录来尝试通过各种Sinonfunction来了解它们的适用位置

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

诚然,这是非常人为的,但现在我想testing任何调用func1导致func2被调用,所以我会用

 describe('Function 2', function(){ it('should be called by Function 1', function(){ myFuncs.func1(); assert(spyFunc2.calledOnce); }); }); 

我也想testingfunc1将返回200所以我可以使用

 describe('Function 1', function(){ it('should return 200', function(){ assert.equal(myFuncs.func1(), 200); }); }); 

但是我也看到了在这种实例中使用stubs的例子,比如

 describe('Function 1', function(){ it('should return 200', function(){ var test = sinon.stub().returns(200); assert.equal(myFuncs.func1(test), 200); }); }); 

这些有什么不同? stub是什么给一个简单的断言testing不?

当我的计划变得越来越复杂时,我遇到的最麻烦的事情就是这些简单的testing方法会如何演变。 说我开始使用mysql并添加一个新的function

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

我知道当谈到数据库时,一些人build议有一个testing数据库用于这个目的,但是我的最终目标可能是一个带有许多表的数据库,并且为了testing而复制这个数据可能会很麻烦。 我已经看到引用嘲笑与SINON分贝,并试图跟随这个答案,但我不明白什么是最好的办法。

你在一个post上提出了很多不同的问题,我会尽力解决。

  1. 用两个函数testingmyFuncs。

Sinon是一个具有广泛特色的嘲笑图书馆。 “嘲弄”意味着你应该用嘲笑或存根来替代将要testing的部分内容。 Sinon文档中有一篇很好的文章描述了这个区别。 当你在这种情况下创build一个间谍…

 var spyFunc1 = sinon.spy(myFuncs.func1); var spyFunc2 = sinon.spy(myFuncs.func2); 

…你刚刚创build了一个观察者。 myFuncs.func1和myFuncs.func2将被一个间谍函数替代,但它将被用于logging调用参数并在此之后调用实函数。 这是一个可能的场景,但是请注意,在testing(例如:数据库查询)中调用myFuncs.func1 / func2的所有可能复杂的逻辑之后将运行。

2.1。 描述('函数1',…)testing套件看起来太devise了我。

你不明白你的意思是哪个问题。 返回一个常量值的函数并不是一个真实的例子。在大多数情况下会有一些参数,被testing的函数会实现一些转换input参数的algorithm。 所以在你的testing中,你将部分实现相同的algorithm来检查函数是否正常工作。 那就是TDD到位的地方,这实际上是假设你从一个testing开始执行,并拿出部分unit testing代码来实现被testing的方法。

2.2。 存根。 在给定的例子中,unit testing的第二个版本看起来没有用处。 func1不接受任何参数。

 var test = sinon.stub().returns(200); assert.equal(myFuncs.func1(test), 200); 

即使您用100replace返回部分,testing也会成功运行。 有意义的是,例如,用stubreplacefunc2,以避免在testing中启动大量计算/远程请求(DB查询,http或其他API请求)。

 myFuncs.func2 = sinon.spy(); assert.equal(myFuncs.func1(test), 200); assert(myFuncs.func2.calledOnce); 

unit testing的基本规则是unit testing应尽可能简单,提供尽可能小的代码片段检查。 在这个testing中,func1正在testing,所以我们可以忽略func2的逻辑。 哪个应该在另一个unit testing中testing。 介意做下面的尝试是没用的:

 myFuncs.func1 = sinon.stub().returns(200); assert.equal(myFuncs.func1(test), 200); 

因为在这种情况下,你用stub掩盖了真正的func1逻辑,而实际上你正在testingsinon.stub()。return()。 相信我,它运作良好! :d

  1. 嘲笑数据库查询。 嘲笑数据库一直是一个障碍。 我可以提供一些build议。

3.1。 环境分散。 即使是一个小项目,也最好有一个发展,阶段和生产完全独立的环境。 包括数据库。 这意味着你有一个自动的数据库创build方式:脚本或ORM。 在这种情况下,您将使用before()/ beforeEach()方法在testing引擎中轻松维护testing数据库,以便为您的testing提供干净的结构。

3.2。 有很好的碎片代码。 最好有几层。 最低(DAL)应与业务逻辑分开。 在这种情况下,你将编写业务类的代码,只是嘲笑DAL。 要testingDAL,您可以使用您提到的方法(sinon.mock整个模块)或某些特定的库(例如:用SQLitereplace数据库引擎以进行testing,如此处所述)

  1. 结论。 “一旦我的计划变得更加复杂,这些简单的testing方法会如何发展”

除非你开发你的应用程序,考虑到testing,因此很难保持unit testing。 坚持主规则 – 保持每个unit testing尽可能小。 否则你是对的,它最终会变得混乱。 因为你的应用程序的不断发展的逻辑将被包含在你的testing代码中。

Interesting Posts