Unit Testing with Sinon.JS

Preface

Which kind of method is the easiest to test? In my opinion, the answer is like this:
哪种方法最易于测试呢?在我看来,答案应该是这样的:

case1 function(s) {
    return s.split(' ').join('.');
}

It is a pure functional method, which can be tested as easily as below:
这是一个典型的函数式编程, 它的测试很简单:
assert.equal(case1('Sue Zh'), 'Sue.Zh')
Another method easy to test is that can change something global, like:
另一种更改全局属性的方法也像上述方法那样:

case2 function(s) {
    this.set('name', s.split(' ').join(',');
}

Similarly, it can be tested as easily as below:
同样地, 它的测试也很简单

case2('Sue Zh');
assert.equal(this.name, 'Sue.Zh');

How to test other kind of methods, such as that only invoke some methods and doesn't return anything or that always throws errors in testing environment, etc. If you are faced with these problems, it's a good idea to try Sinon.JS.
那么,其他种类的方法要怎么测试呢, 比如只调用其它方法, 不返回任何值的方法? 再比如在测试环境一直报错的方法?如果你正面临这样的问题,不妨试试Sinon.JS。
Let me introduce Sinon.JS to you in detail by giving some simple cases.
让我通过一些简单的案例详细介绍一下Sinon.JS


Case3

case3 function(s) {
    this.anotherMethod1(s.length);
    this.anotherMethod2(s.length+1);
}
what aspects should we test on this method?

对于这个方法,在单元测试的时候,我们关心什么呢?

  1. if the two methods invoked
    anotherMethod1与anotherMethod2是否被调用
  2. if the two methods invoked with corrent parameters
    是否向两个方法传入了正确的参数

By Sinon, we can test the two aspects by spy:
通过Sinon,我们可以通过spy测试它们:

let spy1 = sinon.spy(this, 'anotherMethod1');
let spy2 = sinon.spy(this, 'anotherMethod2');
case3('Sue Zh');
assert.ok(spy1.calledWith(6));
assert.ok(spy2.calledWith(7));
What's spy?

It is like a spy you assign to spy a method, which would record something during the method's invocation.
spy就像一个你委托去监视某一个方法的侦探,它会在方法调用期间窥探出你想要的信息。

What else can spy do?
  1. called, notCalled
    If the spy was called.
    是否spy窥探的方法被调用。
  2. calledOnce, calledTwice, calledThrice, callCount
    How many times the spy was called.
    spy窥探的方法被调用的次数。
  3. firstCall, secondCall, thirdCall, lastCall, getCall(n), getCalls()
    Get a specific call or all calls
    获得某一个或所有的调用。
  4. calledBefore(anotherSpy), calledAfter(anotherSpy), calledImmediatelyBefore(anotherSpy), calledImmediatelyAfter(anotherSpy)
    the order of spies called
    检查各个spy窥探的方法调用的顺序。
  5. calledWith(arg1, ...), alwaysCalledWith(arg1, ...), calledWithExactly(arg1, ...), alwaysCalledWithExactly(arg1, ...), calledWithMatch(arg1, ...), neverCalledWith(arg1, ...)
    Check if the spy was exactly, always or never called with args that given or can match.
    检查spy窥探的方法是否被传入了正确的参数。
  6. returned(obj), alwaysReturned(obj)
    Check if the spy returned the provided value
    检查spy窥探的方法是否返回了期望的值
  7. threw(null|String|Object), alwaysThrew(null|String|Object),
    Check if the spy threw exceptions
    检查spy窥探的方法是否抛出了异常
  8. withArgs(arg1, ...)
    Filter a spy that only spies method invoked with given parameters.
    过滤出窥探的方法被传入了制定参数的spy
  9. thisValues
    各个spy窥探的方法中的this
    Note that a spy just can spy a method, it is not able to do anything to change the invocation of a method.
    请注意spy只能窥探一个方法,它并不能改变方法调用本身。

Case4

case4 function(s) {
    this.anotherMethod1(s.length); //threw an Exception
    this.anotherMethod2(s.length);//send a XMLHttpResquest
}

We also want to test the two aspects as above, however, as you can see, anotherMethod1 throws an error in testing environment, and anotherMethod2 sends a request which may have side effects, what we can do is to try sinon.stub:
针对上面这个方法,我们想测试同样两个方面, 然而,你会发现, anotherMethod1在测试环境下抛出异常, anotherMethod2会发出一个网络请求,这个网络请求可能会出现异常,我们可以尝试sinon.stub:

let spy1 = sinon.stub(this, 'anotherMethod1');
let spy2 = sinon.stub(this, 'anotherMethod2');
case4('Sue Zh');
assert.ok(spy1.calledWith(6));
assert.ok(spy2.calledWith(6));
What's stub?

It is a special kind of spy, but can do more than a pure spy. It creates another method with pre-programmed behavior to replace case4.
Stub是一种特殊的spy, 但是具有很强的干涉性。它创建了另一个方法替代case4, 可以给另一个方法一些预设定的行为。

Difference between spy and stub?

A pure spy just spy a method and won't block it but a stuck is a kind of spy that will block a method after it accepts parameters.
单纯的spy只是窥探方法并不会阻碍它的执行,而stub则是一种会阻止原始方法执行的spy。

What else can stub do?
  1. onCall(n), onFirstCall(), onSecondCall(), onThirdCall()
    When called at some time
    当发生某一次调用时
  2. returns(obj), returnsArg(index), returnsThis()
    Makes the stub return the provided value.
    使stub返回某个值/某次调用的传入参数/调用时的this
  3. restore()
    Recovers the method or obj changed by stub
    恢复对象或者对象的方法使其可以被正常调用
  4. value()
    Assigns a new value to a variable
    给对象的某一个属性赋值
  5. set(setterFn) get(getterFn)
    Defines a new setter or getter for the stub
  6. callThrough()
  7. throws(null|String|Object)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,596评论 18 139
  • 1.Creating mock objects 1.1Class mocks idclassMock=OCMCla...
    奔跑的小小鱼阅读 2,577评论 0 0
  • 文章作者:Tyan博客:noahsnail.com 3.4 Dependencies A typical ente...
    SnailTyan阅读 4,128评论 2 7
  • “窈窕淑女,君子好逑”是男人的爱;“可怜无定河边骨,犹是春闺梦里人”是女人的爱;“慈母手中线,游子身上衣”是母亲的...
    微危道人阅读 787评论 24 45
  • 我不是雨无法把江南和华北都揽在怀里我硬着头皮想挤进每一滴雨里从华北到江南笼罩你在你梦未醒的时候静静的撑起一朵朵晶莹...
    刘汉皇阅读 427评论 18 10