Testing your Apollo Links
If you are using Apollo Client you are using Apollo Links.
These powerful pieces of middleware / afterware allow you to augment requests in variety of ways or help with authentication.
While using Apollo Links is great, we as a responsible software developers should write tests for various pieces of our application to make sure everything is working as intended.
This rule also applies to Apollo Links.
In this article I'm going to go through testing basic authentication link to show you how I'm maintaining confidence in this part of the application I'm working on.
Please note that the references are based on Apollo 3.0 documentation. While there are many changes between versions 2.x and 3.x the ideas and APIs that will be used in this article are the same.
Basic authLink
We are going to follow official Apollo docs and start with the basic localStorage
based authentication link.
Here is the code:
import { ApolloLink } from "@apollo/client";
const authLink = new ApolloLink((operation, forward) => {
operation.setContext({
headers: {
Authorization: localStorage.getItem("token")
}
});
return forward(operation);
});
Nothing fancy here, we are retrieving the token from the localStore
and setting the Authorization
header using the setContext
API which is made to resemble the setState
API from React class components.
The function that I passed to ApolloLink
is the middleware itself. I'm using forward
to pass the control of the execution to the next link in the chain.
Now let's get going with the tests.
Setup for the tests
First of all we have to have a way to actually invoke our link, otherwise we will not be able to set the Authorization
header value.
Luckily, there is one utility which Apollo Client exposes that can help us with that, mainly a function called execute
. This function allows us to execute GraphQL
request and pass a link(s) which will be invoked during that execution.
Let's create a simple utility function which uses execute
to trigger the link chain during the request.
import { execute, gql } from "@apollo/client";
const MockQuery = gql`
query {
foo
}
`;
function executeRequest(link) {
execute(link, { query: MockQuery }).subscribe(() => {
/* not our concern within this test */
});
}
The query
itself does not matter, it is only there to trigger a request.
I've also had to subscribe
to the execute
function. This part is really an implementation detail that you should not concern yourself with.
For the curious minds out there, Apollo uses Observables
underneath. These are lazy by default so to trigger MockQuery
we had to use the subscribe
function.
Next step is to create another link which will be used for making assertions.
Since this will be the last link of the chain, it has to be a terminating link. This just means that we will not use forward(operation)
but return null
instead.
const assertLink = new ApolloLink(operation => {
const headers = operation.getContext().headers;
// we will make assertions here.
return null;
});
All we are doing here is getting the headers
from the context
and potentially making assertions on the headers
object.
The actual test
With every piece of the puzzle ready to be put together, let's write a test case which will make sure that the authLink
actually sets the Authorization
header.
function executeRequest(link) {
// previous implementation
}
// const authLink = ... previous implementation
it("sets the `Authorization` header to the correct value", () => {
// remember to reset the value in-between tests!
localStorage.setItem("token", "token");
const lastLink = new ApolloLink(operation => {
const headers = operation.getContext().headers;
expect(headers.Authorization).toEqual("token");
return null;
});
// compose our links together
// .concat API might be an alternative, but I will stick with `.from` here.
const link = ApolloLink.from([authLink, lastLink]);
executeRequest(link);
});
The test itself is not very sophisticated and it should not be.
All we are doing here is a simple assertion which gives us some confidence in how our authLink
is working.
While this is only one test case, in my opinion, it showcases the steps that you can take to test any kind of link that you might use.
Another thing to think about is how we could refresh this token
. We might be dealing with accessToken
and refreshToken
within our apps after all. Well, for that, stay tuned for my next article :)
Summary
I hope that with this article I was able to give you some insight on how you might test your custom Apollo Links. I'm using this technique for some time now and it's been working great, even for a bit more complicated links.
If you want to read more about testing itself, my colleagues have written some great articles in the past:
Jędrzej wrote about the challenges you might face while writing tests
And if you are into Go just like I am, there is this great article about testing lambdas written in Go.
Thanks 👋