混淆模拟无状态 React 组件辅助函数与 sinon、enyzme 和 ES6 导入
Confusion with mocking stateless React component helper functions with sinon, enyzme, and ES6 imports
我目前正在尝试对 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(myFunc)
选项时,为什么我必须使用最后一个 spy(object, 'myfunc')
方法?
Why do I have to attach someHelper
to the helpers
object for this test to work?
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()
.
的原因
And why do I have to use that last spy(object, 'myfunc')
method...
这又与用包装版本替换函数有关。
...when the sinon docs show a spy(myFunc)
option?
我发现这个成语一般用处有限。它不会像您想象的那样监视对 myFunc
的所有调用,除非 这些调用是对作为 spy()
结果的间谍对象进行的。
例如:
let callback = (err, result) => { ... }
...
let spy = sinon.spy(callback);
someFuncToTest(spy);
expect(spy.callCount).to.equal(1);
所以不是直接传递回调函数,而是传递间谍。
问题不是 React 或 Sinon 特有的,而是一般 JS 的问题。
如果发生这样的事情
var spiedMethod = (...args) => {
console.log('spy!');
return object.method(...args);
};
这会创建一个新函数,它不会取代原来的 object
方法。如果调用 object.method
,则不会调用 spiedMethod
- 将调用原始方法。要在 object
上替换它,应修改对象:
object.method = spiedMethod;
这正是诗乃间谍所做的。
let spy = sinon.spy(myFunc);
returns sinon.spy(myFunc)
提供功能的间谍。为了监视调用,应该调用 spy
而不是 myFunc
。
除非完成这样的事情
helpers.someHelper = sinon.spy(helpers.someHelper);
Sinon 无法将 spy
函数与间谍方法关联起来。这就是
sinon.spy(helpers, 'someHelper')
前来救援。它本质上是做 helpers.someHelper = spy
但也在内部保存了原来的方法,所以当 helpers.someHelper.restore()
被调用时它可以做 helpers.someHelper = someHelperOriginal
。
我目前正在尝试对 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(myFunc)
选项时,为什么我必须使用最后一个 spy(object, 'myfunc')
方法?
Why do I have to attach
someHelper
to thehelpers
object for this test to work?
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()
.
And why do I have to use that last
spy(object, 'myfunc')
method...
这又与用包装版本替换函数有关。
...when the sinon docs show a
spy(myFunc)
option?
我发现这个成语一般用处有限。它不会像您想象的那样监视对 myFunc
的所有调用,除非 这些调用是对作为 spy()
结果的间谍对象进行的。
例如:
let callback = (err, result) => { ... }
...
let spy = sinon.spy(callback);
someFuncToTest(spy);
expect(spy.callCount).to.equal(1);
所以不是直接传递回调函数,而是传递间谍。
问题不是 React 或 Sinon 特有的,而是一般 JS 的问题。
如果发生这样的事情
var spiedMethod = (...args) => {
console.log('spy!');
return object.method(...args);
};
这会创建一个新函数,它不会取代原来的 object
方法。如果调用 object.method
,则不会调用 spiedMethod
- 将调用原始方法。要在 object
上替换它,应修改对象:
object.method = spiedMethod;
这正是诗乃间谍所做的。
let spy = sinon.spy(myFunc);
returns sinon.spy(myFunc)
提供功能的间谍。为了监视调用,应该调用 spy
而不是 myFunc
。
除非完成这样的事情
helpers.someHelper = sinon.spy(helpers.someHelper);
Sinon 无法将 spy
函数与间谍方法关联起来。这就是
sinon.spy(helpers, 'someHelper')
前来救援。它本质上是做 helpers.someHelper = spy
但也在内部保存了原来的方法,所以当 helpers.someHelper.restore()
被调用时它可以做 helpers.someHelper = someHelperOriginal
。