Search code examples
c#asp.net-corensubstitute.net-5refit

Refit with TestServer and NSubstitute


I am trying to use Refit to test a Rest interface on TestServer, while replacing the DateTime.Now to return custom time.

My refit interface looks something like this:

public interface IMyApi
    {
        DateTime CurentTime();
        [Get("/api/...")]
        Task<ApiResponse<DateTime>> SomeOtherFunction();
    }

and in my implementation, I have

public class MyApi {
  public virtual DateTime CurrentTime { return DateTime.Now; } 
  ...
  public async Task<IActionResult> SomeOtherFunction() { return CurrentTime();  }
}

This works fine as a Unit test

var myMock = Substitute.ForPartsOf<MyApi>();
myMock.Configure().CurrentTime().Returns(..some fixed date..);
myMock.SomeOtherFunction();
// returns the fixed date

However I am missing how to get this to work when I create the twin using Refit and running on TestServer, since the original function keeps getting called:

var myMock = Substitute.ForPartsOf<IMyApi>();
myMock.Configure().CurrentTime().Returns(..some fixed date..);
var myServer = new TestServer(builder: ... );

// how do I connect to the invokation below?
var client = RestService.For<IMyApi>(myServer.CreateClient());
client.SomeOtherFunction();

So question is, how do I have the RestService.For<> created out of the mocked API rather than the original api? Or do you have any other suggestions?


Solution

  • The idea is that we need to replace the controller that is created at run time by the controller factory. The easiest way I have found to do this is:

    1. In "ConfigureServices" of Startup class, tell the framework to store the controller in the services collection. Mine now looks somewhat like this:
            public void ConfigureServices(IServiceCollection services)
            {
                ...
                services.AddControllers().AddControllersAsServices()
                      .AddOtherThings(...)                                                       );
                ...
             }
    
    1. Replace the controller with the mocked controller, somwewhere where you can get to the controller collection (such as in WebHostBuilder's ConfigureTestServices()):
        var toRemove = svc.FirstOrDefault(d => d.ServiceType == typeof(MyApi));
        svc.Remove(toRemove);
    
    
        var c = Substitute.ForPartsOf<MyApi>(new object[] { ...constructor params... });
        svc.AddScoped(controller => c);
        controller.Configure().CurrentTime().Returns(DateTime.Parse("1/1/2001 10:00:00"));