我有一个在“East US”Azure服务器上运行的客户端。一些在开发中可行的代码(在英国服务器上)不在该服务器上(美国东部服务器)。我认为问题是由于我将日期字符串转换为UTC日期时间,但我想为它编写测试以确实证明我已经解决了这个问题。
有没有办法假装我的单元测试在不同的时区运行?
例如,DateTime.Now应该返回美国东部而不是英国的时间。
这可能吗?
答案 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,您将会学到很多东西。