【问题标题】:How to test a GraphQL query providing an auth token with Apollo Server?如何使用 Apollo Server 测试提供身份验证令牌的 GraphQL 查询?
【发布时间】:2019-07-22 12:18:47
【问题描述】:

Apollo Server Integration testing documentation 展示了如何使用createTestClient 测试简单查询:

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

为了测试需要授权令牌的查询,我尝试将jwtToken 作为额外字段传递给传递给查询的对象,如下所示:

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

不幸的是,这不起作用。尝试运行它时,我得到以下响应:

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

知道如何正确测试需要授权令牌的查询吗?

使用:

  "devDependencies": {
    "jest": "^23.6.0",
    "apollo-server-testing": "^2.4.8"
  }

【问题讨论】:

    标签: typescript graphql jestjs apollo-server


    【解决方案1】:

    我遇到了同样的问题并找到了这个库:

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

    来自文档: 为什么不使用 apollo-server-testing? 您不能真正使用 apollo-server-testing 编写真正的集成测试,因为它不支持依赖于 context 选项作为使用 req 对象的函数的服务器

    【讨论】:

      【解决方案2】:

      Mikael's solution 为我工作。为了对此进行扩展,我能够导入我在主 app.js 服务器文件中使用的同一个 ApolloServer,然后在测试中覆盖 context 属性以添加所需的不记名令牌。

      // 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;
      

      然后在我的测试中覆盖上下文看起来像这样。

      // 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({...})
      

      【讨论】:

      • 如果您的上下文仅包含如何从任何位置获取令牌,您将身份验证过程放在哪里?
      • 这不会验证传入的请求。此处的目的是在我们从 GraphQL API 发送到外部 REST API(或其他数据源)的请求中设置授权标头。在我的示例中,我使用与我接收的请求相同的令牌,并在我发送到外部 REST API 的请求中传递它。我希望这有助于使其更加清晰。
      • 我的问题应该更清楚,但无论如何你已经回答了。我的观点是,如果上下文仅处理提取,则身份验证将在每个解析器中进行,这很痛苦。但是考虑到你有一个外部 API,我猜你在这个 API 的中间件中处理它。
      • 它没有解决问题,他想在请求中传递令牌以编写集成测试而不模拟数据
      【解决方案3】:

      您可以为您的测试创建一个具有单独上下文的新服务器实例。像这样的东西(在语法上可能不是 100% 正确,但你会明白的):

      // 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);
        },
      });
      

      【讨论】:

        猜你喜欢
        • 2018-08-18
        • 2016-12-17
        • 1970-01-01
        • 2019-02-25
        • 2020-01-18
        • 2016-05-08
        • 2012-04-11
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多