如何“假”测试Mongoose模型的日期/时间

时间:2017-12-20 22:52:01

标签: node.js mongodb unit-testing mongoose sinon

事先,感谢您提供的任何帮助/建议!

我正在尝试完成

我正在尝试找到一种优雅的方法来测试创建Mongoose模型实例的日期/时间。

我想确保存储的时间是正确的时间。

我的模特目前看起来像这样:

const messageSchema = mongoose.Schema({
  user: { type: String, required: true },
  message: { type: String, required: true },
  created: { type: Date, default: Date.now },
});

const Message = mongoose.model('Message', messageSchema);

我将这个模型导入到mocha测试套件中,我试图按照以下方式进行测试:

const now = {Date message was created}
it('check time matches time created', () => {
  expect(message.created).to.equal(now);
});

我到目前为止所做的事

我尝试实现此目的的方法是使用Sinon's Fake timers功能。

所以我的测试用例如下:

describe('creating new message', () => {
  let clock;
  let message;
  let now;

  before(() => {
    clock = sinon.useFakeTimers();
    clock.tick(100);
    message = new Message({
      user: 'Test User',
      message: 'Test Message',
    });
    // Time the message was created
    now = Date.now();
    clock.tick(100);
  });

  it('check time matches time created', () => {
    expect(message.created).to.equal(now);
  });
});

为什么我认为这不起作用

我认为这不起作用,因为作为Mongoose模型的默认值传递的Date.now函数与Sinon假计时器隔离(伪计时器在测试文件中,模型是从另一个文件导入)。

再次感谢你!

1 个答案:

答案 0 :(得分:2)

解决方案

只需将Date.now包装在匿名函数中,就像那样:

function() { return Date.now(); }

或ES6版

() => Date.now()

因此Schema会变成类似的东西:

const messageSchema = mongoose.Schema({
   user: { type: String, required: true },
   message: { type: String, required: true },
   created: { type: Date, default: () => Date.now() },
});

为什么会这样?

因为当你执行sinon.useFakeTimers()时,sinon在后面做的是覆盖global属性Date

调用Date与调用global.Date相同。

当您将Date.now传递给mongoose时,您实际上是在传递global.Date引用的Node内部方法,而mongoose会调用此方法,不再访问global.Date 参考

但是,根据我的解决方案,我们传递了一个方法,在调用时,会访问global.Date引用,现在由Sinon存根

为了在实践中看到这种行为,您可以在Javascript中执行以下操作:

var nativeDate = Date; // accessing global.Date
Date = { now: () => 1 }; // overrides global.Date reference to a entirely new object
console.log(Date.now()); // now it outputs 1
console.log(nativeDate.now()); // outputs current date, stub doesn't work here, because it's calling the javascript native Date method, and not global.Date anymore