我有一个值,除了
instanceof Date
检查之外,其他方面都像 Javascript 日期一样。
这是我用来检查是否是日期的代码。
const deletedUser = await app.prisma.user.findUnique({
where: { id: 1 },
});
console.log(deletedUser);
console.log(typeof deletedUser.deleted_at); // object
console.log(deletedUser.deleted_at.constructor); // [Function: Date]
console.log(deletedUser.deleted_at.constructor.name); // Date
console.log(deletedUser.deleted_at instanceof Date); // false
console.log(deletedUser.deleted_at); // 2022-08-15T21:50:34.344Z
console.log(Object.prototype.toString.call(deletedUser.deleted_at) === '[object Date]'); // true
console.log(Object.prototype.toString.call(deletedUser.deleted_at)); // [object Date]
console.log(new Date(deletedUser.deleted_at)); // 2022-08-15T21:50:34.344Z
console.log(deletedUser.deleted_at.prototype); // undefined
Object.keys(deletedUser.deleted_at).forEach(prop => console.log(prop))
console.log(Object.keys(deletedUser.deleted_at)); // []
console.log(deletedUser.deleted_at.__proto__); // {}
console.log(isNaN(deletedUser.deleted_at)); // false
console.log(deletedUser.deleted_at.valueOf()); // Mon Aug 15 2022 21:56:54 GMT+0000 (Coordinated Universal Time)
console.log(Date(deletedUser.deleted_at.constructor.prototype));
我查看了以下资源来研究这个问题。
使用这些资源,我使用上述检查来检查
deleted_at
是否是 Date
并且它们都通过了。
这个答。 https://stackoverflow.com/a/643827/9530790从上面的第一个问题中提到......
您可以使用instanceof运算符,即但对于无效日期它也会返回true,例如new Date('random_string') 也是实例 日期
日期实例
如果对象跨越框架边界,这将会失败。
解决此问题的方法是通过
检查对象的类Object.prototype.toString.call(date) === '[对象日期]'
我的理解是框架与 iframe 和浏览器上的单独窗口有关。我正在使用节点,所以我不确定这是否与单独的框架有关。也如 ans 中提到的。如果它是单独的帧,那么
Object.prototype.toString.call(date) === '[object Date]'
应该是 false
但就我而言,它是 true
。
开玩笑,当我进行如下测试时......
expect(deletedUser).toMatchObject(
expect.objectContaining({
deleted_by_id: expect.any(Number),
deleted_at: expect.any(Date),
}),
);
expect.any(Date)
调用失败。有可能在幕后的笑话中调用了instanceOf
。我在笑话文档中找不到提到这一点。
有趣的是,当我在执行上面的expect调用之前设置deleted_at时,就像这样
deletedUser.deleted_at
= new Date`,然后测试通过。只是当它从 prisma 数据库中出来时它失败了。
Jest 打印出测试失败,deleted_at 看起来像这样
"deleted_at": Date {}
。当我之前将deleted_at设置为新日期时,它会像这样通过并打印"deleted_at": 2022-08-15T21:56:54.402Z
。然而,当它失败并且笑话打印Date {}
时,如果我用console.log
记录它,它会正常打印日期"deleted_at": 2022-08-15T21:56:54.402Z
我最近改变了测试的设置,我想这与它有关。但谁造成了这次失败呢?原因又是什么呢?谁导致
instanceof Date
变得错误,为什么?
我更改的是,在 jest.config.js 中,我创建了一个用于测试的全局应用程序,并附加了 prisma 客户端,这样我就可以通过
app.prisma
获取它
const app = require('./tests/app');
const prisma = require('./prisma/client');
const request = require('supertest');
app.prisma = prisma;
app.testRequest = request(app);
module.exports = {
testEnvironment: 'node',
globals: {
app,
},
};
我这样做了,所以我只需要在测试套件中使用一个应用程序,这大大加快了我们的测试速度,将时间从大约 130 秒减少到 40 秒。我可以想出一个解决方法来通过此测试,但我担心这可能表明我偶然发现了一个更重要的问题。
我想知道 node、prisma 或 jest 是否在一个 Date 构造函数和另一个 Date 构造函数之间创建单独的上下文。类似于跨帧边界传递对象。但是,我无法确认这一点,如上所述,那么
Object.prototype.toString.call(date) === '[object Date]'
检查应该是 false
。
我注意到我们附加到
Array.prototype
属性的函数也出现了类似的问题。当尝试在测试中访问它时,它不存在。
从这种行为来看,我倾向于这样的想法:jest 在每次测试之前创建某种新的上下文,而在 jest.config.js 中添加的内容在每次测试期间不一定存在。
笑话文档 https://jestjs.io/docs/configuration#globals-object 提到了使用全局变量时,数据在测试之间不持久的情况。有些东西虽然一直存在,但也分不清什么是什么不是,不能依赖。
Prisma 可能使用不同的日期类,其行为与本机
Date
类相同,如下所示:
const RealDate = Date;
const fakeDateToRealDate = new WeakMap();
// The name of this function is important since this is the
// constructor and the name can be accessed using `.constructor.name`.
const FakeDate = function Date(...args) {
fakeDateToRealDate.set(this, new RealDate(...args));
}
// For Object.prototype.toString()
FakeDate.prototype[Symbol.toStringTag] = "Date";
function defineMethod(f) {
Object.defineProperty(FakeDate.prototype, f.name, {
configurable: true,
enumerable: false,
writable: true,
value: f
});
}
defineMethod(function toString(...args) {
return fakeDateToRealDate.get(this).toString(...args);
});
defineMethod(function getTime(...args) {
return fakeDateToRealDate.get(this).getTime(...args);
});
defineMethod(function setTime(...args) {
return fakeDateToRealDate.get(this).setTime(...args);
});
// etc.
const fakeDate = new FakeDate("2022-08-15T21:50:34.344Z");
console.log(typeof fakeDate); // object
console.log(fakeDate.constructor); // [Function: Date]
console.log(fakeDate.constructor.name); // Date
console.log(fakeDate instanceof Date); // false
console.log(fakeDate); // 2022-08-15T21:50:34.344Z
console.log(Object.prototype.toString.call(fakeDate) === '[object Date]'); // true
console.log(Object.prototype.toString.call(fakeDate)); // [object Date]
console.log(new Date(fakeDate)); // 2022-08-15T21:50:34.344Z
console.log(fakeDate.prototype); // undefined
Object.keys(fakeDate).forEach(prop => console.log(prop))
console.log(Object.keys(fakeDate)); // []
console.log(fakeDate.__proto__); // {}
console.log(isNaN(fakeDate)); // false
console.log(fakeDate.valueOf()); // Mon Aug 15 2022 21:56:54 GMT+0000 (Coordinated Universal Time
Prisma 也有可能使用
vm
模块,这类似于在不同框架中运行的代码,如下所示:
const vm = require("vm");
const ctx = {};
vm.createContext(ctx);
const fakeDate = vm.runInContext('new Date("2022-08-15T21:50:34.344Z");', ctx);
console.log(typeof fakeDate); // object
console.log(fakeDate.constructor); // [Function: Date]
console.log(fakeDate.constructor.name); // Date
console.log(fakeDate instanceof Date); // false
console.log(fakeDate); // 2022-08-15T21:50:34.344Z
console.log(Object.prototype.toString.call(fakeDate) === '[object Date]'); // true
console.log(Object.prototype.toString.call(fakeDate)); // [object Date]
console.log(new Date(fakeDate)); // 2022-08-15T21:50:34.344Z
console.log(fakeDate.prototype); // undefined
Object.keys(fakeDate).forEach(prop => console.log(prop))
console.log(Object.keys(fakeDate)); // []
console.log(fakeDate.__proto__); // {}
console.log(isNaN(fakeDate)); // false
console.log(fakeDate.valueOf()); // Mon Aug 15 2022 21:56:54 GMT+0000 (Coordinated Universal Time
不幸的是,我无法确切确认发生了什么,因为我在 Prisma 的代码中没有找到这两件事中的任何一个,尽管我可能搜索到了错误的地方。