2

Let's say I have a web api application which has this service interface.

public interface IMyService
{
    Task<int> Process(string processType);
}

and this is my service class. I am using option pattern.

public class MyService : IMyService
{
    private readonly MyOptions _myOptions;
    private readonly MyContext _myContext;
    private readonly IHttpClientFactory _httpClientFactory;

    public MyService(IOptions<myOptions> myOptions,
        MyContext myContext,
        IHttpClientFactory httpClientFactory)
    {
        _myOptions = myOptions.Value;
        _myContext = myContext;
        _httpClientFactory = httpClientFactory;
    }
}

and this is how I register it in Program.cs. I am using .NET 6 framework.

var builder = WebApplication.CreateBuilder(args);

var myConnStr = 
builder.Configuration.GetConnectionString("MyConnection");

// EF Core
builder.Services.AddDbContext<MyContext>(options =>
{
    options.UseSqlServer(myConnStr);
});

builder.Services.AddOptions().Configure<MyOptions>(builder.Configuration.GetSection("ABC"));

builder.Services.AddHttpClient();

builder.Services.AddScoped<IMyService, MyService>();

// some code are removed for brevity

How do I test Process method in my service using Xunit? Note: Although the web api is calling this method, I do not want to test the web api. The reason is because this is actually a background process. Thus there won't be much thing return in the controller (web api). I also don't feel the need to mock it if possible to simplify the integration test.

I managed to do something like this.

public class MyServiceTest
{
    private const string sqlServerConnection = "xxx";
    private IMyService _myService;
    private MyContext _myContext;

    public MyServiceTest()
    {            
        _configuration = new ConfigurationBuilder().AddJsonFile("appsettings.test.json").Build();
    }

    [Fact]
    public async Task WhenProcessIsInValid_ThenReturn0()
    {
        // Arrange
        SetupSqlServerContext();
        await SetupMockData();

        // _myService = new MyService(_myContext); 
    }
}

    private void SetupSqlServerContext()
    {
        var options = new DbContextOptionsBuilder<MyContext>();
        options.UseSqlServer(sqlServerConnection);

        _myContext = new SqlServer.MyContext(options.Options);
    }

I am stuck on creating the instance for MyService. How do I pass it IHttpClientFactory and IOptions<yOptions> to its constructor?

Steve
  • 2,963
  • 15
  • 61
  • 133
  • 2
    You can use [this](https://stackoverflow.com/questions/39131219/is-it-possible-to-use-dependency-injection-with-xunit) Q&A to set up Dependency Injection for your test project and register the services needed and then simply inject your `MyService` into `MyServiceTest` – MindSwipe Aug 31 '22 at 12:39
  • 1
    [Moq](https://github.com/moq/moq) them all ! Juste create your service instance manually passing Mocks as parameters. Unless you are using the containr directly inside your class, that is sufficient for most needs. – Irwene Aug 31 '22 at 13:05
  • 1
    @MindSwipe I wouldn't recommend using DI in unit tests. UTs should be simple. Adding DI in UTs will delay error discovery from compile time to runtime. You will then start the chase after missing dependencies running the test again and again to discover all the missing parts. The dev experience is very similar to using the Service Locator Antu-Pattern. – Artur Sep 02 '22 at 20:42

1 Answers1

2

Use one of the mocking frameworks like Moq, for example, to mock the IHttpClientFactory. It will look similar to this

var factoryMock = new Mock<IHttpClientFactory>();
factoryMock
    .Setup(factory => factory.CreateClient(It.IsAny<string>()))
    .Returns(httpClientMock.Object);

Check this answer to learn how to mock HttpClient.

For IOptions<MyOptions> it's even simpler, just create an instance of it using this code

var options = Options.Create(new MyOptions{ });

And finally, instantiate the MyService

_myService = new MyService(options, _myContext, factoryMock.Object); 
Artur
  • 4,595
  • 25
  • 38