模拟
根据模式模拟您的 GraphQL 数据。
📣 Apollo Server 4 新功能: Apollo Server 4 移除了mocks
和 mockEntireSchema
构造函数选项。本文已更新,使用 @graphql-tools
包模拟 Apollo Server 的数据。关于 @graphql-tools
的最新信息,建议参考 他们的文档。
模拟允许 Apollo Server 返回基于您服务器模式的模拟数据,用于 GraphQL 操作。GraphQL API 强类型的特点使其易于模拟,这是 GraphQL 开发过程的重要部分。
模拟允许前端开发者在无需等待完整的后端实现的情况下构建和测试 UI 组件和功能。在像 Storybook 这样的 UI 工具中使用,非常有价值,因为您不需要启动真正的 GraphQL 服务器。
启用模拟
要从您的设计开始模拟数据,请先安装到您的开发依赖项中的 @graphql-tools/mock
和 @graphql-tools/schema
包:
npm install --save-dev @graphql-tools/mock @graphql-tools/schema
您可以将addMocksToSchema
(来自@graphql-tools/mock
)和makeExecutableSchema
(来自@graphql-tools/schema
)结合使用,为您的服务器模式中的每个字段提供模拟数据:
import { ApolloServer } from '@apollo/server';import { addMocksToSchema } from '@graphql-tools/mock';import { makeExecutableSchema } from '@graphql-tools/schema';import { typeDefs } from './schema';import { resolvers } from './resolvers';new ApolloServer({// addMocksToSchema accepts a schema instance and provides// mocked data for each field in the schemaschema: addMocksToSchema({schema: makeExecutableSchema({ typeDefs, resolvers }),}),});
import { ApolloServer } from '@apollo/server';import { addMocksToSchema } from '@graphql-tools/mock';import { makeExecutableSchema } from '@graphql-tools/schema';import { typeDefs } from './schema';import { resolvers } from './resolvers';new ApolloServer({// addMocksToSchema accepts a schema instance and provides// mocked data for each field in the schemaschema: addMocksToSchema({schema: makeExecutableSchema({ typeDefs, resolvers }),}),});
模拟逻辑查看每个模式字段的返回类型,并为此类型返回一个默认值。
下表概述了默认的标量类型和每个类型返回的默认模拟值:
类型 | 默认模拟值 |
---|---|
| 返回一个随机正或负整数。 |
| 返回 |
| 返回一个随机正数或负数的双精度浮点数值。 |
| 随机返回 |
| 返回一个随机生成的UUID,包含整数和字母的组合。 |
当使用模拟时,您不必指定resolvers
。默认情况下,启用模拟时,您指定的任何resolvers
将被忽略。要配置此行为,请参阅使用模拟与现有解析器。
注意:如果typeDefs
有任何自定义标量类型,您需要指定服务器为这些类型应返回什么。您可以通过为每个自定义标量类型创建一个自定义模拟来实现这一点,具体说明如下。
自定义模拟
为了更复杂的测试,您可以自定义模拟以返回用户指定的数据。您可以通过提供一个对象来自定义模拟,该对象指定了不同返回类型应返回的值。
默认情况下,您在mocks
中定义的函数将优先于任何当前定义的resolvers
。
例如,以下示例中,Query.hello
和Query.resolved
都返回Hello
:
import { ApolloServer } from '@apollo/server';import { startStandaloneServer } from '@apollo/server/standalone';import { addMocksToSchema } from '@graphql-tools/mock';import { makeExecutableSchema } from '@graphql-tools/schema';const typeDefs = `#graphqltype Query {hello: Stringresolved: String}`;const resolvers = {Query: {resolved: () => 'Resolved',},};const mocks = {Int: () => 6,Float: () => 22.1,String: () => 'Hello',};const server = new ApolloServer({schema: addMocksToSchema({schema: makeExecutableSchema({ typeDefs, resolvers }),mocks,}),});const { url } = await startStandaloneServer(server, { listen: { port: 4000 } });console.log(`🚀 Server listening at: ${url}`);
import { ApolloServer } from '@apollo/server';import { startStandaloneServer } from '@apollo/server/standalone';import { addMocksToSchema } from '@graphql-tools/mock';import { makeExecutableSchema } from '@graphql-tools/schema';const typeDefs = `#graphqltype Query {hello: Stringresolved: String}`;const resolvers = {Query: {resolved: () => 'Resolved',},};const mocks = {Int: () => 6,Float: () => 22.1,String: () => 'Hello',};const server = new ApolloServer({schema: addMocksToSchema({schema: makeExecutableSchema({ typeDefs, resolvers }),mocks,}),});const { url } = await startStandaloneServer(server, { listen: { port: 4000 } });console.log(`🚀 Server listening at: ${url}`);
您也可以使用mocks
来定义对象类型以及属于这些对象类型的字段(类似于解析器映射)。在以下示例中,我们的模拟Person
对象调用一个返回包含字段的其他函数的对象
// importing the casual libraryconst casual = require('casual');const mocks = {Person: () => ({name: casual.name,age: () => casual.integer(0, 120),}),};
// importing the casual libraryconst casual = require('casual');const mocks = {Person: () => ({name: casual.name,age: () => casual.integer(0, 120),}),};
此示例使用 casual,这是一个JavaScript的假数据生成器,每次函数被调用时都会返回不同的结果。在其他场景中,如测试,通常需要一组假对象或始终使用一致种子的生成器来提供一致的数据。
在mocks中使用列表
要自动化模拟列表,返回所需长度的数组。使用 [...new Array(n)]
是创建包含 n 个 undefined
复制品的数组的便捷语法。
import casual from 'casual';const mocks = {Person: () => ({// a list of length between 2 and 6, using the "casual" npm module// to generate a random integerfriends: [...new Array(casual.integer(2, 6))],// a list of three lists of two items: [[1, 1], [2, 2], [3, 3]]listOfLists: () => [...new Array(3)].map((i) => [...new Array(2)]),}),};
import casual from 'casual';const mocks = {Person: () => ({// a list of length between 2 and 6, using the "casual" npm module// to generate a random integerfriends: [...new Array(casual.integer(2, 6))],// a list of three lists of two items: [[1, 1], [2, 2], [3, 3]]listOfLists: () => [...new Array(3)].map((i) => [...new Array(2)]),}),};
使用mocks中的现有解析器
默认情况下,mocks会覆盖您的服务器上的现有 resolvers。要使用mocking时同时使用服务器上的解析器,将 makeExecutableSchema
函数的 preserveResolvers
选项设置为 true
:
import { ApolloServer } from '@apollo/server';import { startStandaloneServer } from '@apollo/server/standalone';import { addMocksToSchema } from '@graphql-tools/mock';import { makeExecutableSchema } from '@graphql-tools/schema';const typeDefs = `#graphqltype Query {hello: Stringresolved: String}`;const resolvers = {Query: {resolved: () => 'Resolved',},};const mocks = {Int: () => 6,Float: () => 22.1,String: () => 'Hello',};const server = new ApolloServer({schema: addMocksToSchema({schema: makeExecutableSchema({ typeDefs, resolvers }),mocks,preserveResolvers: true,}),});const { url } = await startStandaloneServer(server, { listen: { port: 4000 } });console.log(`🚀 Server listening at: ${url}`);
import { ApolloServer } from '@apollo/server';import { startStandaloneServer } from '@apollo/server/standalone';import { addMocksToSchema } from '@graphql-tools/mock';import { makeExecutableSchema } from '@graphql-tools/schema';const typeDefs = `#graphqltype Query {hello: Stringresolved: String}`;const resolvers = {Query: {resolved: () => 'Resolved',},};const mocks = {Int: () => 6,Float: () => 22.1,String: () => 'Hello',};const server = new ApolloServer({schema: addMocksToSchema({schema: makeExecutableSchema({ typeDefs, resolvers }),mocks,preserveResolvers: true,}),});const { url } = await startStandaloneServer(server, { listen: { port: 4000 } });console.log(`🚀 Server listening at: ${url}`);
如上所示,resolved
查询现在使用其定义的 resolver,因此它返回字符串 Resolved
。