简体   繁体   English

单元测试Web服务响应

[英]Unit testing Web Service responses

I am currently writing an API wrapper in C# for ResellerClub's REST/HTTP API, which provides responses in garden-variety JSON objects. 我目前正在C#中为ResellerClub的REST / HTTP API编写API包装器,它在花园种类的JSON对象中提供响应。 Invocation is performed by performing HTTP POST/GET on API endpoints using the HttpClient class. 通过使用HttpClient类在API端点上执行HTTP POST / GET来执行调用。 JSON.Net is used for parsing the responses. JSON.Net用于解析响应。

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. 我如何单独测试API的API包装器功能,因为大多数调用需要一定程度的预期状态才能成功。 For example, I cannot test the creation of a CNAME record on a domain that I have not already registered. 例如,我无法测试在我尚未注册的域上创建CNAME记录。

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? 因此,对于CNAME记录的上述示例,作为测试“安排”阶段的一部分,我应该注册一个测试域,断言它是否有效,然后执行实际的CNAME函数?

Alternative, should I come up with some way of mocking the JSON responses that are returned from the Reseller Club API? 另外,我应该想出一些模拟Reseller Club API返回的JSON响应的方法吗?

EDIT: Example of my API class (ResellerClubApi.cs) 编辑:我的API类示例(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. 我的功能使用DownloadString()方法作为从第三方服务获取响应的通用方法。

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 上面的SuggestNames()等方法从更高的服务层调用

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. 正如您所看到的,当我的ResellerClubApi类是通过HTTP执行操作之前我自己的代码中可能的最低层时,我对如何模拟来自HttpClient的JSON响应感到有些困惑。

I also don't know how to start using IoC to hand the HttpClient dependency... 我也不知道如何开始使用IoC来处理HttpClient依赖...

Thanks 谢谢

Here is a way to do it by mocking the HttpMessageHandler using Moq unit test. 这是通过使用Moq单元测试模拟HttpMessageHandler来实现的。 http://geekswithblogs.net/abhi/archive/2013/11/20/unit-tests-for-httpclient-using-httpmessagehandler.aspx http://geekswithblogs.net/abhi/archive/2013/11/20/unit-tests-for-httpclient-using-httpmessagehandler.aspx

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. 我将代码与您的ResellerClubApi类分开,其中包括下载内容和授权,以及涉及连接到远程服务的所有内容,比如ResellerClubClient ,并让它实现一个IResellerClubClient接口。

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. 这允许您测试ResellerClubApi类,而不依赖于具体的IResellerClubClient实现。 And the best is, you can change it (from HttpClient to socket or whatever and don't ever have to touch your ResellerClubApi . 最好的是,您可以更改它(从HttpClient到套接字或其他任何东西,不必触及您的ResellerClubApi

And then set up your Unit test in framework of your choice. 然后在您选择的框架中设置您的单元测试。 Some example with Moq framework: Moq框架的一些示例:

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. 通过将连接和数据检索方法抽象为由接口表示的命中自己的类,您创建了Api类UnitTestable并且易于模拟服务器响应。

Of course, the ResellerClubClient can't be Unit tested of course. 当然, ResellerClubClient当然不能进行单元测试。 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. UnitTest 永远不应涉及连接到服务器或数据库。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM