Search code examples
c#xunit

How to test interface which has dependency injection with option pattern?


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?


Solution

  • 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);