在测试复制实际对象时注入全局变量,需要相同的实例
Injecting global variables while testing copy the actual object, need the same instance
我是运行用mocha、chai和sinon进行的测试,主要目的是模拟localStorage并将其作为全局变量注入,这样我以后就可以调用它了localStorage
。主要问题是我使用 webpack 和 babel 使用节点策略进行测试(为了更快的测试和开发),所以我使用 rewire 来覆盖全局变量。
这是我的代码:
import assert from 'assert';
import rewire from 'rewire';
import sinon from 'sinon';
import {expect} from 'chai';
import todos from './mocks/todos';
import LocalStorage from './mocks/localStorage.js';
let Todo = rewire('../src/store/todo');
let todo,
localStorage,
localStorageMock,
task = { id: 5, title: 'new task', completed: false };
describe('Todo Model', () => {
beforeEach(function() {
localStorage = new LocalStorage();
localStorageMock = sinon.mock(localStorage);
Todo.__set__('globals.localStorage', localStorage);
todo = new Todo();
});
describe('add todo', () => {
it('save the todo to localStorage',() =>{
localStorageMock.expects('setItem').once();
todo.create(task);
localStorageMock.verify();
expect(localStorage.data['todos']).to.be.a('string');
});
});
});
编辑
这是待办事项模块
import _ from 'ramda';
import {Maybe} from 'ramda-fantasy';
let log = (x, y) => console.log(x, y);
// getItem :: storage, item -> string
let getItem = _.curry((storage, x) => storage.getItem(x));
// parse :: string -> json
let parse = (x) => x ? JSON.parse(x): null;
// stringify :: json -> string
let stringify = (x) => JSON.stringify(x);
// setItem :: storage, string, jsonStringified
let setItem = _.curry((storage, x, y) => storage.setItem(x, y));
export default class todo {
constructor(){
this.name = 'name';
this.storage = localStorage;
this.storage.getItem('todos') ? '' : this.storage.setItem('todos', '');
}
create(todo) {
this.new = _.compose(_.map(_.compose(setItem(this.storage, 'todos'), stringify, _.append(todo), parse)), Maybe, getItem(this.storage));
this.new('todos');
}
}
测试失败,因为setItem
从未被调用,但在代码中被调用。如果你 return localStorage 的实例并将其与我的模拟 localStorageMock === returnedLocalStorage
进行比较,则给出 false.
这是因为重新连接复制每个方法分别创建一个新对象。
当我更改 beforeEach 块时:
Todo.__set__('globals.localStorage', localStorage);
到
Todo.__set__('localStorage', localStorage);
它抛出一个引用错误:
1) Todo Model "before each" hook for "have a create method":
ReferenceError: localStorage is not defined
at Function.eval (eval at __set__ (src/store/todo.js:18:26), <anonymous>:1:14)
at Function.__set__ (src/store/todo.js:18:26)
at Context.<anonymous> (spec/index.spec.js:18:5)
如何测试是否调用了具有全局依赖性的服务?
我认为问题在于您的 Todo 模块引用了 localStorage
,但您正在重新布线 global.localStorage
。在 rewire README 中,模拟全局变量的示例使用名称 process
和 console
,而不是 globals.process
或 globals.console
。 Rewire "smart" 不足以(我假设是设计使然)知道它们是同一回事。您需要使用模块中使用的确切名称。考虑到这一点,我想你想要的是:
Todo.__set__('localStorage', localStorage);
P.S。在这种情况下,我认为重新布线可能有点矫枉过正。由于您的模块的构造函数执行 this.storage = localStorage
,您可以监视 todo.storage
:
todo = new Todo();
sinon.spy(todo.storage, 'setItem');
编辑
我 认为 发生 ReferenceError 是因为,与示例中的 console
和 process
不同,localStorage
在 Node.js。然而,我对 rewire 的了解并不完整,所以可能会有另一个答案。
我认为最简单的解决方法是在全局上下文中创建它(也不需要重新布线):
describe('Todo Model', () => {
beforeEach(function() {
localStorageMock = sinon.mock(new LocalStorage());
global.localStorage = localStorageMock; // <-- set it here
todo = new Todo();
});
afterEach(function() {
delete global.localStorage; // <-- clean up here
});
// ...
});
有点丑,但也很简单。
我是运行用mocha、chai和sinon进行的测试,主要目的是模拟localStorage并将其作为全局变量注入,这样我以后就可以调用它了localStorage
。主要问题是我使用 webpack 和 babel 使用节点策略进行测试(为了更快的测试和开发),所以我使用 rewire 来覆盖全局变量。
这是我的代码:
import assert from 'assert';
import rewire from 'rewire';
import sinon from 'sinon';
import {expect} from 'chai';
import todos from './mocks/todos';
import LocalStorage from './mocks/localStorage.js';
let Todo = rewire('../src/store/todo');
let todo,
localStorage,
localStorageMock,
task = { id: 5, title: 'new task', completed: false };
describe('Todo Model', () => {
beforeEach(function() {
localStorage = new LocalStorage();
localStorageMock = sinon.mock(localStorage);
Todo.__set__('globals.localStorage', localStorage);
todo = new Todo();
});
describe('add todo', () => {
it('save the todo to localStorage',() =>{
localStorageMock.expects('setItem').once();
todo.create(task);
localStorageMock.verify();
expect(localStorage.data['todos']).to.be.a('string');
});
});
});
编辑
这是待办事项模块
import _ from 'ramda';
import {Maybe} from 'ramda-fantasy';
let log = (x, y) => console.log(x, y);
// getItem :: storage, item -> string
let getItem = _.curry((storage, x) => storage.getItem(x));
// parse :: string -> json
let parse = (x) => x ? JSON.parse(x): null;
// stringify :: json -> string
let stringify = (x) => JSON.stringify(x);
// setItem :: storage, string, jsonStringified
let setItem = _.curry((storage, x, y) => storage.setItem(x, y));
export default class todo {
constructor(){
this.name = 'name';
this.storage = localStorage;
this.storage.getItem('todos') ? '' : this.storage.setItem('todos', '');
}
create(todo) {
this.new = _.compose(_.map(_.compose(setItem(this.storage, 'todos'), stringify, _.append(todo), parse)), Maybe, getItem(this.storage));
this.new('todos');
}
}
测试失败,因为setItem
从未被调用,但在代码中被调用。如果你 return localStorage 的实例并将其与我的模拟 localStorageMock === returnedLocalStorage
进行比较,则给出 false.
这是因为重新连接复制每个方法分别创建一个新对象。
当我更改 beforeEach 块时:
Todo.__set__('globals.localStorage', localStorage);
到
Todo.__set__('localStorage', localStorage);
它抛出一个引用错误:
1) Todo Model "before each" hook for "have a create method":
ReferenceError: localStorage is not defined
at Function.eval (eval at __set__ (src/store/todo.js:18:26), <anonymous>:1:14)
at Function.__set__ (src/store/todo.js:18:26)
at Context.<anonymous> (spec/index.spec.js:18:5)
如何测试是否调用了具有全局依赖性的服务?
我认为问题在于您的 Todo 模块引用了 localStorage
,但您正在重新布线 global.localStorage
。在 rewire README 中,模拟全局变量的示例使用名称 process
和 console
,而不是 globals.process
或 globals.console
。 Rewire "smart" 不足以(我假设是设计使然)知道它们是同一回事。您需要使用模块中使用的确切名称。考虑到这一点,我想你想要的是:
Todo.__set__('localStorage', localStorage);
P.S。在这种情况下,我认为重新布线可能有点矫枉过正。由于您的模块的构造函数执行 this.storage = localStorage
,您可以监视 todo.storage
:
todo = new Todo();
sinon.spy(todo.storage, 'setItem');
编辑
我 认为 发生 ReferenceError 是因为,与示例中的 console
和 process
不同,localStorage
在 Node.js。然而,我对 rewire 的了解并不完整,所以可能会有另一个答案。
我认为最简单的解决方法是在全局上下文中创建它(也不需要重新布线):
describe('Todo Model', () => {
beforeEach(function() {
localStorageMock = sinon.mock(new LocalStorage());
global.localStorage = localStorageMock; // <-- set it here
todo = new Todo();
});
afterEach(function() {
delete global.localStorage; // <-- clean up here
});
// ...
});
有点丑,但也很简单。