Testing your Apollo Links

Wojciech Matuszewski - Jul 7 '20 - - Dev Community

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);
});
Enter fullscreen mode Exit fullscreen mode

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 */
  });
}
Enter fullscreen mode Exit fullscreen mode

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;
});
Enter fullscreen mode Exit fullscreen mode

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);
});
Enter fullscreen mode Exit fullscreen mode

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:

Thanks 👋

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .