unknown
unknown

Reputation: 203

Testing controller with xUnit

I have webapi where it needs to call some other endpoint and get data.

My current code as follows

//http client implementation

public interface IHttpClientFactory
{
    HttpClient Create();
}

public class HttpClientFactory : IHttpClientFactory
{
    private readonly ApplicationSettings _applicationSettings;
    HttpClient _httpClient;

    public HttpClientFactory(IOptions<ApplicationSettings> settings)
    {
        _applicationSettings = settings.Value;
    }
    public HttpClient Create()
    {
        if (_httpClient != null)
            return _httpClient;

        var client = new HttpClient()
        {
            BaseAddress = new Uri($"{_applicationSettings.BaseUrl}")
        };
        _httpClient = client;
        return _httpClient;
    }
}

 public interface IGetItemsQuery
{
    Task<IEnumerable<T>> Execute<T>(string url);
}

public class GetItemQuery: IGetItemsQuery
{
    private readonly IHttpClientFactory _httpClientFactory;
    public GetPhotosQuery(IHttpClientFactory httpClientFactory)
    {
        _httpClientFactory = httpClientFactory;
    }

    public async Task<IEnumerable<T>> Execute<T>(string url)
    {
        using (var response = await _httpClientFactory.Create().GetAsync($"{url}").ConfigureAwait(false))
        {
            response.EnsureSuccessStatusCode();
            var resp = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
            var items = JArray.Parse(resp);
            return items.ToObject<T[]>();
        }
    }

In my controller part

   private readonly IGetItemsQuery _getItemsQuery;

       public HomeController(IGetItemsQuery getItemsQuery)
    {
        _getItemsQuery = getItemsQuery;
    }

 appsettings

     "ApplicationSettings": {
      "BaseUrl": "http://someendpoint.com/"

}

Startup

   services.Configure<ApplicationSettings>(Configuration.GetSection("ApplicationSettings"));
        services.AddScoped<IGetItemsQuery, GetPhotosQuery>();
        services.AddScoped<IHttpClientFactory, HttpClientFactory>();

I want to try something like below in my test

     [Fact]
    public void Test_Index()
    {
        // Arrange
        var itemsQuery = new Mock<IGetItemsQuery>();
        var controller = new HomeController(itemsQuery.Object);

        // Act
        var result = controller.Index();

        // Assert
        var viewResult = Assert.IsType<ViewResult>(result);
        Assert.Null(viewResult.ViewName);
    }

This is creating mock IGetItemsQuery but this isn't mocking the actual IHttpClientFactory.

Is there a way to do this

Upvotes: 3

Views: 2330

Answers (1)

Nkosi
Nkosi

Reputation: 247471

Based on your design with the abstracted dependencies there would be no need to mock a client factory in order to unit test the controller.

As you have done in your test, you mock IGetItemsQuery, but you have not set it up to behave as expected when invoked in the test.

If, for example, the controller method under test look something like this

private readonly IGetItemsQuery getItemsQuery;

public HomeController(IGetItemsQuery getItemsQuery) {
    this.getItemsQuery = getItemsQuery;
}

public async Task<IActionResult> Index() {
    var url = "....";

    var items = await getItemsQuery.Execute<MyItem>(url);

    return View(items);
}

Then an isolated unit test for the Index action as the method under test could look something like

[Fact]
public async Task Index_Should_Return_View_With_Items() {
    // Arrange
    var itemsQuery = new Mock<IGetItemsQuery>();

    var items = new MyItem[] {
        new MyItem(),
        new MyItem()
    };

    itemsQuery.Setup(_ => _.Execute<MyItem>(It.IsAny<string>()))
        .ReturnsAsync(items);

    var controller = new HomeController(itemsQuery.Object);

    // Act
    var result = await controller.Index();

    // Assert        
    var viewResult = Assert.IsType<ViewResult>(result);
    Assert.Null(viewResult.ViewName);
}

Upvotes: 3

Related Questions