suntunutuxx
suntunutuxx

Reputation: 35

How do I inject dependencies in Moq testing without the interface on the constructor of the service?

I'm trying to create a Moq test project but I cannot inject the dependencies on the service/interface because my service constructor doesn't have an interface argument.

Service constructor:

public NearEarthObjectService(HttpClient httpClient)
        {
            _httpClient = httpClient;
        }

Project Program.cs:

using NasaApi.Services;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddTransient(sp => new HttpClient { BaseAddress = new Uri("https://api.nasa.gov/neo/rest/v1/") });
builder.Services.AddScoped<INearEarthObjectService, NearEarthObjectService>();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Empty Moq test project :(

public class NearEarthObjectService_Tests {

public NearEarthObjectService_Tests()
{
    
}

As can you see, the dependencies in the program are injected in the Startup.cs with the builder.Services.AddScoped

builder.Services.AddScoped<INearEarthObjectService, NearEarthObjectService>();

My controller

[Route("[controller]")]
    [ApiController]
    public class AsteroidsController : ControllerBase
    {
        INearEarthObjectService _earthObjectService;

    public AsteroidsController(INearEarthObjectService earthObjectService)
    {
        _earthObjectService = earthObjectService;
    }

    // GET: <AsteroidsController>
    [HttpGet]
    public async Task<ActionResult<IEnumerable<NearEarthObjectDTO>>> Get([BindRequired, FromQuery] int days)
    {
        if (days < 1 || days > 7)
        {
            return StatusCode(400);
        }
        else
        {
            var response = await Task.Run(() => _earthObjectService.GetAllNeos(days));
            return Ok(response);
        }
    }
}

Upvotes: 0

Views: 666

Answers (1)

P.G Wisgerhof
P.G Wisgerhof

Reputation: 762

You can't mock HttpClient with a mocking library for so far as I know. What you can do is instantiate a new HttpClient in you test and provide a custom HttpMessageHandler to it's contructor. This is what I always do to fake responses for HttpClient calls. It works like this

First create your custom HttpMessageHandler:

public class MyHttpMessageHandler : HttpMessageHandler
{
    private readonly HttpResponseMessage fakeRespone;

    public MyHttpMessageHandler(HttpResponseMessage fakeRespone)
    {
        this.fakeRespone = fakeRespone;
    }

    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        return Task.FromResult(fakeRespone);
    }
}

And then instantiate your test like this:

[Fact]
public async void Test1()
{
    //Mock the HTTP client
    var fakeResponse = new HttpResponseMessage
    {
        StatusCode = HttpStatusCode.OK,
    };
    var httpClient = new HttpClient(new MyHttpMessageHandler(fakeResponse));

    //Create System under test
    var sut = new ClassToTest(httpClient);

    //Call your method
    var response = await sut.CallHttpClient(new Uri("http://example.com/somepage"));
    Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}

For good measure, this is test class i created for the example:

public class ClassToTest
{
    private readonly HttpClient httpClient;

    public ClassToTest(HttpClient httpClient)
    {
        this.httpClient = httpClient;
    }

    public Task<HttpResponseMessage> CallHttpClient(Uri uri)
    {
        return httpClient.GetAsync(uri);
    }
}

Hope this helps you

Upvotes: 2

Related Questions