如何“伪造”日期/时间来测试猫鼬模型

Bra*_*sky 5 unit-testing mongoose mongodb node.js 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);
Run Code Online (Sandbox Code Playgroud)

我将此模型导入到mocha测试套件中,在其中尝试按照以下方式运行测试:

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

到目前为止我尝试过的

我尝试完成此操作的方法是使用Sinon的Fake计时器功能。

所以我的测试用例看起来像这样:

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);
  });
});
Run Code Online (Sandbox Code Playgroud)

为什么我认为这行不通

我相信这是行不通的,因为Date.now作为Mongoose模型的默认值传递的函数与Sinon伪计时器隔离(伪计时器位于测试文件中,并且该模型是从另一个文件导入的)。

再次感谢你!

day*_*aes 5

解决方案

只需包装Date.now一个匿名函数,就像这样:

function() { return Date.now(); }
Run Code Online (Sandbox Code Playgroud)

或者ES6版本

() => Date.now()
Run Code Online (Sandbox Code Playgroud)

所以架构会变成这样:

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

为什么它有效?

因为当你这样做时sinon.useFakeTimers(),sinon 在后面所做的就是覆盖该global属性Date

并且调用Date与调用相同global.Date

当您传递给 mongoose 时,您实际上是在传递 引用的Date.nowNode 内部方法global.Date,并且 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
Run Code Online (Sandbox Code Playgroud)