如何在我的 Jest 配置中设置时区?

Posted

技术标签:

【中文标题】如何在我的 Jest 配置中设置时区?【英文标题】:How do I set a timezone in my Jest config? 【发布时间】:2019-10-09 05:04:11 【问题描述】:
✗ npx jest --version
24.5.0

得到了一组时区敏感的笑话测试。我们通常使用 npm 脚本运行它们:"jest": "TZ=utc jest"

将 TZ 设置为 utc 我在快照中得到如下值:

modificationDate="2019-01-08T00:00:00.000Z" 

没有它我得到:

modificationDate="2019-01-08T08:00:00.000Z"

有没有办法在我的 jest 配置中设置它,这样我就可以在命令行中运行 npx jest 而无需通过 NPM 脚本? config docs 中没有关于此的内容。

我尝试将这两个添加到我的 jest.config.js 中。两者都不起作用:

  TZ: 'utc',

  globals: 
    TZ: 'utc',
  ,

当然,解决它似乎微不足道,但我很惊讶 Jest 没有办法为测试配置它。

【问题讨论】:

【参考方案1】:

这不适用于节点 17.0.1 之前的窗口 - 请参阅 https://github.com/nodejs/node/issues/4230


process.env.TZ = 'UTC'; 的问题在于,如果在此行之前运行某些内容并使用Date,则该值将缓存在Date 中。因此process.env 一般不适合设置时区。见https://github.com/nodejs/node/issues/3449

因此,更好的方法是使用实​​际的环境变量,但对于测试,这将起作用:

1.将此添加到您的 package.json 中

  "jest": 
     ...
     // depending on your paths it can also be './global-setup.js' 
    "globalSetup": "../global-setup.js"
  

2。将此文件放在 package.json 之外 为 global-setup.js

module.exports = async () => 
    process.env.TZ = 'UTC';
;

3.可选:添加确保 UTC 执行的测试

describe('Timezones', () => 
    it('should always be UTC', () => 
        expect(new Date().getTimezoneOffset()).toBe(0);
    );
);

普通的setupFiles 对我来说不起作用,因为它们运行得太晚了(开玩笑:^23.5.0)。所以强制使用 globalSetup 文件。

【讨论】:

@OliverWatkins,能否通过添加一些控制台日志来验证是否调用了 global-setup.js? 我试过 Jest 24.8.0 。我将控制台放在任何地方,它们都被调用(即也在异步内部 - 行 process.env.TZ = 'UTC';)。我的时区偏移量是 -120,而不是零。 @Can 它对我也不起作用。记录process.env.TZ 给了我UTC。你的操作系统是什么?我的是Win10。 此解决方案在我的 Mac 上运行良好,但似乎不适用于我的 Windows 10 机器。我的 global-setup.js 肯定被调用并且有 ``` process.env.TZ = 'UTC'; console.log('JEST SETUP TZ OFFSET: ' + new Date().getTimezoneOffset()) ``` 我的测试有 ``` console.log('IN TEST TZ OFFSET' + new Date().getTimezoneOffset()) ; console.log('IN TEST TZ' + process.env.TZ);期望(新日期().getTimezoneOffset()).toBe(0); ``` 的输出是 ` JEST SETUP TZ OFFSET: 0 IN TEST TZ OFFSET-60 IN TEST TZ UTC Expected: 0 Received: -60 ``` @Can 看起来已修复“我可以确认此问题已在使用 Windows 10 的节点版本 17.0.1 中得到解决”——该 github 票证中的最后一条评论【参考方案2】:

如果您使用 npm 脚本运行测试,即:npm run test,您可以像这样传递时区:

  "scripts": 
    "test": "TZ=UTC jest"
  ,

我个人也觉得,在远程 CI 服务器上调试问题时,这(与 process.env 方法相比)更清晰,更容易识别时区。

【讨论】:

如果我在没有 npm 脚本的情况下通过命令行运行,我必须记住每次都这样做——接受的答案更好 是的。虽然我建议转向使用 npm 脚本。更容易跟踪您和其他人应该如何运行和维护您的应用程序。 IE:npm run test 将为我拥有的每个节点项目运行测试。您无需彻底阅读自述文件(希望是最新的)即可了解预期内容或该应用程序有哪些怪癖。 错误的决定。这样,您将依赖于使用自定义脚本命令运行测试。请改用全局设置。 不同意。您应该只使用一个命令运行自动化测试,最好是您和您的 CI 控制的命令,例如 npm 脚本命令。 @jcollum 如果您对队友的测试在与您的计算机不同的时区运行感到满意,那就去吧。弄清楚为什么你的测试通过而他们的测试失败了:)如果你有一个特定的组件需要使用本地时区,请在该特定测试中而不是全局模拟它。【参考方案3】:

我刚刚遇到了同样的问题,我可以通过将process.env.TZ = 'your/timezone'; 添加到我的jest.config.js 来解决它。

也许这对你的情况也有帮助:)

process.env.TZ = 'UTC';

module.exports = 
  ...
;

【讨论】:

这很遗憾是错误的。请参阅github.com/nodejs/node/issues/3449“关闭,设置 process.env.TZ 不是设置默认时区的正确方法。”使用`expect(new Date().getTimezoneOffset()).toBe(0);`进行验证 @CanK。这很奇怪......我刚刚更新到 Angular 8 并更改了一些依赖项,现在我的测试不再工作了......如果我找到另一个解决方案,我会告诉你。 @thegnuu 是的,我做到了,由于内容的原因,我会将其放入答案中。 @CanK。我意识到在我的情况下,只是因为配置路径的更改导致我的解决方案停止工作,但是您的解决方案也能正常工作,所以我切换到这个,似乎是设置时区的更好方法!谢谢! 这应该是公认的答案。特别是对于我们这些使用 monorepos 的人来说,这是迄今为止最简单的解决方案【参考方案4】:

更新:如果您想动态更改 TZ,这不会产生确定性结果,但如果您只需要一个 TZ,它将起作用。它可能是在脚本级别指定的替代方法,但我认为这将是更好的答案。

问题在于,通过在运行时设置 process.env.TZ,它会在常规 Jest 测试运行期间在测试中产生不确定性行为(副作用)。如果您使用连续运行测试的--runInBand,它可能可以工作,但我不会指望它。

我还在 Node 中发现了一个关于动态时区的旧存档问题,它看起来像 dynamically adjusting it won't work in general。

相反,在启动 jest 之前,我可能会得到多个脚本,每个脚本都设置为 TZ


对于我的用例,我实际上想针对特定的基于日期的边缘情况在不同的时区运行测试。有时用户会遇到基于时区的错误,我们希望在我们的测试套件中轻松解决这一问题。

默认情况下,我们使用此处建议的答案之一运行项目中的所有测试,方法是在 npm 脚本中设置 TZ=UTC(例如 TZ=UTC npm run jest。这会在 UTC 时区下运行所有​​测试。

然后,我们利用 testEnvironment 配置,该配置可以使用 JSDoc pragma @jest-environment 在测试套件级别设置。使用这个自定义测试环境,我们可以使用像@timezone 这样的“自定义文档块编译指示”来读取套件所需的时区。这可以实现每个测试套件的时区自定义,它不如每个测试理想,但对于我们的目的来说已经足够了。

jsdom-with-timezone.js

const JSDOMEnvironment = require('jest-environment-jsdom');

/**
 * Timezone-aware jsdom Jest environment. Supports `@timezone` JSDoc 
 * pragma within test suites to set timezone.
 *
 * You'd make another copy of this extending the Node environment, 
 * if needed for Node server environment-based tests.
 */
module.exports = class TimezoneAwareJSDOMEnvironment extends JSDOMEnvironment 

  constructor(config, context) 

    // Allow test suites to change timezone, even if TZ is passed in a script.
    // Falls back to existing TZ environment variable or UTC if no timezone is specified.
    // IMPORTANT: This must happen before super(config) is called, otherwise
    // it doesn't work.
    process.env.TZ = context.docblockPragmas.timezone || process.env.TZ || 'UTC';

    super(config);
  
;

tz-eastern.test.js

/**
 * @timezone America/New_York
 */

describe('timezone: eastern', () => 
  it('should be America/New_York timezone', () => 
    expect(process.env.TZ).toBe('America/New_York');
    expect(new Date().getTimezoneOffset()).toBe(300 /* 5 hours */);
  );
);

jest.config.js

module.exports = 
  "testEnvironment": "<rootDir>/jsdom-with-timezone.js"

将此方法与jest.useFakeTimers('modern');jest.setSystemTime() 一起使用足以进行更强大的日期测试,所以我想我会分享这种方法,让其他人从中受益!由于 pragma 处理是自定义的,因此您可以根据自己的用例以任何您喜欢的方式对其进行自定义。

来源:

开玩笑testEnvironment docs 开玩笑的测试DocblockPragmasEnvironment example

【讨论】:

实际上,跟进(我编辑了答案)。这在正常运行 Jest 时会产生不确定的行为(即并行运行多个套件)。问题是 process.env.TZ 在测试中流血,因为它不是沙箱的一部分。看起来 Node.js 在运行时不支持动态 TZ :( github.com/nodejs/node-v0.x-archive/issues/3286【参考方案5】:

直到最近,我还使用以下方法来模拟处于不同时区:

  beforeEach(() => 
    // Temporarily allow us to alter timezone calculation for testing
    /*eslint no-extend-native: "off"*/
    Date.prototype.getTimezoneOffset = jest.fn(() => 73);
  );

  afterEach(() => 
    jest.resetAllMocks();
  );

这并没有将测试代码放置在特定的时区,而是确保正确进行了任何时区偏移计算。例如:

new Date("2010-10-01") 将比new Date("2010-10-01T00:00:00") 早 73 分钟,前者相当于 new Date("2010-10-01T00:00:00Z")(UTC 时区),后者位于“本地时区”

我说“直到最近”,因为最近对 date-fns 的更新似乎不再有效

【讨论】:

以上是关于如何在我的 Jest 配置中设置时区?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Amazon EC2 中设置时区?

如何在流明 5.2 中设置时区?

如何在此 Jest 测试中触发窗口加载事件?

如何在 Selenium Chromedriver 中设置时区?

如何在 Crashlytics 中设置时区?

如何在 Yaml 日期字符串中设置时区?