9

Apollo Server Integration testing documentation shows how to test simple queries using createTestClient:

const { query } = createTestClient(server);
const res = await query({ query: GET_LAUNCH, variables: { id: 1 } });

In the attempt to test a query which requires an authorization token, I have tried passing the jwtToken as an extra field to the object passed to query like below:

const res = await query({ 
  query: MY_QUERY, 
  http: { headers: { authorization: `Bearer ${jwtToken}` } },
});

Unfortunately this doesn't work. When trying to run it I get the following response:

{
  "http": {
    "headers": {}
  },
  "errors": [{
    "message": "must authenticate",
    "locations": [{
      "line": 2,
      "column": 3
    }],
    "path": ["myQuery"],
    "extensions": {
      "code": "UNAUTHENTICATED"
    }
  }],
  "data": {
    "myQuery": null
  }
}

Any idea how to properly test queries which require an authorization token?

Using:

  "devDependencies": {
    "jest": "^23.6.0",
    "apollo-server-testing": "^2.4.8"
  }
Marco Lackovic
  • 6,077
  • 7
  • 55
  • 56

3 Answers3

4

Mikael's solution worked for me. To expand on that, I was able to import my same ApolloServer that I'm using in the main app.js server file and then overwrite the context property in the test to add the required bearer token.

// apollo-server.js
const { ApolloServer } = require('apollo-server-express');

const resolvers = require('./resolvers');
const typeDefs = require('./schema');

const apolloServer = new ApolloServer({
  typeDefs,
  resolvers,
  context: ({ req }) => ({ bearerToken: req.headers.authorization }),
});

module.exports = apolloServer;

Then overwriting the context in my test looked like this.

// graphql.test.js
const apolloServer = require('./apollo-server');

apolloServer.context = () => ({ bearerToken: `Bearer <token>` });

const { query } = createTestClient(apolloServer);

const QUERY = gql`
  // ...
`;

const response = await query({ query: QUERY });
expect(response.data).eql({...})
Calaway
  • 714
  • 6
  • 12
  • Where do you put your authentication process if your context only contains how to fetch the token from wherever it is? – Emmanuel Hadoux Jan 14 '20 at 10:30
  • This does not authenticate the incoming request. The purpose here is to set an authorization header in the request we're sending from the GraphQL API to the external REST API (or other datasource). In my example I'm using the same token from the request I'm receiving and passing it though in the request I'm sending to the external REST API. I hope that helps make it a bit more clear. – Calaway Jan 15 '20 at 16:10
  • My question should have been clearer but you've answered it anyway. My point was that if the context only deals with the extraction, the authentication would be in every resolver, which is a pain. But given that you have an external API, I guess you deal with it in the middlewares of this API. – Emmanuel Hadoux Jan 16 '20 at 11:53
  • it does not solve the issue, he wants to pass the token in the request to write an integration test without mocking data – Rafael Zeffa Dec 10 '20 at 18:20
3

You can create a new server instance with a separate context for your tests. Something like this (may not be 100% correct syntactically, but you'll get the idea):

// Create a test user, no need to mess with JWT here, just create a plain object
// that resembles a user in your system.
const testUser = {
  id: 'testId',
  email: 'test@example.com',
};

// Use this for both test servers and real servers
export function getContext(models, viewer) {
  return {
    ...models,
    viewer,
  };
}

// Create test server
const testServer = new ApolloServer({
  typeDefs,
  resolvers,
  context: getContext({}, testUser),
});

const { query } = createTestClient(testServer);

// The actual server (you know better how this looks on your end, but just for
// the concept)
const realServer = new ApolloServer({
  typeDefs,
  resolvers,
  context: ({ req, res }) => {
    const user = decodeUser(req.headers.authorization);

    return getContext({}, user);
  },
});
Mikael Lirbank
  • 4,355
  • 2
  • 28
  • 25
2

I was facing the same issue and found this library:

https://github.com/zapier/apollo-server-integration-testing

From the docs: Why not use apollo-server-testing? You can't really write real integration tests with apollo-server-testing, because it doesn't support servers which rely on the context option being a function that uses the req object

Rafael Zeffa
  • 2,334
  • 22
  • 20