我正在尝试使用
Pose在单元测试中模拟
DateTime.UtcNow
,但似乎在 .NET 6 中不起作用。
代码如下所示:
// Arrange
...
var created = DateTime.UtcNow;
var expectedRequest = new ExpectedRequest()
{
Created = created
...
};
var shim = Shim.Replace(() => DateTime.UtcNow).With(() => created);
PoseContext.Isolate(() =>
{
// Act
var mappedRequest = request.ToExpectedRequest();
// Assert
mappedRequest.Should().BeEquivalentTo(expectedRequest);
}, shim);
...
问题是,当达到
PoseContext.Isolate(()
时,单元测试会卡住,当我尝试停止单元测试时,Visual Studio 会冻结并自行重新启动。
还有其他类似于Pose的选项或库吗?
我知道最常见的方法是引入应用程序逻辑将使用的接口,而不是直接使用
DateTime.UtcNow
属性,但我不想为此注入新服务,并且我正在尝试在不修改实现的情况下找到解决方案。
编辑: 有多种方法可以“解决”
DateTime
的这个模拟问题,可以在这里或这里找到,但我选择环境上下文方法。
单元测试现在看起来像这样:
// Arrange
...
using var context = new DateTimeProviderContext(DateTime.UtcNow);
var expectedRequest = new ExpectedRequest()
{
Created = DateTimeProvider.UtcNow
...
};
// Act
var mappedRequest = request.ToExpectedRequest();
// Assert
mappedRequest.Should().BeEquivalentTo(expectedRequest);
...
在实现中唯一要做的就是使用
Created = DateTimeProvider.UtcNow
而不是 Created = DateTime.UtcNow
。
我将在这里添加这两个类
DateTimeProvider
和 DateTimeProviderContext
,以防有人需要它们。
public static class DateTimeProvider
{
public static DateTime Now
=> DateTimeProviderContext.Current == null
? DateTime.Now
: DateTimeProviderContext.Current.ContextDateTime;
public static DateTime UtcNow => Now.ToUniversalTime();
}
public class DateTimeProviderContext : IDisposable
{
internal DateTime ContextDateTime;
private static readonly ThreadLocal<Stack> _threadScopeStack = new(() => new Stack());
private bool _isDisposed = false;
public DateTimeProviderContext(DateTime contextDateTime)
{
ContextDateTime = contextDateTime;
_threadScopeStack.Value?.Push(this);
}
public static DateTimeProviderContext? Current
{
get
{
if (_threadScopeStack.Value?.Count == 0)
{
return null;
}
else
{
return _threadScopeStack.Value?.Peek() as DateTimeProviderContext;
}
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!_isDisposed && disposing)
{
_threadScopeStack.Value?.Pop();
}
_isDisposed = true;
}
}
我知道这个问题已经有将近两年了,但是我遇到了同样的问题并找到了一个解决方案,这对我有用。
使用 Pose 时,
Assert
- 不应将语句隔离在 PoseContext
内。
用
Pose假装
DateTime
看起来像这样:
// Arrange
var fixture = new ClassUnderTest();
var timeStamp = new DateTime(2024, 4, 15, 15, 16, 17, 745, DateTimeKind.Utc);
var expected = "someExpectedValue";
var actual = string.Empty;
// Act
PoseContext.Isolate(() =>
{
actual = fixture.DoSomething();
}, dateTimeShim);
// Assert
Assert.AreEqual(expected, result);
注意
PoseContext.Isolate
内部如何仅调用被测试的方法。
在您的情况下,测试将如下所示:
// Arrange
...
var created = DateTime.UtcNow;
var expectedRequest = new ExpectedRequest()
{
Created = created
...
};
var shim = Shim.Replace(() => DateTime.UtcNow).With(() => created);
ExpectedRequest mappedRequest = null;
PoseContext.Isolate(() =>
{
// Act
mappedRequest = request.ToExpectedRequest();
}, shim);
// Assert
mappedRequest.Should().BeEquivalentTo(expectedRequest);
...