3

I need to write unit tests (and most importantly, i'm being asked to get code coverage) for this controller.

public class MyController : Controller
{
    public async Task<ActionResult> Index()
    {
        ViewBag.Title = "Home Page";
        var httpClient = new HttpClient();
        var json = await httpClient.GetStringAsync(ConfigurationManager.AppSettings["MyCustomAPI"]);
        var tasks = JsonConvert.DeserializeObject<List<MyObject>>(json);
        return View(tasks);
    }
}

I've been reading ideas for two days about this but i can't wrap my head around it.

I've been considering injecting the HttpClient like this:

    private readonly HttpClient _httpClient;

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

But it's probably not a good idea to pass an HttpClient in the controller constructor.

Then i've been following this guide that uses FakeItEasy but i couldn't adapt this solution to my requirements.

How should i proceed for testing this?

1
  • First of all, see this post: codereview.stackexchange.com/questions/69950/… regarding creating a new HttpClient all the time. Second, you could abstract the HttpClient work into an interface, then inject that interface into the controller, that way you'd be able to mock it up for testing. Commented Apr 11, 2018 at 17:25

1 Answer 1

2

Extract your http work into an interface and inject an implementation of that interface into your controller.

public interface INetworkService {
  Task<List<MyObject>> GetThingsAsync(string api);
}

public class HttpNetworkService : INetworkService {
  private readonly HttpClient Client;
  public HttpNetworkService(HttpClient client) {
    Client = client;
  }

  public Task<List<MyObject>> GetThingsAsync(string api) {
    var json = await Client.GetStringAsync(api);
    return JsonConvert.DeserializeObject<List<MyObject>>(json);
  }
}

public class TestNetworkService : INetworkService {
  public Task<List<MyObject>> GetThingsAsync(string api) {
    return Task.FromResult(new List<MyObject> { /*build out your mock result here*/ });
  }
}

public class MyController : Controller {
  private readonly INetworkService NetworkService;
  public MyController(INetworkService svc) {
    NetworkService = svc;
  }

  public async Task<ActionResult> Index() {
    ViewBag.Title = "Home Page";
    var tasks = await NetworkService.GetThingsAsync(ConfigurationManager.AppSettings["MyCustomAPI"]);
    return View(tasks);
  }
}

Now when you test you can sub in a test version of the INetworkService that doesn't actually perform any networked IO. Additionally, as mentioned in the comment, you should share a single HttpClient instead of creating new ones all the time, that client could be injected into the real INetworkedService implementation.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.