使用 Date 对象的组件在不同的时区产生不同的快照

IT技术 javascript reactjs jestjs enzyme
2021-04-01 12:40:45

我正在使用Enzyme酶到 json我的 React 组件进行Jest快照测试。我正在测试一个DateRange组件的浅快照,该组件使用当前范围(例如5/20/2016 - 7/18/2016)和两个DateInput允许选择Date组件呈现显示字段这意味着我的快照包含Date我在DateInputprops 和文本表示中传递给组件s ,它会自行解析。在我的测试中,我使用new Date(1995, 4, 23).

当我在不同的时区运行我的测试时,这会产生不同的快照,因为Date(year, month, ...)构造函数在本地时区创建日期。例如,使用new Date()在我的本地时区和我们的 CI 服务器上运行之间的快照会产生这种差异。

- value={1995-05-22T22:00:00.000Z}
+ value={1995-05-23T00:00:00.000Z}

我尝试从日期中删除时区偏移量,但是快照在显示字段值中有所不同,其中使用了本地时区相关表示。

- value={5/20/2016 - 7/18/2016}
+ value={5/19/2016 - 7/17/2016}

如何让我的测试Date在快照中产生相同的s,而不管它们运行在哪个时区?

6个回答

我为此挣扎了几个小时/几天,只有这对我有用:

1)在你的测试中:

Date.now = jest.fn(() => new Date(Date.UTC(2017, 7, 9, 8)).valueOf())

2)然后TZ在运行测试之前更改环境变量。所以我的 package.json 中的脚本:

  • 仅限 Mac 和 Linux

    "test": "TZ=America/New_York react-scripts test --env=jsdom",
    
  • (视窗)

    "test": "set TZ=America/New_York && react-scripts test --env=jsdom",
    
这是我现在使用的整条线路"test": "TZ=America/New_York jest",这就是全部。谢谢!
2021-05-22 12:40:45
好的,我想通了,并用解决方案编辑了答案。
2021-05-30 12:40:45
这(IMO)应该是公认的答案。具体来说,第 2 步对我来说就足够了。我刚刚TZ在我的测试脚本声明中添加了一个快速环境变量,它就像一个魅力。我什至不必模拟任何 Date 函数。
2021-06-08 12:40:45
你拯救了我的一天!@Devid 对于所有环境(之前安装跨环境包):"test": "cross-env TZ=America/New_York && react-scripts test --env=jsdom"
2021-06-08 12:40:45
指定TZ=...也对我有用!作为参考,timezoneconverter.com/cgi-bin/tzc有一个时区值列表,这些值都应该有效。
2021-06-11 12:40:45

我最终得到了一个由两部分组成的解决方案。

  1. 切勿Date以时区相关的方式在测试中创建对象。如果您不想直接使用时间戳来获得可读的测试代码,请使用Date.UTC,例如

    new Date(Date.UTC(1995, 4, 23))
    
  2. 模拟用于将Dates 转换为显示值的日期格式化程序,以便它返回与时区无关的表示,例如 use Date::toISOString()幸运的是,这对我来说很容易,因为我只需要模拟formatDate本地化module中函数。如果组件以某种方式Date自行s 转换为字符串,则可能会更难

在我到达上述解决方案之前,我试图以某种方式改变快照的创建方式。很难看,因为enzyme-to-json 保存了一个本地副本toISOString(),所以我不得不使用_.cloneDeepWith和修改所有的Dates。无论如何,它对我不起作用,因为我的测试还包含Date从时间戳创建的案例(该组件比我上面描述的要复杂得多)以及这些案例与我在测试中明确创建的日期之间的交互。所以我首先必须确保我所有的日期定义都指的是同一个时区,其余的都紧随其后。


更新(11/3/2017):当我enzyme-to-json最近检查时,我无法找到 的本地保存toISOString(),所以也许这不再是一个问题,它可以被嘲笑。尽管如此,我也无法在历史中找到它,所以也许我只是错误地指出了哪个图书馆做到了。测试风险自负:)

我通过使用做到了这一点timezone-mock,它在内部替换了全局Date对象,这是我能找到的最简单的解决方案。

该软件包支持一些测试时区。

import timezoneMock from 'timezone-mock';

describe('when in PT timezone', () => {
  beforeAll(() => {
    timezoneMock.register('US/Pacific');
  });

  afterAll(() => {
    timezoneMock.unregister();
  });

  // ...

https://www.npmjs.com/package/timezone-mock

我最终通过模拟toLocaleString(或您正在使用的任何 toString 方法)原型来解决这个问题使用sinon我做了:

var toLocaleString;

beforeAll(() => {
    toLocaleString = sinon.stub(Date.prototype, 'toLocaleString', () => 'fake time')
})

afterAll(() => {
    toLocaleString.restore()
})

这样,如果您直接从Date对象生成字符串,您仍然可以。

@morgs32 在撰写本文时,我清楚地记得看到代码就是这样做的。但是现在我在任何地方都找不到它,所以我不能确定它不在某个用作依赖项的库中或类似的东西。
2021-06-11 12:40:45
不幸的是,正如我在回答中所描述的,enzyme-to-json保存并使用 的本地副本Date.toISOString,因此存根不会影响结果。
2021-06-18 12:40:45

如果您使用new Date()构造函数而不是 Date.now,则可以执行以下操作:

const RealDate = Date;

beforeEach(() => {
  // @ts-ignore
  global.Date = class extends RealDate {
    constructor() {
      super();
      return new RealDate("2016");
    }
  };
})
afterEach(() => {
  global.Date = RealDate;
});

如果你在这里,这个问题是必须访问的。