Ваша проблема может быть вызвана несколькими модульными тестами, которые работают с static SystemTime
. Если бы вы, например, иметь что-то вроде:
тест Unit 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
не будет (обязательно) иметь ожидаемое значение DateTime
, которое вы установили в SystemTime.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();
}
}
Для фактической реализации вашего Service
вы могли новые вверх, как (или используйте контейнер МОК):
Service service = new Service(new SystemDateTime());
Для тестирования вы можете либо использовать насмешливые рамки, или ваш Пробный класс как таковые:
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);
}
ли 'UpdateLastAccessedTimestamp' делать какие-либо манипуляции с даты, или, возможно, тянуть время от внешнего источника? Можете ли вы увидеть причину сбоя ('values') между« ожидаемыми »и« фактическими »? Что такое this._testEntry? ваша часть 'Act', похоже, не проходит в вашем ожидаемомTimestamp – Kritner
@ Kritner благодарит за ваш ответ. Я обновил свой вопрос в ответ на ваши отзывы. Обратите внимание, что вызовы объекта _unitOfWork не выполняют никаких манипуляций с полем LastAccessedOn. – aw1975
Просто любопытно ... если вместо 'SystemTime.Now =() => expectedTimestamp;' вы сделали 'SystemTime.Now =() => новый DateTime (2000, 1, 1);' - что бы ваши результаты вышли в виде? Является ли ваш ожидаемый 2000, 1, 1? Является ли ваш фактический 2000, 1, 1? Или это возможно 2016, 2, 24? Это, по крайней мере, надеюсь, поможет сузить проблему. – Kritner