如何使用 rewirejs 和 chai-spies 模拟函数以测试它?

How to mock a function using rewirejs and chai-spies in order to test it?

tl;博士

我正在尝试使用 mochachaichai-spiesrewire

特别是,我想做的是模拟模块中存在的函数并改用 chai spy。

我的设置

我有一个名为 db.js 的模块,它导出一个 saveUser() 方法

db.js

module.exports.saveUser = (user) => {
  // saves user to database
};

app.js 模块

需要 db 模块

app.js

const db = require('./db');

module.exports.handleSignUp = (email, password) => {
  // create user object
  let user = {
    email: email,
    password: password
  };
  // save user to database
  db.saveUser(user); // <-- I want want to mock this in my test !!
};

最后在我的测试文件中 app.test.js 我有以下内容

app.test.js

const chai = require('chai')
  , spies = require('chai-spies')
  , rewire = require('rewire');

chai.use(spies);
const expect = chai.expect;

// Mock the db.saveUser method within app.js
let app = rewire('./app');
let dbMock = {
  saveUser: chai.spy()
};
app.__set__('db', dbMock);

// Perform the test
it('should call saveUser', () => {
  let email = 'someone@example.com'
    , password = '123456';

  // run the method we want to test
  app.handleSignUp(email, password);

  // assert that the spy is called
  expect(dbMock.saveUser).to.be.spy; // <--- this test passes
  expect(dbMock.saveUser).to.have.been.called(); // <--- this test fails
});

我的问题

我的问题是我确保间谍被 app.handleSignUp 调用的测试失败如下

AssertionError: expected { Spy } to have been called at Context.it (spies/app.test.js:25:40)

我觉得我做错了什么,但我现在卡住了。感谢任何帮助,谢谢

终于明白问题出在哪里了。来自 rewire github 页:

Limitations

Using const It's not possible to rewire const (see #79). This can probably be solved with proxies someday but requires further research.

因此,在 app.js 中将 const db = require('./db'); 更改为 let db = require('./db'); 使所有测试都通过了。

更好的解决方案

但是,由于将所有 const 声明更改为 let 以测试带有间谍的应用程序很麻烦,因此以下方法似乎更好:

我们可以像以前那样在 app.js 中将我们的 db 模块作为 const,而不是创建间谍并覆盖 const 变量:

let dbMock = {
  saveUser: chai.spy()
};
app.__set__('db', dbMock);

我们可以使用rewiregetter方法在我们的app.test.js文件中导入db模块,并且然后使用我们的间谍模拟 saveUser() 方法(即 mutate const 变量的属性之一;因为 JS 中的对象是通过引用传递的,获取和改变app.test.js 模块中的 db 对象也在 app.js 模块中改变相同的对象)

const db = app.__get__('db');
db.saveUser = chai.spy()

最后,我们可以预期突变的 db.saveUser(即我们的间谍)将被称为

expect(db.saveUser).to.have.been.called();

综上所述,db.jsapp.js 都不会更改,但测试文件现在应如下所示:

const chai = require('chai')
  , spies = require('chai-spies')
  , rewire = require('rewire');

chai.use(spies);
let expect = chai.expect;

// Fetch the app object
let app = rewire('./app');

// Use the getter to read the const db object and mutate its saveUser property
const db = app.__get__('db');
db.saveUser = chai.spy()

// Finally perform the test using the mocked 
it('should call saveUser', () => {
  let email = 'someone@example.com'
    , password = '123456';

  // run the method we want to test
  app.handleSignUp(email, password);

  expect(db.saveUser).to.have.been.called(); // <--- now passes!!!
});