为什么 date instanceof Date 返回 false,而所有其他日期检查都返回 true?

问题描述 投票:0回答:2

使用:Node、Prisma 和 Jest。

我有一个值,除了

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));

我查看了以下资源来研究这个问题。

  1. 如何检查一个对象是否是日期?
  2. https://jestjs.io/docs/expect#expectobjectcontainingobject
  3. 获取对象类型的名称
  4. 在 JavaScript 中检测“无效日期”日期实例
  5. 当 myarray 在框架中时,为什么 myarray instanceof Array 和 myarray.constructor === Array 都是 false?
  6. http://perfectionkills.com/instanceof-considered-harmful-or-how-to-write-a-robust-isarray/
  7. https://groups.google.com/g/comp.lang.javascript/c/XTWYCOwC96I/m/70rNoQ3L-xoJ
  8. 如何知道字符串值是Javascript中Date的实例?

使用这些资源,我使用上述检查来检查

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 提到了使用全局变量时,数据在测试之间不持久的情况。有些东西虽然一直存在,但也分不清什么是什么不是,不能依赖。

javascript node.js jestjs prisma
2个回答
2
投票

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 的代码中没有找到这两件事中的任何一个,尽管我可能搜索到了错误的地方。


1
投票

我不知道

app.prisma
来自哪里,但是单上下文环境可能会有所帮助。

@quramy/jest-prisma 解释了类似的问题以及如何解决它。

© www.soinside.com 2019 - 2024. All rights reserved.