I want to use a custom response when comming from UITests for a specific path.
So basically, when calling a path like "endPointThatShouldReturnAStub404" i want to return 404 and a sampleResponse. Otherwise i want to return regular stubs as you can see here (MoyaProvider<ApiProvider>(stubClosure: MoyaProvider.immediatelyStub)).
So when im passing an argument "CUSTOM_STUB" i want to stub reponses depending on the target.path. How can i do this?
static func setProvider() -> MoyaProvider<ApiProvider> {
var provider: MoyaProvider<TWApiProvider> = MoyaProvider<ApiProvider>(stubClosure: MoyaProvider.immediatelyStub)
if ProcessInfo().arguments.contains("CUSTOM_STUB") {
let endpointClosure = { (target: ApiProvider) -> Endpoint<ApiProvider> in
switch target.path {
case "endPointThatShouldReturnAStub404":
return Endpoint<ApiProvider>(url: url(route: target), sampleResponseClosure: {.networkResponse(404, target.sampleData)}, method: target.method, parameters: target.parameters)
default: ///endPointThatShouldReturnARegularStub
return Endpoint<ApiProvider>(url: url(route: target), sampleResponseClosure: {.networkResponse(200, target.sampleData)}, method: target.method, parameters: target.parameters)
}
}
// **The problem is here cause im adding a default endpointClosure**
provider = MoyaProvider<TWApiProvider>(endpointClosure: endpointClosure)
}
return provider
}
@ivangodfather The stubClosure argument for the MoyaProvider initializer is a closure that takes in a TargetType and returns StubBehavior. You can pass it a custom closure that switches on the cases of the target and returns the respective stub behavior that you'd like. Is this what you were looking for?
Example:
let stubClosure = { (target: TargetType) -> Moya.StubBehavior in
switch target.path {
case "endPointThatShouldReturnAStub404":
return .immediate
default:
return .never
}
}
Not exactly.
I want also for the default case continue stubbing. And for "endPointThatShouldReturnAStub404" return a custom stub.
So imagine this situation, you have all your endpoints stubbed and no other requeriment.
You can have something like: var provider: MoyaProvider<TWApiProvider> = MoyaProvider<ApiProvider>(stubClosure: MoyaProvider.immediatelyStub)
This is ok since stubs all your requests with a 200 with a json from sampleData.
Now imagine you want to do a test for a specific endpoint that relies that other still stubbed as before but for this endPoint you want to return a 404 and another json.
So in other words:
let stubClosure = { (target: TargetType) -> Moya.StubBehavior in
switch target.path {
case "endPointThatShouldReturnAStub404":
return // i want to return a custom stub like 404 and specific json
default:
return .immediate
}
}
@ivangodfather Sorry, I think I understand better now. The example was just that -- an example 馃槃.
What's wrong with your current approach?
@SD10 with my first approach?
returning return Endpoint<ApiProvider>(url: url(route: target), sampleResponseClosure: {.networkResponse(200, target.sampleData)}, method: target.method, parameters: target.parameters)i get no stub, it just do the network call when i call apiProvider.request(.404case), i want to get the sampleClosure
@ivangodfather Are you calling setProvider() before every request?
@SD10 why you think with just this
default: ///endPointThatShouldReturnARegularStub
return Endpoint<ApiProvider>(url: url(route: target), sampleResponseClosure: {.networkResponse(200, target.sampleData)}, method: target.method, parameters: target.parameters)
i would get a stub instead of a regular network call?
No, i just set this when creating the provider.
let apiProvider = UITestsSupport.setProvider()
@SD10 just thing you have an API Contract but the API still not ready. So you just create a provider with (stubClosure: MoyaProvider.immediatelyStub) and in your sample data you just return what the backend would return with 200 status code. But if you also want to test that what would happen when a certain endpoint fails you need more than that, i dont want to return the sampleData i defined there cause its the regular response, i want to customize that, makes more sense?
@ivangodfather I think there may be some issues with the initialization of the MoyaProvider.
// Here when you're passing the new endpointClosure you're not giving it stubBehavior
// it defaults to `.never`
provider = MoyaProvider<TWApiProvider>(endpointClosure: endpointClosure)
// I think it should be
provider = MoyaProvider<TWApiProvider>(endpointClosure: endpointClosure, stubClosure: MoyaProvider.immediatelyStub)
Sorry I wasn't able to help but I will check back later 馃槥 Let me know if you resolve this.
@SD10 i know, the think is that i don't want to never stub, i want to still stubbing, but with a customized stub for a specific path.
@ivangodfather Did you see the edit in my most recent comment? It looks like you're overwriting the stub behavior you provided in the first initialization of MoyaProvider.
With this approax:
provider = MoyaProvider<TWApiProvider>(endpointClosure: endpointClosure, stubClosure: MoyaProvider.immediatelyStub)
How in my tests i determine which calls are stubbed are which are not? Seems i'm missunderstood that cause makes no sot much sense to me that you actually can create a Provider setting both, but still unsure how to determine which ones goes into which closure.
And yes i know i'm overriding it, cause its not the the behaviour that i want when i found set "CUSTOM_STUB".
@ivangodfather Can you see if this documentation helps? Providers.md
Basically, your stubClosure handles the mapping of a (TargetType) -> StubBehavior.
This allows the MoyaProvider to have different stub behavior based on the target.
Similarly, the endpointClosure handles the mapping of a (TargetType) -> Endpoint.
This allows the MoyaProvider to have different endpoints based on the target.
You need this because you want to return a 404 response for some targets.
It looks like you will need both a custom stubClosure and endpointClosure to achieve the behavior you want. Am I making sense?
My final recommendation is to maybe do your tests in a Unit Test Target. Then create a mock provider solely for testing in the XCTestCase.
But in any case seem i didnt explain it properly what i wanted to achieve.
By parts:
1- One and most important
This initialization was till i opened the tick perfecly fine, and i want to continue the sampleData from my Provider still gets returned. Cause there is NO real API at this moment.
var provider: MoyaProvider<TWApiProvider> = MoyaProvider<ApiProvider>(stubClosure: MoyaProvider.immediatelyStub)
2- Now that i have all the network calls stubbed from my previous initialization, i want for an specific path to return a 404 response. Just in the case that "CUSTOM_STUB" is set. Maybe i need to create another Provider instead of doing this, i dunno.
3- Im doing this from UITests
Im gonna re-read the doc and tell you about if i see something clear this morning.
Doesnt help at all.
It says _But usually you want the same stubbing behavior for all your targets._ Yes for all the targets its ok, but i want for a specific one return a 404.
let stubClosure = { (target: ApiProvider) -> Moya.StubBehavior in
switch target {
case .404case: ???
default: return .immediate
}
}
In the 404 case i would like to return something like EndpointSampleResponse.networkResponse(404, Data()).
So from your previous comment i think i can't achieve with just a stubClosure. I need also a endPointClosure.
let endPointClosure = { (target: ApiProvider) -> Endpoint<TWApiProvider> in
switch target {
case ..404case:
return ???
default:
return //I dont want here to make a real request, i want to still stubbing.
}
}
In the 404case i want to return always the EndpointSampleResponse.networkResponse(404, Data()), not just creating an Endpoint like return Endpoint<ApiProvider>(url: url(route: target), sampleResponseClosure: {.networkResponse(404, target.sampleData)}, method: target.method, parameters: target.parameters) cause it will make the actual request since i dunno after creating a Regular endpoint how the calls can go throught the sample response if you dont create just a stubClosure when initializing. If you create a provider like this: let provider = MoyaProvider<MyService>()when the sampleResponse will be returning instead of the actual request? If you are in UnitsTests it gets returned instead of the request?
static func setProvider() -> MoyaProvider<ApiProvider> {
var provider: MoyaProvider<ApiProvider>!
if ProcessInfo().arguments.contains("STUB_TAGS") {
let endPointClosure = { (target: ApiProvider) -> Endpoint<ApiProvider> in
let sampleClosure = { () -> EndpointSampleResponse in
switch target {
case .404target: return EndpointSampleResponse.networkResponse(400, target.sampleData)
default: return EndpointSampleResponse.networkResponse(200, target.sampleData)
}
}
return Endpoint<TWApiProvider>(url: url(route: target), sampleResponseClosure: sampleClosure)
}
provider = MoyaProvider<ApiProvider>(endpointClosure: endPointClosure)
} else {
provider = MoyaProvider<ApiProvider>(stubClosure: MoyaProvider.immediatelyStub)
}
return provider
}
After reading again the docs I think this soltuion could work for me. But i dont know why if i initialize the Provider with this Endpoint the sample response closure never gets executed, instead it makes the real url calls.
@ivangodfather I'm trying to tell you that you're not receiving the sample response closure because your MoyaProvider is defaulting to a stubBehavior of .never.
// This line of code will not stub requests. It defaults to `.never`.
// This is why you are receiving an actual request and not stubbing.
provider = MoyaProvider<ApiProvider>(endpointClosure: endPointClosure)
// You need to provide a stub behavior to receive the sample response
provider = MoyaProvider<ApiProvider>(endpointClosure: endPointClosure, stubClosure: MoyaProvider.immediatelyStub)
Am I making sense?
I think i got it. So basically when you provide an endpointClosure and a stubClosure if you return .immediate or .delayed it will go to the endpointClosure and grab the sampleData.
So finally it works! Thx you a lot! If you are curious how i implemented this here it is:
static func setProvider() -> MoyaProvider<ApiProvider> {
var provider: MoyaProvider<ApiProvider>!
if ProcessInfo().arguments.contains("STUB_TAGS") {
let endPointClosure = { (target: ApiProvider) -> Endpoint<ApiProvider> in
let sampleClosure = { () -> EndpointSampleResponse in
switch target {
case .400target:
return EndpointSampleResponse.networkResponse(400, target.sampleData)
default:
return EndpointSampleResponse.networkResponse(200, target.sampleData)
}
}
return Endpoint<ApiProvider>(url: url(route: target), sampleResponseClosure: sampleClosure)
}
provider = MoyaProvider<ApiProvider>(endpointClosure: endPointClosure, stubClosure: MoyaProvider.immediatelyStub)
} else {
provider = MoyaProvider<ApiProvider>(stubClosure: MoyaProvider.immediatelyStub)
}
return provider
}
Nice to hear you've resolved the issue, and thanks for providing your implementation! 馃憤
Most helpful comment
@ivangodfather The
stubClosureargument for theMoyaProviderinitializer is a closure that takes in aTargetTypeand returnsStubBehavior. You can pass it a custom closure that switches on the cases of the target and returns the respective stub behavior that you'd like. Is this what you were looking for?Example: