Jim*_*988 7 c# timezone unit-testing
我有一个在“美国东部”Azure 服务器上运行的客户端。在开发中(在英国服务器上)运行良好的一些代码不在该服务器(美国东部服务器)上。我相信这个问题是由于我将日期字符串转换为 UTC 日期时间,但我想为它编写一个测试来证明我已经解决了这个问题。
有没有办法伪造我的单元测试在不同时区运行的事实?
例如,DateTime.Now 应该返回美国东部而不是英国的时间。
这可能吗?
0xc*_*ced 18
是的,您可以伪造运行单元测试的时区。
这是一个简单的类,它将本地时区更改为timeZoneInfo
构造函数中提供的时区,并在处置时重置原始本地时区。
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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
该类FakeLocalTimeZone
使用ReflectionMagic访问私有字段(受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)
}
}
}
Run Code Online (Sandbox Code Playgroud)
这回答了如何伪造我的单元测试在不同时区运行的事实。
现在,正如 user3292642 在评论中建议的那样,更好的设计是使用接口而不是DateTime.Now
直接在代码中调用,以便您现在可以在单元测试中提供假的。
更好的选择是使用Noda Time而不是DateTime
类型。Noda Time 拥有正确处理日期和时间的所有抽象和类型。即使您不打算使用它,您也应该阅读它的用户指南,您会学到很多东西。
小智 6
TimeZoneInfo.ClearCachedData()
使用的简化版本Dispose()
:
public class LocalTimeZoneInfoMocker : IDisposable
{
public LocalTimeZoneInfoMocker(TimeZoneInfo mockTimeZoneInfo)
{
var info = typeof(TimeZoneInfo).GetField("s_cachedData", BindingFlags.NonPublic | BindingFlags.Static);
var cachedData = info.GetValue(null);
var field = cachedData.GetType().GetField("_localTimeZone",
BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Instance);
field.SetValue(cachedData, mockTimeZoneInfo);
}
public void Dispose()
{
TimeZoneInfo.ClearCachedData();
}
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
3583 次 |
最近记录: |