Search code examples
c#.net-coreintegration-testingasp.net-core-webapixunit

Integration testing calls with simulated delays


I'm testing an asp.net Core 2.2 WebApi backend which uses EF Core for data persistance.

The solution is arquitected using CLEAN arquitecture with a repository/service pattern making every component individually testable.

My integration tests (using XUnit and EF Core in-memory database) need to test the following cases:

  1. User makes a request and instantly makes another identical request.
  2. User makes a request and 15 seconds later, makes another identical request.
  3. User makes a request and 30 seconds later, makes another identical request.

Each of these cases have about 5 variants each. Right now, I'm using a Thread.Sleep(x) call in between each of the calls to simulate this delay. This, of course, means that my integration tests take ages to complete. The delays are used in order for the repository layer to save a DateTime.UtcNow value when each of the calls is made; the value of which is used by the Service layer to perform some business-logic decisions.

What I'm wondering, is whether there is a way of 'simulating' these delays using the testing framework, removing the need for the tests to actually wait this time, and tricking the repository layer into using a DateTime.UtcNow value which is incremented by x.

Thanks you!


Solution

  • Abstract the DateTime access behind some interface

    public interface IDateTime { 
        DateTime UtcNow { get; }
    
        //...other members as needed. eg: DateTime Now { get; }
    } 
    

    That can be mocked to behave as desired when testing.

    The production implementation would simple wrap the actual DateTime

    public class DefaultClock : IDateTime {
        public DateTime UtcNow => DateTime.UtcNow;
    
        //...
    }
    

    and registered with your DI container

    service.AddSingleton<IDateTime, DefaultClock>();
    

    When testing, the abstraction can be mocked as needed;

    //Arrange
    DateTime time;
    DatTime.TryParse("2009-05-01 12:00:00", out time);
    
    var mock = new Mock<IDateTime>(); //USING MOQ
    
    mock.SetupSequence(_ => _.UtcNow)
        .Returns(time);                 //first call
        .Returns(time.AddSeconds(15))   //second call
        .Returns(time.AddSeconds(30));  //third call
    
    IDateTime clock = mock.Object;
    
    //...inject clock into subject under test