Subito-it / SBTUITestTunnel

Enable network mocks and more in UI Tests

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Monitored requests have no httpBody

fermoya opened this issue · comments

Hi, I have this UITest that was previously working in iOS 16.4 simulator. We've moved now to use iOS 17.2 simulator. We're using latest version 10.0.0. I'm monitoring a request and debugging I can see we send a body. However, when I peek the requests:

app.monitoredRequestsPeekAll()

I see my requests have no httpBody somehow whereas if I run the same test on iOS 16.4 I can see the body. This is a regular request using URLSession.dataTask(with:completionHandler).

Any ideas what's wrong?

Hi there! I just tried running the library's test suite on an iOS 17.2 simulator, and tests are passing including a few that also verify the httpBody. Moreover we have several tests in the codebase @Subito that monitor requests and checks the httpBody. Did you try to replicate the issue in a separate project that can be shared? It would make debugging the issue easier.

Hey @tcamin, I have the same problem.
The issue doesn't seem to reproduce on a small body.

Try to change httpBody in this test to String(repeatElement("a", count: 20000))
and you will see this error in the console:

Dropping oversized protocol property key SBTUITunneledNSURLProtocolHTTPBodyKey in <NSMutableURLRequest: 0x60000000c1e0> { URL: https://httpbin.org/post }

That's interesting! I'll try to take a look as soon as I can.

I've have this open branch to test out hotfix/large_body. I still need to investigate some issues when running in CI but locally it seems to be working. If you try it out let me know!

hey @tcamin thanks for the quick fix. I just tried and whereas now the request has an httpBody it seems that the ObjC casting is off:

Could not cast value of type '__NSCFString' (0x1f1d04258) to 'NSData' (0x1f1d09130).

httpBody is supposed to have NSData type. I've seen this before while working in ObjC

I'm checking the diff:

+ (void)setProperty:(id)property forKey:(nonnull NSString *)key inRequest:(nonnull NSMutableURLRequest *)request
{
    if ([property isKindOfClass:[NSData class]] && ((NSData *)property).length > 16834) { ... }
    else { ... }
}

+ (id)propertyForKey:(NSString *)key inRequest:(NSURLRequest *)request;
{
    id property = [NSURLProtocol propertyForKey:key inRequest:request];
    return [storage objectForKey:property] ?: property;
}

it seems that storage is always storing NSData but propertyForKey returns anything. And there's also a collision of types. If I'm not wrong, [storage objectForKey:property] should return NSData whereas ?: property returns NSString

Yeah so the original body is something like this:

{
    "foo": <very_long_text>
    "bar": <something_of_interest_for_the_test>
}

and what I get in the monitored [request HTTPBody] is a UUID: D68DD094-EAB4-4CEA-B401-AB1FA21BB34A

The logic should be right, I've slightly modified the implementation which should address the issue you are experiencing. Let me know if it works now.

Thanks @tcamin . I've updated my dependency to point at 985d63ec9a6080aeacbc2ca9d76b86cf2cea189e but unfortunately I still see the same issue.

What is it that you tweaked?

Same issue as in getting UUID or empty body?

Same issue as in getting UUID or empty body?

Getting UUID

Could you try again on e5c0f7f?

hey same issue on e5c0f7f4bd343627e55aecc39b76c39b32c8b444, I'm getting a UUID:

(lldb) po _originalRequest.HTTPBody
E8A5F5B2-65EF-432D-B7C4-F26E44E8DFA3

@tcamin it seems that the properties saved in SBTRequestPropertyStorage from the app process are not visible in the UI Tests process.

@tcamin we have the same issue, any updates about the fix

Hi! Sorry for the late reply. It would help if you could provide a way to replicate this locally, ideally by adding a failing test to the libary.

@tcamin We appreciate your support and adding this test will help reproduce the issue locally. Please add the following test to DownloadUploadTests:

func testMonitorPostRequestWithHTTPLargeBodyInAppProcess() {

        let largeBody = String(repeating: "a", count: 20000)
        let matchingRequest = SBTRequestMatch(url: "httpbin.org", method: "POST")
        app.monitorRequests(matching: matchingRequest)

        XCTAssertTrue(app.tables.firstMatch.staticTexts["executePostDataTaskRequestWithLargeHTTPBody"].waitForExistence(timeout: 5))
        app.tables.firstMatch.staticTexts["executePostDataTaskRequestWithLargeHTTPBody"].tap()

        XCTAssertTrue(app.waitForMonitoredRequests(matching: matchingRequest, timeout: 10))
        let requests = app.monitoredRequestsFlushAll()
        XCTAssertEqual(requests.count, 1)

        print(requests.map(\.debugDescription))

        for request in requests {
            guard let httpBody = request.request?.httpBody else {
                XCTFail("Missing http body")
                continue
            }

            XCTAssertEqual(String(data: httpBody, encoding: .utf8), largeBody)

            XCTAssert((request.responseString()!).contains("httpbin.org"))
            XCTAssert(request.timestamp > 0.0)
            XCTAssert(request.requestTime > 0.0)
        }

        XCTAssert(app.stubRequestsRemoveAll())
        XCTAssert(app.monitorRequestRemoveAll())
    }

and add in SBTTableViewController

@objc func executePostDataTaskRequestWithLargeHTTPBody() {
        let largeBody = String(repeating: "a", count: 20000)
        dataTaskNetwork(urlString: "https://httpbin.org/post", httpMethod: "POST", httpBody: largeBody)
    }

The issue is that the request being fired is from the app process and not from the UITest process. As a result, we are unable to obtain the original request body.

this is working @AhmedAshraf605 , thanks for the contribution 🙌

@tcamin will you cut a new version soon?

Hi! If everything works on our tests suites for a few days I will release a new version in the coming week.