GYHttpMock is a new open source iOS request simulation tool for iOS App network layer development. GYHttpMock intercepts specified HTTP requests and completely replaces or partially modifs real network return data based on rules.

background

In the development process of iOS App, the foreground development process is usually carried out in parallel, so it is inevitable that some clients need to wait for the background development, and the waiting process is often painful and helpless (the background is urged to be painful, and the front end has to wait). The usual solution is for the client to return data from the HardCode network somewhere, and of course, it is not uncommon for such test code to be accidentally submitted online. There is a more “advanced” way of setting up proxies and using packet capture tools to modify network data, but this is maddeningly inefficient.

Introducing a tool that simulates network requests seems to be an easy way to meet this requirement, but it turns out that “simulating network requests” is not an easy requirement. For example, for new business, if there is no data in the background, the front end can make fake data according to the agreement. However, in many cases, it may be a change to an existing service, that is, the existing service data needs to be modified in the background.

Industry Solutions

In order to meet the requirements of simulating network requests during development, HttpMock tools have been developed. There are many different implementations in the industry, which can be basically divided into two categories:

1. Create an HTTP Server

The HTTP Server can be set up locally to simulate the return of data required by the client. Take hibri/HttpMock as an example, which sets up an HTTP Mock Server locally and returns the specified data as required. Requests that do not need to be emulated are directed to the real Server, and requests that need to be emulated are directed to MockServer.

Review images

The advantage of this approach is that it can be applied to multiple platforms and implemented in a variety of languages. However, the limitation is that to establish an HTTP Server, on the one hand, you have to build and maintain the Server, which has a high threshold for users. On the other hand, when using the Server, you need to modify the client code while switching to the Server environment to modify the returned data, which is quite troublesome. In addition, this scheme can only replace or not replace, and cannot replace the data returned by a request.

2. Client interception

Clients can intercept their own network requests at the network layer and return specified data. This way of implementing HttpMock is more flexible, but it can be implemented completely differently on different clients. The implementation principle is the request distribution of the network layer of the Hook system. It intercepts the HTTP request that meets the rules, and then directly calls back to the upper layer with the data defined before, without sending out real requests.

Review images

OHHTTPStubs and Nocilla are widely used on iOS, and they have similar functions. Nocilla chooses to create mock requests in the form of domain-specific language (DSL), which is easier to understand. However, mock functions need to be actively turned on and off in the application. Once turned on or off, all HTTP requests in the application will be affected. OHHTTPStubs automatically starts after installation, and determines whether interception is required according to request. However, none of these open source libraries can flexibly modify the data returned by the network.

GYHttpMock advantage

Based on the Nocilla DSL feature and the automatic opening and recognition of OHHTTPStubs, GYHttpMock implements partial replacement of HTTP response. Specific advantages:

  • Partial HTTP Response support, which allows you to modify data returned by the real network, is a core feature unique to other HttpMocks.
  • With the introduction of GYHttpMock, clients can intercept specific requests and return the data they need in a single line of code. There is no Server support and no need to set up a local HTTP Server.
  • Supports NSURLConnection, NSURLSession, AFNetworking and all networking frameworks that use iOS Cocoa URL loading.
  • Support for re matching HTTP Requests so that one httpMock can support multiple requests at the same time.
  • mocked Response Supports JSON files. In general, it is clear to express mocked response directly with NSString, but in the case of a large number of returned contents, it is easier to write the contents into the file in JSON format because of escape characters.

use

The installation

Simply add the GYHttpMock source file to the project. You can also access it through CocoaPods.

application

Create the correct mockRequest before the request to intercept:

1. Create a simple mockRequest. Intercept an application get request to www.weread.com and return an empty Response body.

mockRequest(@"GET", @"http://www.weread.com");
Copy the code

2. Create a mockRequest that intercepts with more complex conditions. The capture application url contains weread.com and contains the name= ABC parameter

mockRequest(@"GET", @"(.*?) weread.com(.*?)".regex). withBody(@"{\"name\":\"abc\"}".regex);Copy the code

3. Create a mockRequest specifying the returned data. The withBody value could also be some xxx.json file, but that JSON file needs to be added to the project.

mockRequest(@"POST", @"http://www.weread.com").
    withBody(@"{\"name\":\"abc\"}".regex);
    andReturn(200).
    withBody(@"{\"key\":\"value\"}");Copy the code

4. Create a mockRequest that modifies the partially returned data. The data returned by a normal network is modified based on the weread.json contents

mockRequest(@"POST", @"http://www.weread.com"). isUpdatePartResponseBody(YES). withBody(@"{\"name\":\"abc\"}".regex); AndReturn (200). WithBody (@ "weread. Json");Copy the code

Suppose the raw data returned by a normal network looks like this:

{" data ": [{" bookId" : "0000001", "updated" : [{" chapterIdx ": 1," title ":" preface ",}, {" chapterIdx ": 2," title ": "Chapter 2 ",}]}]}Copy the code

Weread. json looks like this:

{" data ": [{" bookId" : "0000001", "updated" : [{" chapterIdx ": 1," title ":" preface ",}, {" chapterIdx ": 2," title ": "Chapter 2 ",}]}]}Copy the code

The modified data would look like this:

{" data ": [{" bookId" : "0000001", "updated" : [{" chapterIdx ": 1," title ":" preface ", "hello", "world"}, {" chapterIdx ": 2, "title": "chapter 2 ", "hello":"world"}]}Copy the code

GYHttpMock will modify the raw data according to the weread.json specified hierarchy, as long as the wearied.json data structure is consistent with the normal returned data, otherwise it will cause modification failures or unpredictable errors.

Realize the principle of

Here’s how GYHttpMock works: View the image

Its core implementation mainly includes three parts: Request matching, request interception and Response replacement.

Matching the request

Used to determine whether an HTTP Request in an application should be mock. The criteria include method, URL, Headers, and Body. URL and Body support regular matching. An httpMock can match multiple HTTP requests at the same time.

The request to intercept

Request interception is implemented by subclassing NSURLProtocol. NSURLProtocol is a very powerful class in iOS URL network loading. The official document also describes NSURLProtocol. By rewriting its method, you can redefine the system network loading behavior. Previously, for network requests with NSURLConnection, the subclass of NSURLProtocol, GYMockURLProtocol, was registered as follows

[NSURLProtocol registerClass:[GYMockURLProtocol class]];
Copy the code

Replace the protocolClasses method for network requests on NSURLSession

Class cls = NSClassFromString(@"__NSCFURLSessionConfiguration") ? : NSClassFromString(@"NSURLSessionConfiguration"); [self swizzleSelector:@selector(protocolClasses) fromClass:cls toClass:[self class]];Copy the code

Finally, the focus is on overriding the canInitWithRequest and startLoading methods of the NSURLProtocol class. CanInitWithRequest is used to determine whether network requests can be initiated. Requests that are not in the interception range can be filtered through this filter and normal network requests of App will not be affected. StartLoading is the core of replacing response data. A successfully intercepted request will enter this method, which will replace or modify response data and then call back to the upper layer.

The response to replace

For the need to replace all the response, is implemented in startLoading method of adjustable NSURLProtocol URLProtocol: didReceiveResponse: cacheStoragePolicy: method, Calls back the replaced response to the upper layer. For a response that needs to be partially replaced, GYHttpMock makes a real network request in the form of an NSURLConnection, merges the response data from mockRequest when the data comes back, and finally calls the merged data back to the upper layer. There are two problems with partial replacement:

  1. Partial replacement involves making a real network request to retrieve the raw data, which is intercepted by NSURLProtocol according to the previous rules, thus entering an endless loop. The solution is to mark the GYHttpRequest before starting the request to indicate that it does not need to be intercepted again, and then untag the GYHttpRequest after retrieving the Reponse to avoid an endless loop.

  2. The problem of merging two response contents. Because JSON data structures are very flexible and can be nested at any level, specifying how to modify or add data under a node can be difficult. In particular, the nesting of arrays in JSON makes specifying how to modify elements at a particular location in the array very difficult. GYHttpMock does this by pointing out the complete location of the node to be modified in a mockRequest response, and then using this data structure to match the target data. (See the GYHttpMock source code for the algorithm. The advantage of GYHttpMock is that it supports more complex data structures. But this requires the user to be very clear about the target data structure.

GYHttpMock has been open source on GitHub and is currently used in the wechat Reading project. If you have any questions or suggestions, please submit an issue or pull Request.