If you are using Apollo Client, you are using Apollo Links.These powerful pieces of middleware / afterware allow you to augment requests in a variety of ways or help with
I’m going to go through testing a basic
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.
authLink
We are going to follow thelocalStorage
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 just retrieving the token from the localStore
and setting the Authorization
header using the setContext
API that 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.
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 Apollo Client exposes that can help us with that, a function called execute
. This function allows us to execute GraphQL
requests and pass a link(s) that will be invoked during that execution.
Let’s create a simple utility function that 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 triggerMockQuery
, we had to use thesubscribe
function.
The next step is to create another link that 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 the forward(operation)
and will 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.
With every piece of the puzzle ready to be put together, let’s write a test case that makes sure the authLink
actually sets the Authorization
header.
function executeRequest(link) {// previous implementation}// const authLink = ... previous implementationit("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, but it shouldn’t be;all we’re doing here is a simple assertion that gives us some confidence in how our authLink
is working.
While this is only one test case, in my opinion, it showcases the steps you can take to test any kind of link you might use.