Unit Testing nested methods

Viewed 870

While I understand that when unit testing a method it is very important to mock all it's dependencies, what I still have confusion about is what happens when the method is nested? Do I mock only the dependencies of the parent method or do I mock the dependencies of the child method(s) as well or do I set expectations on the calls to the dependent object and set the exact return values so that I can perform the test that I want?

For instance, in the below example, if we want to unit test the method B, do we only mock IHttpClientFactory & ILogger or do we also set the method's return value to what we are actually expecting because otherwise when the test method executes it goes ahead and tries to execute methodC where it fails because the value of client after the line var client = _clientFactory.CreateClient() executes is null?

using System.Net.Http;
...

public class classA
{
   private readonly IHttpClientFactory _clientFactory;
   private sting url = "...";
   private ILogger _log { get; set; }
   ...

   public classA(ILogger log, IHttpClientFactory clientFactory, ...)
   {
     _log = log;
     _clientFactory = clientFactory;
     ...
   }

   public string methodB(string inputB)
   {
      var varB = methodC(inputB);
      ...
      return ..;
   }

   public string methodC(string inputC)
   {
      ...
      var client = _clientFactory.CreateClient();
      client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
      HttpResponseMessage httpResponseMessage = await client.PostAsync(url, new StringContent(inputC, Encoding.UTF8, "application/json"));
      responJsonText = await httpResponseMessage.Content.ReadAsStringAsync();
      ...
      return ..;
   }

}
1 Answers

So you have an HTTP client, a high-level method that gets you some structured data and a low-level method that gets you the contents of the response.

These things are more of an art than hard-cut rules, but the rule I prefer most of the time is to write code that can have all of its I/O abstracted and then mock or test-implement the I/O itself. This way the most amount of business logic would be testable.

I/O can be many things - file, network, user input, but even things such as getting a certificate from the cert store or reading registry setting. Any data that originates at runtime from outside the process is I/O, no matter the method.

When you mock functionality, the most common things you're interested in is validating the method's input or emulating its output (or both). So in your mock, you shouldn't be too concerned with the actual implementation since you're not testing your mocked method - you're testing whatever is calling it.

So... about your sample code. If you're trying to test MethodB, you'd need MethodC to have a test implementation - either by mocking the HttpClient it relies on or by making it virtual and having it overridden in test.

Side note: reuse the HttpClient, keep it with the class

Related