How to mock Prisma (via Proxy) in Jest 27 - jestjs

Our Prisma setup is as follows:
// server/database/prisma.ts
import { Prisma } from 'database/generated/prisma-client';
const client = new Prisma({
endpoint: PRISMA_ENDPOINT,
secret: PRISMA_SECRET,
debug: PRISMA_DEBUG,
});
// Add a proxy 'middleware' to the Prisma client to record query execution time
const prismaProxy = new Proxy(client, {
get(client, endpoint) {
const endTimer = metrics.prisma_call_duration_seconds.startTimer({ endpoint });
return function (...args) {
const result: Prisma = client[endpoint](...args);
endTimer();
return result;
};
},
});
export const prisma = prismaProxy;
I then have a utility that uses prisma that I would like to test:
// utils.ts
import { prisma } from 'database/prisma';
export const myFunc = async () => {
const result = await prisma.items();
if (items && items.length > 0) {
return true;
}
return false;
};
How do I mock prisma in a test? In Jest 26, this worked:
// utils.test.ts
jest.mock('server/database/prisma');
import { prisma } from 'server/database/prisma';
import { myFunc } from 'server/utils';
describe.only('my test', () => {
beforeAll(() => {
prisma.items.mockImplementation(() =>
Promise.resolve([
{
foo: 'bar',
},
])
);
});
test('it works', async () => {
const items = await myFunc();
expect(items).toBeTruthy();
});
});
This no longer works in Jest 27 it seems. Having made no changes except upgrading Jest from v26 -> v27 the mocks no longer seem to take hold.
What is the proper way to mock prisma here?

Related

Tests haphazardly fail when using graphql-request and MSW

I am trying to set up tests to test a Remix loader and noticed that the request function from graphql-request haphazardly fails when using MSW. If I replace this and use a simple fetch for the requests the tests pass.
Is there any configuration I need to change? I have created a sample repo that demonstrates the bug. The bug gets worse the more tests you have that are using the same mocked request.
Sample repo: https://github.com/charklewis/sb9or3
Here is a summary of the code I am using:
//modules/database.server
import { GraphQLClient } from "graphql-request";
const client = new GraphQLClient("http://some-graphql-api.com/api/graphql", {});
export const fetchQuery = async (query: any, variables: any) => {
try {
const response = await client.request(query, variables || {});
return response;
} catch (error) {
return {};
}
};
//routes/index
import type { LoaderFunction } from "remix";
import { json } from "remix";
import { gql } from "graphql-request";
import { fetchQuery } from "~/modules/database.server";
export const loader: LoaderFunction = async () => {
const query = gql`
query MyQuery {
demoQuery {
value
}
}
`;
const response = await fetchQuery(query, {});
return json({ value: response.demoQuery.value });
};
//routes/__tests__/index
import { graphql } from "msw";
import { setupServer } from "msw/node";
import { loader } from "~/routes/index";
const createServer = (handlers: any[] = []) => {
const server = setupServer(...handlers);
beforeAll(() => server.listen({ onUnhandledRequest: "bypass" }));
afterAll(() => server.close());
afterEach(() => server.resetHandlers());
return server;
};
const createDemoQueryHandler = ({ value = true } = {}) => {
return graphql.query("MyQuery", (req, res, ctx) => {
return res(ctx.data({ demoQuery: { value } }));
});
};
createServer([createDemoQueryHandler()]);
test("the loader returns data (round 1)", async () => {
const response = await loader({
request: new Request("/", { method: "GET" }),
params: {},
context: {},
});
const data = await response.json();
expect(data.value).toBe(true);
});
My vitest configuration is:
/// <reference types="vitest" />
/// <reference types="vite/client" />
import { defineConfig } from "vite";
import react from "#vitejs/plugin-react";
import tsconfigPaths from "vite-tsconfig-paths";
export default defineConfig({
plugins: [react(), tsconfigPaths()],
test: {
globals: true,
environment: "jsdom",
setupFiles: "./app/test-utils.ts",
testTimeout: 20000
}
});

How to mock firestore query with mocha and sinon?

W.r.t. How to mock firestore with mocha how do I mock the following firestore query using sinon?
import * as admin from "firebase-admin";
const db: FirebaseFirestore.Firestore = admin.firestore();
const storeSnapshot: = await db
.doc(`/Client/${clientId}/Store/${storeId}`)
.get();
I tried:
import * as _sinon from 'sinon';
it('Query Client collection not empty test', async () => {
const clientStoreDocStub = _sinon.stub(db, "doc");
clientStoreDocStub.withArgs("/Client/123/Store/789").resolves({
id: "789",
settings: [ {setting1: "Setting1-value", setting2: "Setting2-value"}, {setting1: "Setting3-value", setting2: "Setting4-value"}]
});
clientStoreDocStub.withArgs("/Client/456/Store/012").resolves({
id: "012",
settings: [ {setting1: "Setting5-value", setting2: "Setting6-value"}, {setting1: "Setting7-value", setting2: "Setting8-value"}]
});
const storeSnapshot: FirebaseFirestore.DocumentSnapshot = await db
.doc("/Client/123/Store/789")
.get();
const store = storeSnapshot.data();
});
but get the following error:
1) Mock firebase firestore Tests
Query Client collection not empty test:
TypeError: dbInit_1.db.doc(...).get is not a function
import * as _chai from "chai";
import * as chaiAsPromised from "chai-as-promised";
import * as admin from "firebase-admin";
import * as _sinon from 'sinon';
_chai.use(chaiAsPromised);
var expect = _chai.expect;
const db: FirebaseFirestore.Firestore = admin.firestore();
describe("Test with mock firestore", () => {
var docRefStub;
beforeEach(() => { docRefStub = _sinon.stub(db, "doc");});
afterEach(() => {db.doc.restore();});
it("Test should pass with valid request params", async () => {
const expectedString = "Hello World!!!";
const testCollection = {
"/Client/123/Store/789": {
data: 1,
moreData: "Hello World!!!"
}
}
const setSpy = _sinon.spy();
docRefStub.callsFake(fakeFsDoc(testCollection, setSpy));
await callAFunction("123", "789");
expect(setSpy.called).to.be.true;
expect(setSpy.getCall(0).args[0].data).to.not.be.null;
expect(setSpy.getCall(0).args[0].moreData).to.deep.equal(expectedString);
}
});
export function fakeFsDoc(database, setSpy = null) {
return docId => {
const data = database[docId] ?? undefined;
return {
get: async () => ({
data: () => data
}),
set: setSpy
}
}
}

How to mock implementation of the redis createClient function in an integration tests with jest and supertest?

I have created a unit test for a redis repository that works fine:
import { RedisSecretRepository } from "../../../../src/adapters/repositories/RedisSecretRepository";
import { Secret } from "../../../../src/domain/models/Secret";
import { UrlId } from "../../../../src/domain/models/UrlId";
import { SecretNotFoundInRepositoryError } from "../../../../src/domain/models/errors/SecretNotFoundInRepository";
jest.mock("redis");
import { createClient } from "redis";
const mockedCreateClient = createClient as jest.MockedFunction<any>;
describe("Redis Secret Repository", () => {
it("should get a secret by urlId", () => {
const mockedRedisClient = {
connect: jest.fn(),
get: jest.fn(async () => "123qwe"),
on: jest.fn(),
};
mockedCreateClient.mockReturnValue(mockedRedisClient);
const redisSecretRepository = new RedisSecretRepository();
expect(redisSecretRepository.getSecretByUrlId(new UrlId("123456qwerty")))
.resolves.toEqual(new Secret("123qwe"));
expect(mockedRedisClient.get).toBeCalledTimes(1);
expect(mockedRedisClient.get).toBeCalledWith("123456qwerty");
});
});
and here I receive my mockedRedisClient and everything works good, I can mock my behaviour. But in the integration test using supertest, with the same implementation and same mocking procedure, it simply does not work, it does not take the fake implementation:
import supertest from "supertest";
import server from "../../src/server";
const request = supertest(server.app);
jest.mock("redis");
import { createClient } from "redis";
const mockedCreateClient = createClient as jest.MockedFunction<any>;
describe("Get Secrets By Id Integration Tests", () => {
it("should retrieve a secret", async () => {
const mockedRedisClient = {
connect: jest.fn(),
get: jest.fn(async () => "123qwe"),
on: jest.fn(),
};
mockedCreateClient.mockReturnValue(mockedRedisClient);
const res = await request
.get("/api/v1/secrets/123456qwerty");
expect(createClient).toBeCalledTimes(3);
console.log(res.body);
expect(res.status).toBe(200);
expect(res.body).toEqual({
secret: "123qwe"
});
});
});
Using this, createClient is mocked, but with no implementation. It is ignoring later modifications like .mockReturnValue.
In a different approach, I could hardcode the implementation in the mock declaration doing this:
import { createClient } from "redis";
jest.mock("redis", () => {
return {
createClient: () => {
return {
connect: jest.fn(),
get: jest.fn(async () => "123qwe"),
on: jest.fn(),
}
}
}
});
but this is hardcoded, I cannot change the implementation for each test where I want to assess different behaviors.
anyone knows why this happens? Supertest does not allow me to change implementations on runtime?

Test ApolloClient API with Jest in Vue3 (Composition API)

I am using Vue3 (typescript) with Composition API for my application. I am using ApolloClient grapghql for API calls. I have created a separate service file for API calls. (PFB files)
Service file
import { ApolloClient, InMemoryCache, HttpLink } from "#apollo/client/core"
import { gql } from "#apollo/client/core"
import fetch from 'cross-fetch';
const httpLink = new HttpLink({
uri: process.env.VUE_APP_BACKEND_GRAPHQL_URI,
fetch
})
const apolloClient = new ApolloClient({
link: httpLink,
cache: new InMemoryCache(),
})
export const getAplloAPIdata = async (reqQuery: any) => {
const query = gql `${reqQuery}`
try {
return await apolloClient.query({ query })
}catch {
console.log('API error')
}
}
Home.vue
setup() {
const threatList = ref([])
const threat = ref(null)
// get all threats
const getThreats = async () => {
const getThreatsQuery = `
query {
threats {
short_description
threat_level
}
}
`
try {
const result = await getAplloAPIdata(getThreatsQuery)
if (result) {
threatList.value = result.data.threats
}
} catch {
console.log('Error receiving threats data')
}
}
Can you please tell me how can I write test cases to mock this API in jest? Thank you!
I would mock getAplloAPIdata to return mock data, and verify that data in your test. The key is to make sure the mock path is the same as that imported in your component:
// Home.vue
import { getAplloAPIdata } from '#/service'
/*...*/
// Home.spec.js
jest.mock('#/service', () => {
return {
getAplloAPIdata: () => ({
data: {
threats: [{ id: 123456 }]
}
})
}
})
describe('Home.vue', () => {
it('gets threats', async () => {
const wrapper = shallowMount(Home)
await wrapper.vm.getThreats()
expect(wrapper.vm.threatList).toContainEqual({ id: 123456 })
})
})
GitHub demo

Unit test for customPollingHook which uses apollo useLazyQuery

So I have written a custom polling hook which uses useContext and useLazyQuery hooks. I want to write a unit test for this, which should cover its returned values state and side effect.
So far I have managed to do this much but I'm not so sure how to proceed ahead. Any tips?
export const useUploadActivityPolling = (
teId: TeIdType
): UploadActivityPollingResult => {
const { dispatch, uploadActivityId }: StoreContextType = useAppContext();
const [fetchActivityStatus, { error: UploadActivityError, data: UploadActivityData, stopPolling }] = useLazyQuery(
GET_UPLOAD_ACTIVITY,
{
pollInterval: 3000,
fetchPolicy: 'network-only',
variables: { teId, activityId: uploadActivityId },
}
);
useEffect(() => {
if (UploadActivityData) {
setUploadActivityId(
UploadActivityData.getUploadActivityStatus.activity_id,
dispatch
);
updateActivityStateAction(UploadActivityData.getExcelUploadActivityStatus.status, dispatch);
}
}, [UploadActivityData]);
return { fetchActivityStatus, stopPolling, UploadActivityError };
};
import React from 'react';
import { mount } from 'enzyme';
const TestCustomHook = ({ callback }) => {
callback();
return null;
};
export const testCustomHook = callback => {
mount(<TestCustomHook callback={callback} />);
};
describe('useUploadActivityPolling', () => {
let pollingResult;
const teId = 'some id';
beforeEach(() => {
testCustomHook(() => {
pollingResult = useUploadActivityPolling(teId);
});
});
test('should have an fetchActivityStatus function', () => {
expect(pollingResult.fetchActivityStatus).toBeInstanceOf(Function);
});
});

Resources