Pergunta

I am running a nice big list of unit tests which are checking various different class gets and sets. I have come across a bit of a bug in that my tests run too fast?!?

As a quick example, the unit tests start by mocking a blog with a comment;

Blog b = new Blog("Comment");

This sets the DateTime added of the new comment in the blog. I am then testing the addition of a comment to the blog;

b.addComment(comment);

At the end of the test I am asserting the addition of the test using the following;

Assert.AreEqual(x, b.Comments.OrderByDescending(x => x.CreatedDate).First().Comment;

As it stands this passes when run in isolation. However when the test is run in a group of tests this will fail because both comments have the exact same DateTime. It's worth noting the the following will pass only because it will be ordered by the default ordering;

Assert.AreEqual(x, b.Comments.OrderBy(x => x.Created).Last().Comment;

My initial thought was how the hell can it fail? The DateTime.Utc has a ticks function which is accurate to like a millionth of a second. after some research I found out that the system date time is only precise to 10-15ms so this is my issue.

My question is, whats the best solution?

I could add Thread.Sleep(20); to every test but if I run 1000 tests that's now 20 seconds? How have other people got round this issue?

Foi útil?

Solução

Your class depends on external resource, which is current system time. Thus you are not testing class in isolation, which gives you problem you faced. You should mock DateTime.Now (some unit-testing frameworks allow mocking static members - e.g. JustMock), or create abstract dependency, which will provide time to your class:

public interface ITimeProvider
{
    DateTime CurrentTime { get; }
}

As Mark Seemann suggests here, you can use static instance of this abstraction to avoid passing this dependency to each class which needs to get current time, but that will make dependency implicit (so its up to you to decide which way to go):

public class TimeProvider : ITimeProvider
{
    private static ITimeProvider _instance = new TimeProvider();

    private TimeProvider() { }

    public static ITimeProvider Instance 
    {
        get { return _instance; }
        set { _instance = value; } // allows you to set mock
    }

    public DateTime CurrentTime
    {
        get { return DateTime.Now; }
    }
}

Usage will be easy

var date = TimeProvider.Instance.CurrentTime;
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top