ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

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

2019-11-18 23:05:30  阅读:274  来源: 互联网

标签:unit-testing xunit nunit datetime c


为了帮助进行单元测试,我们将DateTime类包装在委托中,以便可以在单元测试中覆盖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是通过上述委托包装器模拟的,因此我很难思考为什么它会失败的原因.我已经验证了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
}

解决方法:

您的问题可能是由于使用静态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上设置的预期DateTime值.Now=()=> ExpectedTimestamp ;,因为另一个测试已经覆盖了您从单元测试1中提供的功能.

这就是为什么我认为您最好将DateTime作为参数传递,或者使用可注入的datetime更好:

/// <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();
        }           
    }

对于服务的实际实现,您可以像(或使用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);
}   

标签:unit-testing,xunit,nunit,datetime,c
来源: https://codeday.me/bug/20191118/2031664.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有