gpt4 book ai didi

c# - 单元测试仅在构建服务器上运行时失败

转载 作者:行者123 更新时间:2023-11-30 16:03:39 25 4
gpt4 key购买 nike

为了帮助进行单元测试,我们将 DateTime 类包装在一个委托(delegate)中,以便可以在单元测试中覆盖 DateTime.Now

public static class SystemTime
{
#region Static Fields

public static Func<DateTime> Now = () => DateTime.Now;

#endregion
}

这是它在 xunit 单元测试中的使用示例:

[Fact]
public void it_should_update_the_last_accessed_timestamp_on_an_entry()
{
// Arrange
var service = this.CreateClassUnderTest();

var expectedTimestamp = SystemTime.Now();
SystemTime.Now = () => expectedTimestamp;

// Act
service.UpdateLastAccessedTimestamp(this._testEntry);

// Assert
Assert.Equal(expectedTimestamp, this._testEntry.LastAccessedOn);
}

测试在本地运行良好,但在我们的构建服务器上失败,因为 Assert 语句中的日期时间不同。

鉴于 DateTime 是通过上述委托(delegate)包装器模拟的,我正在努力想出它失败的原因。我已验证 UpdateLastAccessedTimestamp 方法的实现没有问题,并且测试在本地运行时通过。

不幸的是,我无法在我们的构建服务器上调试它。知道为什么只有在构建服务器上运行时它才会失败吗?

注意UpdateLastAccessedTimestamp的实现如下:

public void UpdateLastAccessedTimestamp(Entry entry)
{
entry.LastAccessedOn = SystemTime.Now();
this._unitOfWork.Entries.Update(entry);
this._unitOfWork.Save();
}

Entry 类只是一个简单的 POCO 类,它有许多字段,包括 LastAccessedOn 字段:

public class Entry
{
public DateTime LastAccessedOn { get; set; }

//other fields that have left out to keep the example concise
}

最佳答案

您的问题可能是由于多个单元测试使用了 static SystemTime。例如,如果你有类似的东西:

单元测试 1

[Fact]
public void it_should_update_the_last_accessed_timestamp_on_an_entry()
{
// Arrange
var service = this.CreateClassUnderTest();

var expectedTimestamp = SystemTime.Now();
SystemTime.Now = () => expectedTimestamp;

// Act
service.UpdateLastAccessedTimestamp(this._testEntry);

// Assert
Assert.Equal(expectedTimestamp, this._testEntry.LastAccessedOn);
}

public void UpdateLastAccessedTimestamp(Entry entry)
{
entry.LastAccessedOn = SystemTime.Now();
this._unitOfWork.Entries.Update(entry);
this._unitOfWork.Save();
}

单元测试2

[Fact]
public void do_something_different
{
SystemTime.Now = () => DateTime.Now;
}

所以让我们假设单元测试 2(它是完整的)在单元测试 1 的行之间触发:

SystemTime.Now = () => expectedTimestamp;

// Unit test 2 starts execution here

// Act
service.UpdateLastAccessedTimestamp(this._testEntry);

如果发生这种情况,那么您的 UpdateLastAccessedTimestamp 将不会(必然)具有您在 SystemTime.Now = () 中设置的预期 DateTime 值=> expectedTimestamp;,因为另一个测试已经覆盖了单元测试 1 中提供的功能。

这就是为什么我认为您最好将 DateTime 作为参数传递,或者像这样使用可注入(inject)的日期时间:

/// <summary>
/// Injectable DateTime interface, should be used to ensure date specific logic is more testable
/// </summary>
public interface IDateTime
{
/// <summary>
/// Current Data time
/// </summary>
DateTime Now { get; }
}

/// <summary>
/// DateTime.Now - use as concrete implementation
/// </summary>
public class SystemDateTime : IDateTime
{
/// <summary>
/// DateTime.Now
/// </summary>
public DateTime Now { get { return DateTime.Now; } }
}

/// <summary>
/// DateTime - used to unit testing functionality around DateTime.Now (externalizes dependency on DateTime.Now
/// </summary>
public class MockSystemDateTime : IDateTime
{
private readonly DateTime MockedDateTime;

/// <summary>
/// Take in mocked DateTime for use in testing
/// </summary>
/// <param name="mockedDateTime"></param>
public MockSystemDateTime(DateTime mockedDateTime)
{
this.MockedDateTime = mockedDateTime;
}

/// <summary>
/// DateTime passed from constructor
/// </summary>
public DateTime Now { get { return MockedDateTime; } }
}

使用这种情况,您的服务类可能会从(类似于)这样改变:

public class Service
{
public Service() { }

public void UpdateLastAccessedTimestamp(Entry entry)
{
entry.LastAccessedOn = SystemTime.Now();
this._unitOfWork.Entries.Update(entry);
this._unitOfWork.Save();
}
}

对此:

    public class Service
{

private readonly IDateTime _iDateTime;

public Service(IDateTime iDateTime)
{
if (iDateTime == null)
throw new ArgumentNullException(nameof(iDateTime));
// or you could new up the concrete implementation of SystemDateTime if not provided

_iDateTime = iDateTime;
}

public void UpdateLastAccessedTimestamp(Entry entry)
{
entry.LastAccessedOn = _iDateTime.Now;
this._unitOfWork.Entries.Update(entry);
this._unitOfWork.Save();
}
}

对于您的 Service 的实际实现,您可以像这样新建(或使用 IOC 容器):

Service service = new Service(new SystemDateTime());

对于测试,您可以使用模拟框架,或者您的 Mock 类:

Service service = new Service(new MockDateTime(new DateTime(2000, 1, 1)));

你的单元测试可以变成:

[Fact]
public void it_should_update_the_last_accessed_timestamp_on_an_entry()
{
// Arrange
MockDateTime mockDateTime = new MockDateTime(new DateTime 2000, 1, 1);
var service = this.CreateClassUnderTest(mockDateTime);

// Act
service.UpdateLastAccessedTimestamp(this._testEntry);

// Assert
Assert.Equal(mockDateTime.Now, this._testEntry.LastAccessedOn);
}

关于c# - 单元测试仅在构建服务器上运行时失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36202924/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com