Graphql-js subscriptions unit tests not working as expected - graphql

I have written integration tests for graphql-js subscriptions, which are showing weird behavior.
My graphq-js subscription works perfectly in GraphiQL. But when the same subscriptions is called from unit test, it fails.
Ggraphql-Js object, with resolve function and subscribe function
return {
type: outputType,
args: {
input: {type: new GraphQLNonNull(inputType)},
},
resolve(payload, args, context, info) {
const clientSubscriptionId = (payload) ? payload.subscriptionId : null;
const object = (payload) ? payload.object : null;
var where = null;
var type = null;
var target = null;
if (object) {
where = (payload) ? payload.object.where : null;
type = (payload) ? payload.object.type : null;
target = (payload) ? payload.object.target : null;
}
return Promise.resolve(subscribeAndGetPayload(payload, args, context, info))
.then(payload => ({
clientSubscriptionId, where, type, target, object: payload.data,
}));
},
subscribe: withFilter(
() => pubSub.asyncIterator(modelName),
(payload, variables, context, info) => {
const subscriptionPayload = {
clientSubscriptionId: variables.input.clientSubscriptionId,
remove: variables.input.remove,
create: variables.input.create,
update: variables.input.update,
opts: variables.input.options,
};
subscriptionPayload.model = model;
try {
pubSub.subscribe(info.fieldName, null, subscriptionPayload);
} catch (ex) {
console.log(ex);
}
return true;
}
),
};
Subscription query
subscription {
Customer(input: {create: true, clientSubscriptionId: 112}) {
customer {
id
name
age
}
}
}
Mutation query
mutation {
Customer {
CustomerCreate (input:{data:{name:"Atif 50", age:50}}) {
obj {
id
name
}
}
}
}
Integration Test
'use strict';
const ws = require('ws');
const { SubscriptionClient } = require('subscriptions-transport-ws');
const { ApolloClient } = require('apollo-client');
const { HttpLink } = require('apollo-link-http');
const { InMemoryCache } = require('apollo-cache-inmemory');
const Promise = require('bluebird');
const expect = require('chai').expect;
const chai = require('chai').use(require('chai-http'));
const server = require('../server/server');
const gql = require('graphql-tag');
let apollo;
let networkInterface;
const GRAPHQL_ENDPOINT = 'ws://localhost:5000/subscriptions';
describe('Subscription', () => {
before(async () => {
networkInterface = new SubscriptionClient(
GRAPHQL_ENDPOINT, { reconnect: true }, ws);
apollo = new ApolloClient({
networkInterface ,
link: new HttpLink({ uri: 'http://localhost:3000/graphql' }),
cache: new InMemoryCache()
});
});
after(done => {
networkInterface.close() ;
});
it('subscription', async () => {
const client = () => apollo;
// SUBSCRIBE and make a promise
const subscriptionPromise = new Promise((resolve, reject) => {
client().subscribe({
query: gql`
subscription {
Customer(input: {create: true,
clientSubscriptionId: 112,
options: {where: {age: 50}}}) {
customer {
name
}
}
}
`
}).subscribe({
next: resolve,
error: reject
});
});
let execGraphQL;
// MUTATE
await execGraphQL(
`mutation {
Customer {
CustomerCreate (input:{data:{name:"Atif 21", age:50}}) {
obj {
id
name
}
}
}
}`
);
// ASSERT SUBSCRIPTION RECEIVED EVENT
expect(await subscriptionPromise).to.deep.equal({});
});
});
Issue Here
When test in run, payload in the resolve function contains global data, where as it should contain the subscription payload. So the code breaks.

Related

apollo graphql network errors

I have tried to resolve this network errors but it has no possible and I do not know what is causing this. This is my apollo server index:
const mongoose = require('mongoose')
const {ObjectId} = require('mongodb')
const { createServer } = require('http')
const { execute, subscribe } = require('graphql')
const { SubscriptionServer } = require('subscriptions-transport-ws')
const { makeExecutableSchema } = require ('#graphql-tools/schema')
const express = require('express')
const { ApolloServer } = require('apollo-server-express')
const {resolvers,typeDefs} = require('./graphql')
const jwt = require('jsonwebtoken')
const {onConnect,onDisconnect} = require('./controllers/User')
require('dotenv').config()
const graphqlUploadExpress = require('graphql-upload/graphqlUploadExpress.js')
const { EventEmitter } = require('events')
const { PubSub } =require('graphql-subscriptions')
const { RedisPubSub } =require('graphql-redis-subscriptions')
const Redis = require('ioredis')
const
{
ApolloServerPluginDrainHttpServer,
ApolloServerPluginLandingPageGraphQLPlayground,
ApolloServerPluginLandingPageLocalDefault
} = require('apollo-server-core')
const { WebSocketServer} = require('ws')
const {useServer } = require('graphql-ws/lib/use/ws')
const path = require('path')
const bodyParser = require('body-parser')
const biggerEventEmitter = new EventEmitter();
biggerEventEmitter.setMaxListeners(0);
const options = {
host: process.env.REDIS_DOMAIN_NAME,
port: process.env.PORT_NUMBER,
password:process.env.REDIS_PASSWORD,
retryStrategy: times => {
// reconnect after
return Math.min(times * 50, 2000);
}
};
const pubsub = process.env.NODE_ENV === 'development' ? new PubSub({eventEmitter: biggerEventEmitter}) : new RedisPubSub({
publisher: new Redis(options),
subscriber: new Redis(options)
});
mongoose.connect(process.env.BBDD,
{},(err,_)=>
{
if(err)
{
console.log("Error de conexion")
}
else
{
console.log("Conexion Base de datos Correcta")
server()
}
})
async function server()
{
const app = express()
const httpServer = createServer(app)
const schema = makeExecutableSchema({ typeDefs, resolvers })
const PORT = process.env.APP_PORT
const getDynamicContext = async (ctx, msg, args) => {
if (ctx.connectionParams.authToken) {
const user = jwt.verify(ctx.connectionParams.authToken.replace("Bearer ", ""),process.env.KEY);
return { user, pubsub};
}
return { user: null };
};
const wsServer = new WebSocketServer(
{
server: httpServer,
path: '/graphql'
})
const serverCleanup = useServer({
schema,
context: (ctx, msg, args) => {
return getDynamicContext(ctx, msg, args)
},
onConnect: async (ctx) => {
console.log('onConnect');
let connectionParams = ctx.connectionParams
try
{
if (connectionParams.authToken)
{
const user = jwt.verify(connectionParams.authToken.replace("Bearer ", ""),process.env.KEY)
await onConnect(user.id,pubsub)
return { user , pubsub}
}
}
catch(error)
{
throw new Error('Missing auth token!')
}
},
async onDisconnect(context)
{
console.log('onDisconnect');
try
{
if(context.connectionParams&&context.connectionParams.authToken)
{
const user = jwt.verify(context.connectionParams.authToken.replace("Bearer ", ""),process.env.KEY)
await onDisconnect(user.id,pubsub)
}
}
catch(error)
{
/* throw new Error('Missing context!') */
}
}, }, wsServer)
const server = new ApolloServer(
{
schema,
persistedQueries:false,
context: async ({ req ,connection }) =>
{
let authorization = req.headers.authorization
try
{
if(authorization)
{
user = jwt.verify(authorization.replace("Bearer ", ""),process.env.KEY)
return{
user,
pubsub
}
}
}
catch (error)
{
throw new Error("Token invalido");
}
},
csrfPrevention: true,
cache: 'bounded',
plugins: [ApolloServerPluginDrainHttpServer({ httpServer }),
ApolloServerPluginLandingPageLocalDefault({ embed: true }),
{
async serverWillStart() {
return {
async drainServer() {
await serverCleanup.dispose();
},
};
},
}]
})
app.use(graphqlUploadExpress())
await server.start()
server.applyMiddleware({ app })
httpServer.listen(process.env.PORT||4000, () => {
console.log(
`Server is now running on http://localhost:${process.env.PORT||4000}${server.graphqlPath}`,
);
});
}
And this is my apollo client config:
import {ApolloClient,createHttpLink,defaultDataIdFromObject,InMemoryCache,split} from '#apollo/client'
import {WebSocketLink} from '#apollo/client/link/ws'
import { getMainDefinition } from '#apollo/client/utilities'
import { setContext } from "apollo-link-context"
import {createUploadLink} from 'apollo-upload-client'
import {getToken, getUpdatedTokenApi} from '../utils/auth'
import { GraphQLWsLink } from '#apollo/client/link/subscriptions';
import { createClient } from 'graphql-ws';
import { from, HttpLink } from '#apollo/client';
import { RetryLink } from '#apollo/client/link/retry';
import { disableFragmentWarnings } from 'graphql-tag';
disableFragmentWarnings()
const type = () =>
{
if(window.location.pathname.startsWith(`/${process.env.ADMIN_DEV}`))
{
return 'admin'
}
else
{
return null
}
}
const link = (e)=>
{
switch (e) {
case 1:
return 'https://unglo.herokuapp.com/graphql'
case 2:
return 'http://localhost:4000/graphql'
default:
break;
}
}
const ws_Link = (e)=>
{
switch (e) {
case 1:
return 'wss://unglo.herokuapp.com/graphql'
case 2:
return 'ws://localhost:4000/graphql'
default:
break;
}
}
import { onError } from "#apollo/client/link/error";
// Log any GraphQL errors or network error that occurred
const errorLink = onError(({ graphQLErrors, networkError }) => {
if (graphQLErrors)
graphQLErrors.forEach(({ message, locations, path }) =>
console.log(
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`
)
);
if (networkError) console.log(`[Network error]: ${networkError}`);
});
const token = getToken(type())
const updatedToken = getUpdatedTokenApi()
const httpLink = new createUploadLink({
uri: link(2),
headers: {
'Apollo-Require-Preflight': 'true',
},
});
const wsLink = new GraphQLWsLink(createClient(
{
url: ws_Link(2),
connectionParams:
{
authToken: token ? `Bearer ${token}` : updatedToken ? `Bearer ${updatedToken}` : "",
},
lazy:true,
}
))
const splitLink = split(
({ query }) => {
const definition = getMainDefinition(query);
return (
definition.kind === 'OperationDefinition' &&
definition.operation === 'subscription'
);
},
wsLink,
httpLink,
errorLink
);
const authMiddleware = setContext((_, { headers }) =>
{
return {
headers:
{
...headers,
Authorization: token ? `Bearer ${token}` :updatedToken ? `Bearer ${updatedToken}` : "",
},
}
})
const client = new ApolloClient
({
ssrMode: true,
connectToDevTools:true,
cache: new InMemoryCache({
}),
link:authMiddleware.concat(splitLink),
})
export default client
And these are errors:
I have tried removing all apollo cliente queries and errors persists and google console dont show error details
Please if any body know about this kind of errors it shoud be helpfull

Modify value inside HOC on NextJS

I've been working on a way to set up authentication and authorization for my NextJS app, so far it was pretty easy but I've hit a wall.
I have a value that lives and is watched on a context, and I have a HOC that I need for my NextJS app to be able to use hooks with GraphQl, the issues is that I don't think I can call the context and use the value from a HOC, since it is simply not allowed.
Is there a way I can dynamically change the value on the HOC so that when the user logs in, I can then update the HOC to have the proper access token?
Some context: the user is first anonymous, whenever he/she logs in, I get an auth state change from Firebase from which I can extract the access token and add it to any future requests. But the point of the hoc is to provide next with full Graphql capabilities, the thing is that I need that hoc go listen for changes on a context state.
This is the Connection Builder:
import {
ApolloClient,
InMemoryCache,
HttpLink,
NormalizedCacheObject,
} from "#apollo/client";
import { WebSocketLink } from "#apollo/client/link/ws";
import { SubscriptionClient } from "subscriptions-transport-ws";
const connectionString = process.env.HASURA_GRAPHQL_API_URL || "";
const createHttpLink = (authState: string, authToken: string) => {
const isIn = authState === "in";
const httpLink = new HttpLink({
uri: `https${connectionString}`,
headers: {
// "X-hasura-admin-secret": `https${connectionString}`,
lang: "en",
"content-type": "application/json",
Authorization: isIn && `Bearer ${authToken}`,
},
});
return httpLink;
};
const createWSLink = (authState: string, authToken: string) => {
const isIn = authState === "in";
return new WebSocketLink(
new SubscriptionClient(`wss${connectionString}`, {
lazy: true,
reconnect: true,
connectionParams: async () => {
return {
headers: {
// "X-hasura-admin-secret": process.env.HASURA_GRAPHQL_ADMIN_SECRET,
lang: "en",
"content-type": "application/json",
Authorization: isIn && `Bearer ${authToken}`,
},
};
},
})
);
};
export default function createApolloClient(
initialState: NormalizedCacheObject,
authState: string,
authToken: string
) {
const ssrMode = typeof window === "undefined";
let link;
if (ssrMode) {
link = createHttpLink(authState, authToken);
} else {
link = createWSLink(authState, authToken);
}
return new ApolloClient({
ssrMode,
link,
cache: new InMemoryCache().restore(initialState),
});
}
This is the context:
import { useState, useEffect, createContext, useContext } from "react";
import { getDatabase, ref, set, onValue } from "firebase/database";
import { useFirebase } from "./use-firebase";
import { useGetUser } from "../hooks/use-get-user";
import { getUser_Users_by_pk } from "../types/generated/getUser";
import { getApp } from "firebase/app";
const FirebaseAuthContext = createContext<FirebaseAuthContextProps>({
authUser: null,
authState: "",
authToken: null,
currentUser: undefined,
loading: true,
login: () => Promise.resolve(undefined),
registerUser: () => Promise.resolve(undefined),
loginWithGoogle: () => Promise.resolve(undefined),
loginWithMicrosoft: () => Promise.resolve(undefined),
});
export const FirebaseAuthContextProvider: React.FC = ({ children }) => {
const [loading, setLoading] = useState<boolean>(true);
const [authUser, setAuthUser] = useState<User | null>(null);
const { data } = useGetUser(authUser?.uid || "");
const [authState, setAuthState] = useState("loading");
const [authToken, setAuthToken] = useState<string | null>(null);
const currentUser = data?.Users_by_pk;
// ...
const authStateChanged = async (user: User | null) => {
if (!user) {
setAuthUser(null);
setLoading(false);
setAuthState("out");
return;
}
const token = await user.getIdToken();
const idTokenResult = await user.getIdTokenResult();
const hasuraClaim = idTokenResult.claims["https://hasura.io/jwt/claims"];
if (hasuraClaim) {
setAuthState("in");
setAuthToken(token);
setAuthUser(user);
} else {
// Check if refresh is required.
const metadataRef = ref(
getDatabase(getApp()),
"metadata/" + user.uid + "/refreshTime"
);
onValue(metadataRef, async (data) => {
if (!data.exists) return;
const token = await user.getIdToken(true);
setAuthState("in");
setAuthUser(user);
setAuthToken(token);
});
}
};
useEffect(() => {
const unsubscribe = getAuth().onAuthStateChanged(authStateChanged);
return () => unsubscribe();
}, []);
const contextValue: FirebaseAuthContextProps = {
authUser,
authState,
authToken,
currentUser,
loading,
login,
registerUser,
loginWithGoogle,
loginWithMicrosoft,
};
return (
<FirebaseAuthContext.Provider value={contextValue}>
{children}
</FirebaseAuthContext.Provider>
);
};
export const useFirebaseAuth = () =>
useContext<FirebaseAuthContextProps>(FirebaseAuthContext);
This is the HOC:
export const withApollo =
({ ssr = true } = {}) =>
(PageComponent: NextComponentType<NextPageContext, any, {}>) => {
const WithApollo = ({
apolloClient,
apolloState,
...pageProps
}: {
apolloClient: ApolloClient<NormalizedCacheObject>;
apolloState: NormalizedCacheObject;
}) => {
let client;
if (apolloClient) {
// Happens on: getDataFromTree & next.js ssr
client = apolloClient;
} else {
// Happens on: next.js csr
// client = initApolloClient(apolloState, undefined);
client = initApolloClient(apolloState);
}
return (
<ApolloProvider client={client}>
<PageComponent {...pageProps} />
</ApolloProvider>
);
};
const initApolloClient = (initialState: NormalizedCacheObject) => {
// Make sure to create a new client for every server-side request so that data
// isn't shared between connections (which would be bad)
if (typeof window === "undefined") {
return createApolloClient(initialState, "", "");
}
// Reuse client on the client-side
if (!globalApolloClient) {
globalApolloClient = createApolloClient(initialState, "", "");
}
return globalApolloClient;
};
I fixed it by using this whenever I have an update on the token:
import { setContext } from "#apollo/client/link/context";
const authStateChanged = async (user: User | null) => {
if (!user) {
setAuthUser(null);
setLoading(false);
setAuthState("out");
return;
}
setAuthUser(user);
const token = await user.getIdToken();
const idTokenResult = await user.getIdTokenResult();
const hasuraClaim = idTokenResult.claims["hasura"];
if (hasuraClaim) {
setAuthState("in");
setAuthToken(token);
// THIS IS THE FIX
setContext(() => ({
headers: { Authorization: `Bearer ${token}` },
}));
} else {
// Check if refresh is required.
const metadataRef = ref(
getDatabase(getApp()),
"metadata/" + user.uid + "/refreshTime"
);
onValue(metadataRef, async (data) => {
if (!data.exists) return;
const token = await user.getIdToken(true);
setAuthState("in");
setAuthToken(token);
// THIS IS THE FIX
setContext(() => ({
headers: { Authorization: `Bearer ${token}` },
}));
});
}
};

React Hook useEffect has some missing dependencies

I tried to run a program, but I got this warning message: Line 75:8: React Hook useEffect has missing dependencies: 'client' and 'loading'. Either include them or remove the dependency array react-hooks/exhaustive-deps
This is my Code
const UserList = ({ setSelectedUsers }) => {
const { client } = useChatContext();
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(false);
const [listEmpty, setListEmpty] = useState(false);
const [error, setError] = useState(false);
useEffect(() => {
const getUsers = async () => {
if(loading) return;
setLoading(true);
try {
const response = await client.queryUsers(
{ id: { $ne: client.userID } },
{ id: 1 },
{ limit: 8 }
);
if(response.users.length) {
setUsers(response.users);
} else {
setListEmpty(true);
}
} catch (error) {
setError(true);
}
setLoading(false);
}
if(client) getUsers()
}, []);
You can get rid of the warning by // eslint-disable-next-line react-hooks/exhaustive-deps above the useEffect dependency. useEffect sometimes suggests useless dependencies that should not be actually added in dependency array
useEffect(() => {
const getUsers = async () => {
if(loading) return;
setLoading(true);
try {
const response = await client.queryUsers(
{ id: { $ne: client.userID } },
{ id: 1 },
{ limit: 8 }
);
if(response.users.length) {
setUsers(response.users);
} else {
setListEmpty(true);
}
} catch (error) {
setError(true);
}
setLoading(false);
}
if(client) getUsers()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);```

Firebase Function Returns Before All Callback functions complete execution

I'm using the Google Storage NodeJS client library to list GCS Bucket paths.
Here's the code to the Firebase Function:
import * as functions from 'firebase-functions';
import { Storage } from '#google-cloud/storage';
import { globVars } from '../admin/admin';
const projectId = process.env.GCLOUD_PROJECT;
// shared global variables setup
const { keyFilename } = globVars;
// Storage set up
const storage = new Storage({
projectId,
keyFilename,
});
export const gcsListPath = functions
.region('europe-west2')
.runWith({ timeoutSeconds: 540, memory: '256MB' })
.https.onCall(async (data, context) => {
if (context.auth?.token.email_verified) {
const { bucketName, prefix, pathList = false, fileList = false } = data;
let list;
const options = {
autoPaginate: false,
delimiter: '',
prefix,
};
if (pathList) {
options.delimiter = '/';
let test: any[] = [];
const callback = (_err: any, _files: any, nextQuery: any, apiResponse: any) => {
test = test.concat(apiResponse.prefixes);
console.log('test : ', test);
console.log('nextQuery : ', nextQuery);
if (nextQuery) {
storage.bucket(bucketName).getFiles(nextQuery, callback);
} else {
// prefixes = The finished array of prefixes.
list = test;
}
}
storage.bucket(bucketName).getFiles(options, callback);
}
if (fileList) {
const [files] = await storage
.bucket(bucketName)
.getFiles(options);
list = files.map((file) => file.name);
}
return { list }; //returning null as it exec before callback fns finish
} else {
return {
error: { message: 'Bad Request', status: 'INVALID_ARGUMENT' },
};
}
});
My problem is that my Firebase function returns the list (null) before all the callback functions finish execution.
Could someone spot and point out what needs to be changed/added to make the function wait for all the callback functions to finish. I've tried adding async/await but can't seem to get it right.
The reason for your error is that you use a callback. It's not awaited in the code. I would recommend to turn the callback code to a promise. Something like this.
import * as functions from "firebase-functions";
import { Storage } from "#google-cloud/storage";
import { globVars } from "../admin/admin";
const projectId = process.env.GCLOUD_PROJECT;
// shared global variables setup
const { keyFilename } = globVars;
// Storage set up
const storage = new Storage({
projectId,
keyFilename,
});
const getList = (bucketName, options) => {
return new Promise((resolve, reject) => {
let list;
let test: any[] = [];
const callback = (
_err: any,
_files: any,
nextQuery: any,
apiResponse: any
) => {
test = test.concat(apiResponse.prefixes);
console.log("test : ", test);
console.log("nextQuery : ", nextQuery);
if (nextQuery) {
storage.bucket(bucketName).getFiles(nextQuery, callback);
} else {
// prefixes = The finished array of prefixes.
list = test;
}
resolve(list);
};
try {
storage.bucket(bucketName).getFiles(options, callback);
} catch (error) {
reject(eror);
}
});
};
export const gcsListPath = functions
.region("europe-west2")
.runWith({ timeoutSeconds: 540, memory: "256MB" })
.https.onCall(async (data, context) => {
if (context.auth?.token.email_verified) {
const { bucketName, prefix, pathList = false, fileList = false } = data;
let list;
const options = {
autoPaginate: false,
delimiter: "",
prefix,
};
if (pathList) {
options.delimiter = "/";
list = await getList(bucketName, options);
}
if (fileList) {
const [files] = await storage.bucket(bucketName).getFiles(options);
list = files.map((file) => file.name);
}
return { list }; //returning null as it exec before callback fns finish
} else {
return {
error: { message: "Bad Request", status: "INVALID_ARGUMENT" },
};
}
});
I'm not sure if the part with fileList will work as expectedt. It looks like the API doesn't support await but only callbacks.
import * as functions from "firebase-functions";
import { GetFilesOptions, Storage } from "#google-cloud/storage";
import { globVars } from "../admin/admin";
const projectId = process.env.GCLOUD_PROJECT;
// shared global variables setup
const { keyFilename } = globVars;
// Storage set up
const storage = new Storage({
projectId,
keyFilename,
});
const getList = (bucketName: string, options: GetFilesOptions) => {
return new Promise((resolve, reject) => {
// let test: any[] = [];
let list: any[] = [];
const callback = (
_err: any,
_files: any,
nextQuery: any,
apiResponse: any
) => {
list = list.concat(apiResponse.prefixes);
console.log("list : ", list);
console.log("nextQuery : ", nextQuery);
if (nextQuery) {
storage.bucket(bucketName).getFiles(nextQuery, callback);
} else {
// prefixes = The finished array of prefixes.
resolve(list);
}
};
try {
storage.bucket(bucketName).getFiles(options, callback);
} catch (error) {
reject(error);
}
});
};
export const gcsListPath = functions
.region("europe-west2")
.runWith({ timeoutSeconds: 540, memory: "256MB" })
.https.onCall(async (data, context) => {
if (context.auth?.token.email_verified) {
const { bucketName, prefix, pathList = false, fileList = false } = data;
let list;
const options = {
autoPaginate: false,
delimiter: "",
prefix,
};
if (pathList) {
options.delimiter = "/";
list = await getList(bucketName, options);
}
if (fileList) {
const [files] = await storage.bucket(bucketName).getFiles(options);
list = files.map((file) => file.name);
}
return { list }; //returning null as it exec before callback fns finish
} else {
return {
error: { message: "Bad Request", status: "INVALID_ARGUMENT" },
};
}
});

How to Unit Test Graphql Resolver functions created using apollo-resolvers

I have created resolvers(userresolver.js) using 'apollo-resolvers' npm module as below.
import { createResolver } from 'apollo-resolvers';
import { isInstance } from 'apollo-errors';
const baseResolver = createResolver(
null,
(root, args, context, error) => isInstance(error) ? error : new UnknownError()
);
const users = baseResolver.createResolver(
(parent, args, { models, me } ) => {
return Object.values(models.users);
}
);
export default {
Query: {
users
}
}
;
These also work fine when I test the queries after starting the server.
I now want to do unit testing of the resolver functions.
I am not sure how to do that. Can someone help me on how to unit test the resolver functions. I am using mocha with chai for testing my project.
You can try easygraphql-tester, it has a method that'll help you to test the resolvers.
Here is the documentation of it.
Example:
Resolver
"use strict";
const license = (__, args, ctx) => {
const { key } = args;
return {
id: "1234",
body: "This is a test license",
description: `This is a description with key ${key}`
};
};
module.exports = {
Query: {
license
}
};
Test
"use strict";
const fs = require("fs");
const path = require("path");
const { expect } = require("chai");
const EasyGraphQLTester = require("easygraphql-tester");
const resolvers = require("../resolvers");
const schemaCode = fs.readFileSync(
path.join(__dirname, "..", "schema.gql"),
"utf8"
);
describe("Test resolvers", () => {
let tester;
beforeAll(() => {
tester = new EasyGraphQLTester(schemaCode, resolvers);
});
it("should return expected values", async () => {
const query = `
query GET_LICENSE($key: String!) {
license(key: $key) {
id
body
description
}
}
`;
const args = {
key: "1234"
};
const result = await tester.graphql(query, {}, {}, args);
expect(result.data.license.id).to.be.eq("1234");
expect(result.data.license.body).to.be.eq("This is a test license");
expect(result.data.license.description).to.be.eq(
`This is a description with key ${args.key}`
);
});
});

Resources