Conversation
… NetworkRequest Override Equatable and Hashable implementations for use in testing comparisons
…orkRequest as keys Refactor usages to use TestableNetworkRequest methods Rename setResponseFor to addResponseFor to more accurately reflect what the method does
By doing the countdown AFTER notifying the completion handler, awaits on the expected network request can properly gate the rest of the test case logic (for example, if the test case resets the mock network service, there isn't a race condition between the reset and the get mock response, due to prematurely ungated await)
Codecov Report
@@ Coverage Diff @@
## dev #404 +/- ##
=======================================
Coverage 96.77% 96.77%
=======================================
Files 27 27
Lines 1671 1671
=======================================
Hits 1617 1617
Misses 54 54 |
| // will need to be updated accordingly to handle that case. | ||
|
|
||
| // MARK: - Equatable (ObjC) conformance | ||
| override func isEqual(_ object: Any?) -> Bool { |
There was a problem hiding this comment.
Add comment stating isEqual only checks URL scheme, host, path and http method, but not the query parameters for equality.
| } | ||
|
|
||
| // MARK: - Hashable (ObjC) conformance | ||
| public override var hash: Int { |
There was a problem hiding this comment.
Add comment stating hash only includes URL scheme, host, path and http method.
| func addResponseFor(networkRequest: NetworkRequest, responseConnection: HttpConnection) { | ||
| let testableNetworkRequest = TestableNetworkRequest(from: networkRequest) | ||
| if networkResponses[testableNetworkRequest] != nil { | ||
| networkResponses[testableNetworkRequest]?.append(responseConnection) | ||
| } | ||
| else { | ||
| networkResponses[testableNetworkRequest] = [responseConnection] | ||
| } |
There was a problem hiding this comment.
I think this should still be setResponseFor and the networkResponses type should be [NetworkRequest: HttpConnection] unless you see a need for an array of HttpConnections for a single request. If you see here in MockNetworkRequest, only the first HttpConnection is used which I believe is a side effect of not being able to group similar NetworkRequest objects together. Now with this TestableNetworkRequest, we can just store a single HttpConnection for the set of "equal" NetworkRequests.
There was a problem hiding this comment.
I think this is a great point! Looking at the default NetworkService connectAsync implementation, I think there can only ever be a single HttpConnection response for a given request, based on the dataTask.
The only case I can think of multiple connections for the same request is maybe a recoverable failure response with retry? But even then I think we can use staggered/interleaved expectations instead of capturing both with the same key
There was a problem hiding this comment.
I've made the following changes:
- Reverted the method name change from
addResponseFortosetResponseFor - Updated param
responseConnectionto beHttpConnection?- This has the subtle effect of removing any existing dictionary entry for the given
NetworkRequestif the HttpConnection value passed isnil
- This has the subtle effect of removing any existing dictionary entry for the given
- Updated get__ResponseFor methods to be singular instead of plural
- Updated integration test cases to check for single response (non-nil instead of count), and removed array access logic
| /// when in mock mode. | ||
| func setMockResponseFor(networkRequest: NetworkRequest, responseConnection: HttpConnection?) { | ||
| helper.setResponseFor(networkRequest: networkRequest, responseConnection: responseConnection) | ||
| helper.addResponseFor(networkRequest: networkRequest, responseConnection: responseConnection ?? defaultMockResponse(networkRequest.url)) |
There was a problem hiding this comment.
Adding a default isn't strictly needed as because of the else clause in the if let response ... statement above on line 37.
There was a problem hiding this comment.
Removed the defaults here, and updated the MockNetworkService connectAsync to own the default directly
| .filter { networkRequest.isCustomEqual($0.key) } | ||
| .map { $0.value } | ||
| /// Gets all network responses for the given `NetworkRequest` | ||
| func getResponsesFor(networkRequest: NetworkRequest) -> [HttpConnection]? { |
There was a problem hiding this comment.
After changing networkResponses to [NetworkRequest: HttpConnection], this can return just HttpConnection? instead of an array.
There was a problem hiding this comment.
Updated return type to HttpConnection?
Refactor related logic for getting and setting, and usage sites
timkimadobe
left a comment
There was a problem hiding this comment.
Thanks so much for the review @kevinlind! Updated based on feedback
| func addResponseFor(networkRequest: NetworkRequest, responseConnection: HttpConnection) { | ||
| let testableNetworkRequest = TestableNetworkRequest(from: networkRequest) | ||
| if networkResponses[testableNetworkRequest] != nil { | ||
| networkResponses[testableNetworkRequest]?.append(responseConnection) | ||
| } | ||
| else { | ||
| networkResponses[testableNetworkRequest] = [responseConnection] | ||
| } |
There was a problem hiding this comment.
I think this is a great point! Looking at the default NetworkService connectAsync implementation, I think there can only ever be a single HttpConnection response for a given request, based on the dataTask.
The only case I can think of multiple connections for the same request is maybe a recoverable failure response with retry? But even then I think we can use staggered/interleaved expectations instead of capturing both with the same key
| .filter { networkRequest.isCustomEqual($0.key) } | ||
| .map { $0.value } | ||
| /// Gets all network responses for the given `NetworkRequest` | ||
| func getResponsesFor(networkRequest: NetworkRequest) -> [HttpConnection]? { |
There was a problem hiding this comment.
Updated return type to HttpConnection?
| // will need to be updated accordingly to handle that case. | ||
|
|
||
| // MARK: - Equatable (ObjC) conformance | ||
| override func isEqual(_ object: Any?) -> Bool { |
| } | ||
|
|
||
| // MARK: - Hashable (ObjC) conformance | ||
| public override var hash: Int { |
| func addResponseFor(networkRequest: NetworkRequest, responseConnection: HttpConnection) { | ||
| let testableNetworkRequest = TestableNetworkRequest(from: networkRequest) | ||
| if networkResponses[testableNetworkRequest] != nil { | ||
| networkResponses[testableNetworkRequest]?.append(responseConnection) | ||
| } | ||
| else { | ||
| networkResponses[testableNetworkRequest] = [responseConnection] | ||
| } |
There was a problem hiding this comment.
I've made the following changes:
- Reverted the method name change from
addResponseFortosetResponseFor - Updated param
responseConnectionto beHttpConnection?- This has the subtle effect of removing any existing dictionary entry for the given
NetworkRequestif the HttpConnection value passed isnil
- This has the subtle effect of removing any existing dictionary entry for the given
- Updated get__ResponseFor methods to be singular instead of plural
- Updated integration test cases to check for single response (non-nil instead of count), and removed array access logic
| /// when in mock mode. | ||
| func setMockResponseFor(networkRequest: NetworkRequest, responseConnection: HttpConnection?) { | ||
| helper.setResponseFor(networkRequest: networkRequest, responseConnection: responseConnection) | ||
| helper.addResponseFor(networkRequest: networkRequest, responseConnection: responseConnection ?? defaultMockResponse(networkRequest.url)) |
There was a problem hiding this comment.
Removed the defaults here, and updated the MockNetworkService connectAsync to own the default directly
kevinlind
left a comment
There was a problem hiding this comment.
One minor comment about documentation, otherwise looks good.
| return networkResponses | ||
| .filter { networkRequest.isCustomEqual($0.key) } | ||
| .map { $0.value } | ||
| /// Gets all network responses for the given `NetworkRequest` |
There was a problem hiding this comment.
nit: Update comment "Get network response for the given NetworkRequest", plus add comment for parameter and return value.
There was a problem hiding this comment.
Updated method docs!
Description
This PR:
TestableNetworkRequestthat uses the@testable import AEPServicesto gainopeninheritable access to theNetworkRequestclass.TestableNetworkRequestoverrides the Equatable and Hashable conformance ofNetworkRequestfor direct use as a dictionary key using custom logic.EquatableandHashablemust have exactly the same results logically for what they consider the "same" elementdefaultMockResponseinMockNetworkServicethat creates a default response provided a valid URLNetworkServiceHelperdata structs and methods to account for the new key equality behavior, and refactors usages accordinglyMockNetworkServiceconnectAsyncimplementation by moving the countdown to after the callback is notified, to properly gate test case logic using awaitRelated Issue
Motivation and Context
How Has This Been Tested?
Screenshots (if appropriate):
Types of changes
Checklist: