Search code examples
c#asp.net-coredependency-injectiontaskautofixture

Delay Returns with NSubstitute


I have an interface IDiscosClient, for testing/demo purposes while I'm developing the app, I want a mock to return a new model when the .GetSingle<T>() method is called with a random delay of between 1 and 5 seconds. This is mostly so I can see that all of my various loading spinner components and whatnot work.

So, I thought I'd be able to do something like this:

Fixture fixture = new();
fixture.Customize(new DiscosModelFixtureCustomizationNoLinks());

builder.Services.AddTransient(_ =>
                              {
                                  IDiscosClient client = Substitute.For<IDiscosClient>();
                                  DiscosObject  obj    = fixture.Create<DiscosObject>();
                                  client.GetSingle<DiscosObject>(Arg.Any<string>()).Returns(Task.Delay(Random.Shared.Next(1000,5000)).ContinueWith(_ => obj));
                                  return client;
                              });

However, while there seems to be a delay when I first call the method, once this has resolved, it just seems to return the completed task with the same model in it every time I call it for that IDiscosClient instance.

Is there a simple enough way to accomplish this?


Solution

  • So the issue is that the code above only creates a fresh Task the first time and then returns the same one (which has already completed) each subsequent time.

    To fix this, we can either change the code above to:

    client.GetSingle<DiscosObject>(Arg.Any<string>()).Returns(_ => Task.Delay(Random.Shared.Next(1000,5000)).ContinueWith(_ => obj));
    

    Or, for legibilities sake, we can extract it into a method and make the whole code block:

    builder.Services.AddTransient(_ =>
                                  {
                                      IDiscosClient client = Substitute.For<IDiscosClient>();
                                      client.GetSingle<DiscosObject>(Arg.Any<string>()).Returns(GetDiscosObject);
                                      return client;
                                  });
    
    async Task<DiscosObject> GetDiscosObject(CallInfo _)
    {
        await Task.Delay(Random.Shared.Next(1000, 5000));
        return fixture.Create<DiscosObject>();
    }