与使用sinon,enyzme和ES6导入的模拟无状态React组件辅助函数混淆

时间:2016-07-29 17:47:52

标签: javascript testing reactjs ecmascript-6 sinon

我目前正在尝试对React组件进行单元测试,并且在帮助函数模拟函数方面遇到了一些困惑。该模块看起来像这样

export const someHelper = () => {
  return ( <div></div> )
}

const MyComponent = () => {
  return (
    <span>
      {someHelper()}
    </span>
  )
}
export default MyComponent

然后这就是测试文件的样子

import chai, { expect } from 'chai'
import chaiEnzyme from 'chai-enzyme'
import sinon from 'sinon'
import { shallow } from 'enzyme'
import React from 'react'

import MyComponent, { someHelper } from './MyComponent'

describe('MyComponent test', function (){
  it.only('should call someHelper once', function () {
    let spy = sinon.spy(someHelper)
    let myComponent = shallow(
      <MyComponent />
    )
    expect(spy.callCount).to.equal(1)
  })
})

然而,当callCount等于0时,此测试失败。我认为它可能与someHelper函数的上下文有关,所以我做了这些更改

export const helpers = {
  someHelper () {
    ...
  }
}
const MyComponent = () => {
   ...
   {helpers.someHelper()}
   ...
}

_

import MyComponent, { helpers } ...

describe(...{
  it(...{
    let spy = sinon.spy(helpers.someHelper)
     ...
  })
})

但是这个测试仍然失败,callCount等于0.然后我做了这些修改

describe(...{
  it(...{
    let spy = sinon.spy(helpers, 'someHelper')
     ...
  })
})

然后测试现在通过。

为什么我必须将someHelper附加到helpers对象才能使此测试生效?当sinon docs显示spy(object, 'myfunc')选项时,为什么必须使用上一个spy(myFunc)方法?

2 个答案:

答案 0 :(得分:3)

  

为什么我必须将someHelper附加到helpers对象才能使此测试生效?

Sinon必须能够使用spy / stub-wrapped版本替换对现有函数的引用,并且只有当该引用存储在对象中时才能这样做(helpers在这种情况下)。

它基本上是这样做的:

let functionToSpyOn = helpers.someHelper;
let spy = sinon.spy(functionToSpyOn);
helpers.someHelper = spy;

这里的另一个复杂因素是原始代码必须通过此引用调用该函数,所以这样的东西也不起作用:

const someHelper     = () => { ... }
export const helpers = { someHelper }
const MyComponent    = () => {
   ...
   {someHelper()}
   ...
}

原因是MyComponent不使用存储在helpers中的引用,这是被Sinon取代的引用。这就是组件需要使用helpers.someHelper()

的原因
  

为什么我必须使用最后spy(object, 'myfunc')方法...

这又与将函数替换为它的包装版本有关。

  

......当sinon docs显示spy(myFunc)选项时?

我发现这个成语通常用途有限。正如您所想,它不会监视对myFunc 的所有调用,除非spy()的结果发出间谍对象的调用。

例如:

let callback = (err, result) => { ... }
...
let spy = sinon.spy(callback);
someFuncToTest(spy);
expect(spy.callCount).to.equal(1);

因此,不是直接传递回调函数,而是传递间谍。

答案 1 :(得分:1)

问题不是针对React或Sinon而是针对JS的。

如果发生类似的事情

var spiedMethod = (...args) => {
  console.log('spy!');
  return object.method(...args);
};

这会创建一个不替换原始object方法的新函数。如果调用object.method,则不会调用spiedMethod - 原始方法将被调用。要在object上替换它,应修改对象:

object.method = spiedMethod;

这正是Sinon间谍所做的事情。

let spy = sinon.spy(myFunc);

为提供的函数返回sinon.spy(myFunc)间谍。为了对来电进行监视,应调用spy而不是myFunc

除非这样的事情已经完成

helpers.someHelper = sinon.spy(helpers.someHelper);

Sinon无法将spy函数与spied方法联系起来。这是

sinon.spy(helpers, 'someHelper')

来救援。它本质上是helpers.someHelper = spy,但也会在内部保存原始方法,因此在调用helpers.someHelper = someHelperOriginal时它可以helpers.someHelper.restore()