سؤال

I am currently writing an API wrapper in C# for ResellerClub's REST/HTTP API, which provides responses in garden-variety JSON objects. Invocation is performed by performing HTTP POST/GET on API endpoints using the HttpClient class. JSON.Net is used for parsing the responses.

How I can unit test my API wrapper functionality for the API as most calls require a level of expected state in order to succeed. For example, I cannot test the creation of a CNAME record on a domain that I have not already registered.

I understand that tests should never rely on state which they do not arrange themselves, and I've also been told that the tests should never actually deal with any kind of persistence mechanism such as a database. So, for the above example of a CNAME record, that as part of the "Arrange" phase of the test I should register a test domain, assert it worked, then do the actual CNAME function?

Alternative, should I come up with some way of mocking the JSON responses that are returned from the Reseller Club API?

EDIT: Example of my API class (ResellerClubApi.cs)

private async Task<string> DownloadString(string uri) 
{
   // HttpClient object downloads the JSON response string asynchronously
}

The DownloadString() method is used by my functionality as a generic means of grabbing the response from the third party service.

public async Task<List<string>> SuggestNames(string domainName) 
{
   // Calls DownloadString() with the correct URI, uses Newtonsoft.JSON to parse 
   // string representation of JSON into object
}

Methods such as SuggestNames() above are called like this from the higher service layer

public void someServiceLayerMethod() 
{
   var rcApi = new ResellerClubApi();

   var x = rcApi.SuggestNames("something");

   // ...

}

As you can see, I am a bit stuck as to how to mock JSON responses from the likes of HttpClient when my ResellerClubApi class is the lowest possible layer of my own code prior to doing things over HTTP.

I also don't know how to start using IoC to hand the HttpClient dependency...

Thanks

هل كانت مفيدة؟

المحلول

I would separate the code from your ResellerClubApi class which involves downloading stuff and authorization, and everything that involves connecting to a remote service, in let's say a ResellerClubClient and have it implement a IResellerClubClient interface.

public interface IResellerClubClient {
    string RequestJson(string url);
}

public class ResellerClubClient : IResellerClubClient {
    // implement your methods here 
}

public ResellerClubApi : IResellerClubApi {
    private readonly IResellerClubClient client;
    // Pass the client as dependency, either manually or using Dependency framework of your choice
    public ResellerClubApi(IResellerClubClient client) {
        this.client = client;
    }

    public List<string> SuggestNames(string domainName) {
        var jsonString = this.client.RequestJson("http://example.com/domains/?name="+domainName);
        // decode it and do something with it
    }
}

This allows you to test your ResellerClubApi class without being depending on a concrete IResellerClubClient implementation. And the best is, you can change it (from HttpClient to socket or whatever and don't ever have to touch your ResellerClubApi.

And then set up your Unit test in framework of your choice. Some example with Moq framework:

var mockedJsonString = '{ succes: true, names: ["domainA.com", "domainA.us"] }';

// create mockup object using IResellerClubClient interface
var resellerClubClient = new Mock<IResellerClubClient>();
// Tell the mock object to return "mockedJsonString" when any parameter is passed to RequestJsonString. 
// If you do more than 1 call in a test, or if that's expected to be called multiple times inside
// the method to be tested, you can setup multiple conditions and results this way too
resellerClubClient.Setup(x => x.RequestJson(It.IsAny<string>())).Returns(mockedJsonString);

var api = new ResellerClubApi(resellerClubClient.Object);
List<string> names = api.SuggestNames("domain.com");

// do your assertions here 

By having abstracted the connection and data retrieving methods into hit's own class represented by an interface, you made your Api class UnitTestable and easy to mock server responses.

Of course, the ResellerClubClient can't be Unit tested of course. But it can be done in an integration test or a verification test. A UnitTest should never involve connecting to a server or a database.

نصائح أخرى

Here is a way to do it by mocking the HttpMessageHandler using Moq unit test. http://geekswithblogs.net/abhi/archive/2013/11/20/unit-tests-for-httpclient-using-httpmessagehandler.aspx

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top