In 2.x.x, we were using some <custom thing> for te...
# apollo-kotlin
s
In 2.x.x, we were using some custom thing for testing graphQL responses, which let us map mutations/queries to responses without having to specify what parameters would go into them. We did that by using the QUERY_DOCUMENT and mapping that to a response and then some code which I can’t quite get to migrate to 3.x.x. So I was thinking, maybe it’s time to use the new
MapTestNetworkTransport
instead. So I was wondering, since I don’t think there’s a way to do that right now, would it make sense to introduce a way to map an Operation to a result using something similar to registerTestResponse but by accepting an operation no matter what the inputs? Otherwise the annoying part is that I have to in cases like the one I linked above, go inside the Activity and check what is being passed inside those queries and copy it inside the tests. But in the tests that I am doing right now I am mostly interested in how the UI reacts to a particular response, not that I am also sending the correct query. With all that said, I know that the queue approach also exists, which doesn’t care for what the query was, but simply responds with specific things in order, but in many of these tests I do want the mapping functionality of at least the Operation type to the response, just not interested in what the input to that Operation was. Does my idea make sense? Does it sound reasonable? Would it even be feasible? Should I reconsider the approach I’m taking maybe? If you have any sort of input I’d love to hear it.
b
if I understand, you'd like to have a map of query types to responses, instead of specific query instances to responses? But that wouldn't work if a particular screen executes several queries of the same type but with different arguments?
s
Yeah exactly. as far as I can think, in 99.9% of the tests that I am thinking about, I wouldn’t call the same query with different variables anyway. And if I did, then it’d be fine for the response to be the same. Basically if I wanted a granular operation -> response I’d always be able to come back to the current implementation which maps the specific operation instances to the response.
b
I see. That makes sense 🙂 I think it wouldn't be too hard to try it, by making a
NetworkTransport
very similar to
MapTestNetworkTransport
but using
::class
in there for the keys of the map.
and set that to your ApolloClient in your tests
s
That’s true actually, and I think it’s very straightforward as you said. Will do that first thing tomorrow, I’ll come back here to tell you if it worked or not 😅 Thanks a lot!
🙏 2
🎉 1
b
yes very curious about if that works! If yes we could also think of having something like it in the lib
s
Alright finally had some time to look into this now. I think I am getting some trouble since no matter what the response will need to hold an instance of the
Operation
too, since the
ApolloResponse
requires an instance of an operation in its Builder function. So I couldn’t get the function which simply accepts data|errors since I need to construct an instance of the operation somehow here. The only way I got it “working” is by having the call-site construct a useless instance, which in the end doesn’t change anything. The responding ApolloResponse will hold that in its “operation” field but it just wouldn’t matter. Not the most fun solution tbh. Not sure if we could use reflection here to create this instance, I doubt it for any arbitrary operation though. With all that said, this is me assuming I wouldn’t care that the response has a different Operation instance from what the operation really was. If there’s some other issue with this, it’s already not a proper solution Actually, right as I posted this I was trying this, and it seems to work actually, again, as long as nobody accesses that response since it’s all TODO()s 😅 Doesn’t look super nice, but it may be something I can use at least on my project. Any thoughts?
Okay this looks like it’s working. Some tests that are green.
b
the
ApolloResponse
requires an instance of an operation in its Builder function.
I forgot about that 😅 Your solution looks good to me! Yes it's a bit 'hacky' to have the dummy operation, but it works 🙂
wondering if you could have
registerMapTestResponse<LaunchDetailsQuery>
instead of passing the
::class
. But not even sure it's actually better/clearer of an API.
s
Yeah I could see either API work, but as you said in general it just feels a bit hacky. Probably not something that you'd be interested to include in the actual library right? I could try and contribute it if yes, otherwise just gonna keep it for ourselves 😅
Also I am now wondering, why is it that the response keeps a reference to the Operation? What’s the story behind that, how is it used? If it’s not a super complicated answer (aka don’t want you to waste too much time answering) I’m curious to know
b
I could try and contribute it if yes
Yes I think it would be cool to have it! The only tricky part I think is naming 😅 . Since the existing one is called
MapTestNetworkTransport
which would also match what the new one does. Thinking out loud: maybe it would be possible to keep one
MapTestNetworkTransport
that does both? Keep 2 maps, one for instances, one for classes, and in
execute
try the instances one first, then fallback to the classes one?
why is it that the response keeps a reference to the Operation
I think it's just a convenience thing 🙂 Looking at our code, it is not really used specifically.
s
I was already thinking of the naming problem and seriously I couldn’t come up with anything reasonable 😅 Your idea sounds interesting yeah, I can try and make that happen with having the fallback as you described and see if that works! Will try to contribute it as soon as I find some time 🙌
🙏 1
b
very cool!
s
Also yeah, if it’s just for convenience then the dummy response doesn’t sound too bad. As long as it’s communicated for people using this type of testing API.
1