模拟您正在运行单元测试的国家/地区时区

时间:2017-06-07 13:00:51

标签: c# unit-testing

我有一个在“East US”Azure服务器上运行的客户端。一些在开发中可行的代码(在英国服务器上)不在该服务器上(美国东部服务器)。我认为问题是由于我将日期字符串转换为UTC日期时间,但我想为它编写测试以确实证明我已经解决了这个问题。

有没有办法假装我的单元测试在不同的时区运行?

例如,DateTime.Now应该返回美国东部而不是英国的时间。

这可能吗?

2 个答案:

答案 0 :(得分:1)

我怀疑你需要设置CurrentUICulture。

https://stackoverflow.com/a/7000587/34092向您展示了如何执行此操作。

这将改变字符串转换为日期的方式,反之亦然。

答案 1 :(得分:1)

是的,您可以伪造运行单元测试的时区。

这是一个简单的类,可将 local 时区更改为构造函数中提供的timeZoneInfo,并在处理后重置原始的 local 时区。

using System;
using ReflectionMagic;

namespace TestProject
{
    public class FakeLocalTimeZone : IDisposable
    {
        private readonly TimeZoneInfo _actualLocalTimeZoneInfo;

        private static void SetLocalTimeZone(TimeZoneInfo timeZoneInfo)
        {
            typeof(TimeZoneInfo).AsDynamicType().s_cachedData._localTimeZone = timeZoneInfo;
        }

        public FakeLocalTimeZone(TimeZoneInfo timeZoneInfo)
        {
            _actualLocalTimeZoneInfo = TimeZoneInfo.Local;
            SetLocalTimeZone(timeZoneInfo);
        }

        public void Dispose()
        {
            SetLocalTimeZone(_actualLocalTimeZoneInfo);
        }
    }
}

FakeLocalTimeZone类正在使用ReflectionMagic访问私有字段(即protected by a lock),因此不要在生产代码中使用它,仅在单元测试中使用!

这里是使用方式:

using System;
using Xunit;

namespace TestProject
{
    public class UnitTest
    {
        [Fact]
        public void TestFakeLocalTimeZone()
        {
            using (new FakeLocalTimeZone(TimeZoneInfo.FindSystemTimeZoneById("US/Eastern")))
            {
                // In this scope, the local time zone is US/Eastern
                // Here, DateTime.Now returns 2020-09-02T02:58:46
                Assert.Equal("US/Eastern", TimeZoneInfo.Local.Id);
                Assert.Equal(TimeSpan.FromHours(-5), TimeZoneInfo.Local.BaseUtcOffset);
            }
            // In this scope (i.e. after the FakeLocalTimeZone is disposed) the local time zone is the one of the computer.
            // It is not safe to assume anything about which is the local time zone here.
            // Here, DateTime.Now returns 2020-09-02T08:58:46 (my computer is in the Europe/Zurich time zone)
        }
    }
}

这回答了如何弄清我的单元测试在不同时区运行的事实

现在,如注释中的user3292642所建议,一个更好的设计是使用一个接口,而不是直接在代码中调用DateTime.Now,以便您可以在自己的代码中提供伪造的 now 单元测试。

一个更好的选择是使用Noda Time而不是DateTime类型。 Noda Time具有所有可以正确处理日期和时间的抽象和类型。即使您不打算使用它,也应该阅读其user guide,您将会学到很多东西。