days since this article was written, please be aware of its timeliness
Preface
Although the following only discusses Sinon’s
spyinterface, it equally applies to interfaces like stub.
The tests utilize the
MochaSinonChailibraries/frameworks, which won’t be introduced here.
Spying on a 单独-exported module
Consider the following module foo.js, which exports a foo function:
1 | |
Here’s a module bar.js that imports this foo function:
1 | |
And here’s a test case bar.test.js:
1 | |
The reason this fails is that the imported bar in bar.test.js is a variable containing a function, whereas Sinon only spies on the variable bar itself, not the function it references. The following test case exhibits the same issue:
1 | |
This scenario can be resolved using the approach described in the next section:
Spying on a 全部-exported module
1 | |
But how do we test non-exported functions? For example, in the first test case above, how can we verify whether foo was called?
There are two methods. The first, purely ES6 approach, would require re-exporting the imported foo function within bar (which violates the 测试未导出函数 precondition).
The second method involves using a Babel plugin. This approach essentially converts ES6 to ES5 for testing. The plugin is called babel-plugin-rewire and is a preset-type plugin.
“Rewire,” as the name suggests, means to reconnect or rewire. In other words, this plugin 可以将某个模块中导入的但是并未导出却在该模块中调用的函数进行重新导出以方便测试. It’s a bit convoluted to explain, so let’s look at a complete example:
Here’s a foo.js:
1 | |
Here’s a bar.js that imports foo.js but doesn’t export foo:
1 | |
And the test file bar.test.js:
1 | |
As mentioned in the comments, default exports are important because re-rewire can only be performed through the __Rewire__ property on the default export, meaning testing cannot be done as shown below:
1 | |
Even something like the previously mentioned 全部导出 cannot be achieved:
1 | |
If you want to use 全部导出 to test unexported functions, the file being tested must meet the condition that the function is 根作用域, for example:
1 | |
Then the test file can be written like this:
1 | |
Conclusion
Note that this applies not only to function testing but also to React components with default exports as functions/classes, since their export nature is the same—both are functions or objects. For testing React components, you might use the enzyme library.
I often wish that when facing some key decisions in life, someone could tell me the best course of action so that I would not waste my precious time. Putting myself in others' shoes, I therefore write blogs often, hoping to record in this tiny corner of the vast Internet the once-in-a-lifetime experiences that matter to me, and to help those who seek help.