I'm developing a set of classes designed to communicate with external APIs, and I'm running into trouble with how to properly structure everything for proper loose coupling and unit testing.

Currently, each API we need to talk to has a distinct class, which implements an interface a bit like this:

public interface IApiIntegration
{
    Task<string> SearchApi (List<string> searchValues);
    Task<string> GetFromApi(string idToGet);
    Task<bool> PostToApi(PostObject api);
}

Each api class inherits from a base abstract with implements this interface. That class also contains a number of helper functions which are only relevant to handling data coming to and from Apis.

Beneath the public PostToApi method of each class there are also a bunch of helper functions to build the object to be posted. These are often quite complicated, and could really do with testing. However, they're specific to the class in question and are thus private.

Inside every public function on IApiIntegration there is also, of course, a call to an external Api. For example it might look something like:

public override async Task<string> GetFromApi(string id)
{
    string result = "";
    string path = $"{integration.RootUrl}items/{id}?username={integration.Username}&key={integration.Password}";

    // client is a static instance of HttpClient
    HttpResponseMessage response = await client.GetAsync(path);
    if (response.IsSuccessStatusCode)
    {
        result = await response.Content.ReadAsStringAsync();
    }

    return result;
}

This leaves me with two problems:

1) It feels right that the helper methods in the base class and the individual classes should be open to unit testing, but also that they should be protected/private. Something, therefore, is clearly wrong with the structure.

2) It's obviously wrong to be testing external APIs so I need somehow to bypass or mock out those dependencies. But that's not possible in this structure.

How can I refactor and restructure this to ensure everything is open for unit tests?

  • Have you considered decoupling Task from HttpClient? Set a new abstraction between these two. Something like Datasource. But do it only if Task has any logic (business) that worth to test. That might not be the case of the example. – Laiv Dec 3 at 15:53

When you have an external API you cant control, ie it doesnt have a test instance with static data, your best bet is to record and replay the network traffic from real calls.

Once you have captured this data you can setup a mock server which listens for incomming requests, matches them against its known requests and returns the matching response.

Now you can test your client against this server without having to expose internal methods

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.