node.js测试: 如何利用import / require语法打桩测试指定函数/ 类方法
来源:互联网 发布:关机准备配置windows 编辑:程序博客网 时间:2024/06/05 18:05
node.js测试: 如何利用import / require语法打桩测试指定函数/ 类方法
ES6: Use of “import { property } from ‘module'” is Not a Great Plan – Ex Ratione
https://www.exratione.com/2015/12/es6-use-of-import-property-from-module-is-not-a-great-plan/
ES6: Use of "import { property } from 'module'" is Not a Great Plan
The Javascript ES6 standard brings a new syntax for declaring imports from other modules and exports from the present modules. Here I'll argue that some of that syntax, specifically the ability to import one or more named properties from a module, is overused in the community and causes more trouble than it is worth when writing ES6 Javascript applications. The ES6 syntax for import and export is noted below in brief:
// Import a module without any import bindings, just to
// execute its code without assigning any variables here.
import
'example'
;
// Import the default export of a module.
import
exampleDefaultExport from
'example'
;
// Import a named export of a module.
import
{ property } from
'example'
;
// Import a named export to a different name,
import
{ property as exampleProperty } from
'example'
;
// Import all exports from a module as properties of an object.
import
* as example from
'example'
;
// Export a named variable.
export
var
property =
'example property'
;
// Export a named function.
export
function
property() {};
// Export an entity to the default export.
export
default
'example default'
;
// Export an existing variable.
var
property =
'example property'
;
export
{ property };
// Export an existing variable as a new name.
export
{ property as exampleProperty };
// Export an export from another module.
export
{ property as exampleProperty } from
'example'
;
// Export all exports from another module.
export
* from
'example'
;
Import and Export in Node.js and ES5
There are both obvious and subtle differences between the ES6 syntax listed above and the module frameworks in common use for ES5 Javascript ecosystems, such as Node.js. In the Node.js ES5 world, every module exports a default object unless that is replaced, and an entirely empty module still exports an object with no properties. There is only one way to import a module, and that imports either the default export object, or whatever that object was explicitly replaced with. So:
exampleObject.js:
// Exports { x: 'y' }.
exports.x =
'y'
;
// This has the same result; exports is just a convenient reference
// to module.exports:
// module.exports.x = 'y';
exampleString.js:
// Overwrite the default exports object to export 'y'.
module.exports =
'y'
;
exampleEmptyModule.js:
// There is no code in this module.
exampleImports.js:
// This is { x: 'y' }.
var
exampleObject = require(
'./exampleObject'
);
// This is 'y'.
var
exampleString = require(
'./exampleString'
);
// This is {}.
var
exampleEmptyModule = require(
'./exampleEmptyModule'
);
Import and Export in ES6
In ES6 modules there is no one straightforward analogy to module.exports in Node.js ES5, though it can be recreated by some combinations of export and import definitions:
example.es6.js:
// Set the default exported property to { x: 'y' }.
export
default
{ x:
'y'
};
exampleProperty.es6.js:
// Set the exported property x to 'y'.
export
var
x =
'y'
;
exampleEmptyModule.es6.js:
// There is no code in this module.
exampleImports.es6.js:
// This default import obtains an object in the same way as ES5
// require('./example'), but requires that the object was
// explicitly exported as the default.
//
// This is { x: 'y' }.
import
exampleDefault from
'./example'
;
// We can see the default as a property on the object returned by
// this form of import.
//
// This is something like: { default: { x: 'y' } }.
import
* as example from
'./example'
;
// Things get more interesting without the default export.
//
// This is 'y'.
import
{ x } from
'./exampleProperty'
;
// This is undefined - it is the default export and this module
// has no default export, only exported properties.
import
examplePropertyDefault from
'./exampleProperty'
;
// This is an object, however, in much the same way as ES5
// require('./exampleProperty'), but again requires that the
// export was set up a certain way.
//
// This is { x: 'y' }.
import
* as exampleProperty from
'./exampleProperty'
;
// For an empty module:
//
// This, the default export, is undefined.
import
exampleEmptyModuleDefault from
'./exampleEmptyModule'
;
// This is {}.
import
* as exampleEmptyModule from
'./exampleEmptyModule'
;
The Syntax "import { x } from 'y'" is Popular
If you look at the ES6 ecosystem, you'll see that the use of property import is popular. I'm seeing a lot of it now that I'm working with modules relating to React, both in documentation and code. In Redux, for example, important functions are exported as properties in modules with no default export, and the documentation provides examples like the following:
import
{ createStore } from
'redux'
import
todoApp from
'./reducers'
let
store = createStore(todoApp)
This could equally be written in the following way:
import
* as redux from
'redux'
import
todoApp from
'./reducers'
let
store = redux.createStore(todoApp)
It isn't, however, and it doesn't take very much work on a real application to start to see that using property import is a problem. Why is it a problem? Because it impedes the use of mocking and stubbing in tests, such as the functionality provided by frameworks like Sinon. It is somewhere between very hard and impossible to write sufficient unit tests without the ability to stub and spy on function calls. It requires an annoying amount of additional boilerplate code to make that possible for property importing, as the following small examples illustrate.
Stubbing in ES5 Javascript Testing
Stubbing a function in Javascript requires the function to be bound to a context, any context, that is in scope for both the test code and the code being tested. In a sane world this context is provided by the module. For example, in ES5 Node.js:
lib/example.js
exports.fn =
function
() {};
lib/invokeExample.js
var
example = require(
'./example'
);
// Usage.
exports.invokeExample =
function
() {
return
example.fn();
};
test/lib/invokeExample.spec.js
var
sinon = require(
'sinon'
);
var
example = require(
'./example'
);
var
invokeExample = require(
'../../lib/invokeExample'
);
// The example function can be stubbed directly, and the
// stub will now be used when invokeExample.invokeExample
// is called.
sinon.stub(example,
'fn'
).
return
({});
The thing to avoid doing in ES5 is the following, overriding module.exports with a function. All too many people do this and it is inconsiderate, as any module using that code must then take extra steps to be usefully unit tested:
lib/example.js
module.exports =
function
() {};
lib/invokeExample.js
var
example = require(
'./example'
);
// Exported for test purposes. If we don't do this, then
// example is encapsulated here and cannot be stubbed.
exports.example = example;
// Usage.
exports.invokeExample =
function
() {
return
exports.example();
};
test/lib/invokeExample.spec.js
var
sinon = require(
'sinon'
);
var
invokeExample = require(
'../../lib/invokeExample'
);
// The example function can only be stubbed because additional code was
// written to export it and use the export explicitly inside invokeExample.
sinon.stub(invokeExample,
'example'
).
return
({});
Stubbing in ES6
In ES6 using "import { x } from 'y'" is analogous to overwriting module.exports with a function in ES5. The result is that an imported function is encapsulated in the module and cannot be stubbed in unit tests without writing more boilerplate code that would otherwise have been the case. See this example:
lib/example.es6.js
export
function
fn () {};
lib/invokeExample.es6.js
import
{ fn } from
'./example'
;
// Export something that allows useful stubbing.
export
var
__ = {
exampleFn: fn
};
// Usage.
export
function
invokeExample () {
return
__.exampleFn();
};
test/lib/invokeExample.spec.es6.js
import
sinon from
'sinon'
;
import
* as invokeExample from
'../../lib/invokeExample'
;
// The example function can only be stubbed because additional code was
// written to export it and use the export explicitly inside invokeExample.
sinon.stub(invokeExample.__,
'exampleFn'
).
return
({});
This boilerplate is unnecessary; it goes away if a sensible import statement is used, as in the following example:
lib/example.es6.js
export
function
fn () {};
lib/invokeExample.es6.js
import
* as example from
'./example'
;
// Usage.
export
function
invokeExample () {
return
example.fn();
};
test/lib/invokeExample.spec.es6.js
import
sinon from
'sinon'
;
import
* as example from
'../../lib/example'
;
import
* as invokeExample from
'../../lib/invokeExample'
;
// The example function can be stubbed directly, and the
// stub will now be used when invokeExample.invokeExample
// is called.
sinon.stub(example,
'fn'
).
return
({});
In Short, Avoid the Use of "import { x } from 'y'"
The bottom line is that "import { x } from 'y'" should be largely or completely absent from ES6 code in order to make the code both clean and testable. Yes, it's the new new thing, but no, it doesn't make life any better.
// Do this.
import
* as example from
'example'
;
example.fn();
// Don't do this.
// import { fn } from 'example';
// fn();
- node.js测试: 如何利用import / require语法打桩测试指定函数/ 类方法
- 存储--IT打桩测试方法
- C++单元测试--打桩测试
- node.js中require如何定位module
- 安装测试node.js
- Node.js测试
- node.js安装、测试
- 测试Node.js 应用程序
- Node.js UDP测试
- node js 安装 测试
- node.js自动化测试断言包assert的方法说明
- Node.js ,require.main
- node.js中的require
- node.js之require
- 使用JUnit4与JMockit进行打桩测试
- Node.js 测试批处理命令
- Node.js的编译测试
- node.js应用程序的测试。
- CodeForces
- git学习笔记整理-3提交与移除
- 事务的四种隔离级别和七种传播机制
- [HDU6074] Phone Call
- Kotlin Reference (八) Classes and Objects
- node.js测试: 如何利用import / require语法打桩测试指定函数/ 类方法
- FFT 模板(hdu 1402)
- 常用正则表达式
- 一些在线工具
- HDU 6069 Counting Divisors
- cocos骨骼动画
- QML中能使用什么JS库
- 时间序列数据的存储和计算
- 发布Feature service