使用 Date 对象的组件在不同的时区产生不同的快照
Components using Date objects produce different snapshots in different timezones
我正在对我的 React 组件使用 Enzyme with enzyme-to-json to do Jest 快照测试。我正在测试 DateRange
组件的浅快照,该组件呈现具有当前范围的显示字段(例如 5/20/2016 - 7/18/2016
)和两个允许选择 Date
值的 DateInput
组件。这意味着我的快照包含我传递给组件的 Date
s 在 DateInput
道具和它自己解析的文本表示中。在我的测试中,我正在使用 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 而不管它们 运行 在哪个时区?
我最终得到了一个由两部分组成的解决方案。
切勿以依赖时区的方式在测试中创建 Date
对象。如果您不想直接使用时间戳来获得可读的测试代码,请使用 Date.UTC
,例如
new Date(Date.UTC(1995, 4, 23))
- 模拟用于将
Date
s 转换为显示值的日期格式化程序,使其 returns 成为独立于时区的表示,例如使用 Date::toISOString()
。 幸运的是,这对我来说很容易,因为我只需要在我的本地化模块中模拟 formatDate
函数。如果组件以某种方式自行将 Date
s 转换为字符串,则可能会更难。
在我找到上述解决方案之前,我试图以某种方式改变快照的创建方式。这很丑陋,因为 enzyme-to-json 保存了 toISOString()
的本地副本,所以我不得不使用 _.cloneDeepWith
并修改所有 Date
。无论如何它对我来说都没有用,因为我的测试还包含 Date
从时间戳创建的案例(该组件比我上面描述的要复杂得多)以及它们与我创建的日期之间的交互明确的测试。所以我首先必须确保我所有的日期定义都指的是同一个时区,然后是其他时区。
更新(11/3/2017):最近查看enzyme-to-json
时,一直没能找到toISOString()
的本地保存,所以可能是这样不再是问题,它可以被嘲笑。不过我也没能在历史上找到它,所以也许我只是错误地注意到是哪个图书馆做的。测试后果自负:)
我最终通过模拟 toLocaleString
(或您正在使用的任何 toString 方法)原型来解决这个问题。使用 sinon
我做了:
var toLocaleString;
beforeAll(() => {
toLocaleString = sinon.stub(Date.prototype, 'toLocaleString', () => 'fake time')
})
afterAll(() => {
toLocaleString.restore()
})
通过这种方式,如果您直接从 Date
对象生成字符串,您仍然可以。
我为 hours/days 苦苦挣扎,只有这个对我有用:
1) 在你的测试中:
Date.now = jest.fn(() => new Date(Date.UTC(2017, 7, 9, 8)).valueOf())
2) 然后在 运行 测试之前更改 TZ
env var。
所以我的 package.json 中的脚本:
(Mac & Linux 只有)
"test": "TZ=America/New_York react-scripts test --env=jsdom",
(Windows)
"test": "set TZ=America/New_York && react-scripts test --env=jsdom",
如果您使用的是 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;
});
This issue 如果您来这里,一定要去看看。
我是通过使用 timezone-mock
做到这一点的,它在内部替换了全局 Date
对象,这是我能找到的最简单的解决方案。
该软件包支持几个测试时区。
import timezoneMock from 'timezone-mock';
describe('when in PT timezone', () => {
beforeAll(() => {
timezoneMock.register('US/Pacific');
});
afterAll(() => {
timezoneMock.unregister();
});
// ...
将 TZ=UTC
添加到我的 .env
文件解决了我的问题。
一个简单的事实可以使它变得容易。
只需使用:
new Date('some string').
这将始终给出无效日期,无论是哪台机器,它始终是无效日期。
干杯。
2020 年适合我的解决方案
beforeEach(() => {
jest.useFakeTimers('modern');
jest.setSystemTime(Date.parse(FIXED_SYSTEM_TIME));
});
afterEach(() => {
jest.useRealTimers();
});
我正在对我的 React 组件使用 Enzyme with enzyme-to-json to do Jest 快照测试。我正在测试 DateRange
组件的浅快照,该组件呈现具有当前范围的显示字段(例如 5/20/2016 - 7/18/2016
)和两个允许选择 Date
值的 DateInput
组件。这意味着我的快照包含我传递给组件的 Date
s 在 DateInput
道具和它自己解析的文本表示中。在我的测试中,我正在使用 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 而不管它们 运行 在哪个时区?
我最终得到了一个由两部分组成的解决方案。
切勿以依赖时区的方式在测试中创建
Date
对象。如果您不想直接使用时间戳来获得可读的测试代码,请使用Date.UTC
,例如new Date(Date.UTC(1995, 4, 23))
- 模拟用于将
Date
s 转换为显示值的日期格式化程序,使其 returns 成为独立于时区的表示,例如使用Date::toISOString()
。 幸运的是,这对我来说很容易,因为我只需要在我的本地化模块中模拟formatDate
函数。如果组件以某种方式自行将Date
s 转换为字符串,则可能会更难。
在我找到上述解决方案之前,我试图以某种方式改变快照的创建方式。这很丑陋,因为 enzyme-to-json 保存了 toISOString()
的本地副本,所以我不得不使用 _.cloneDeepWith
并修改所有 Date
。无论如何它对我来说都没有用,因为我的测试还包含 Date
从时间戳创建的案例(该组件比我上面描述的要复杂得多)以及它们与我创建的日期之间的交互明确的测试。所以我首先必须确保我所有的日期定义都指的是同一个时区,然后是其他时区。
更新(11/3/2017):最近查看enzyme-to-json
时,一直没能找到toISOString()
的本地保存,所以可能是这样不再是问题,它可以被嘲笑。不过我也没能在历史上找到它,所以也许我只是错误地注意到是哪个图书馆做的。测试后果自负:)
我最终通过模拟 toLocaleString
(或您正在使用的任何 toString 方法)原型来解决这个问题。使用 sinon
我做了:
var toLocaleString;
beforeAll(() => {
toLocaleString = sinon.stub(Date.prototype, 'toLocaleString', () => 'fake time')
})
afterAll(() => {
toLocaleString.restore()
})
通过这种方式,如果您直接从 Date
对象生成字符串,您仍然可以。
我为 hours/days 苦苦挣扎,只有这个对我有用:
1) 在你的测试中:
Date.now = jest.fn(() => new Date(Date.UTC(2017, 7, 9, 8)).valueOf())
2) 然后在 运行 测试之前更改 TZ
env var。
所以我的 package.json 中的脚本:
(Mac & Linux 只有)
"test": "TZ=America/New_York react-scripts test --env=jsdom",
(Windows)
"test": "set TZ=America/New_York && react-scripts test --env=jsdom",
如果您使用的是 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;
});
This issue 如果您来这里,一定要去看看。
我是通过使用 timezone-mock
做到这一点的,它在内部替换了全局 Date
对象,这是我能找到的最简单的解决方案。
该软件包支持几个测试时区。
import timezoneMock from 'timezone-mock';
describe('when in PT timezone', () => {
beforeAll(() => {
timezoneMock.register('US/Pacific');
});
afterAll(() => {
timezoneMock.unregister();
});
// ...
将 TZ=UTC
添加到我的 .env
文件解决了我的问题。
一个简单的事实可以使它变得容易。
只需使用:
new Date('some string').
这将始终给出无效日期,无论是哪台机器,它始终是无效日期。
干杯。
2020 年适合我的解决方案
beforeEach(() => {
jest.useFakeTimers('modern');
jest.setSystemTime(Date.parse(FIXED_SYSTEM_TIME));
});
afterEach(() => {
jest.useRealTimers();
});