Jest environment has been torn down error in graphql, nodejs project. Used jest.useFakeTimers but still getting the error - node.js

Getting ReferenceError: You are trying to import a file after the Jest environment has been torn down From office/test/office.test.ts.
Used jest.useFakeTimers(); after imports, but the issue still exists. Is there any way to resolve it ?
Here is my jest.config.ts
import type { Config } from "#jest/types"
const config: Config.InitialOptions = {
transform: {
'^.+\\.tsx?$': 'ts-jest'
},
testRegex: '(/__test__/.*|(\\.|/)(test|spec))\\.[jt]sx?$',
moduleFileExtensions: [ 'js','ts'],
testEnvironment: "jest-environment-node",
fakeTimers: {
enableGlobally: true,
},
verbose: true
}
export default config
The test file office.test.ts
import {ApolloServer} from 'apollo-server'
import {expect,test,jest} from '#jest/globals';
import officeGql from '../controllers/controller'
jest.useFakeTimers();
test('Office Module Test', async () => {
let typeDefs = officeGql.types
let resolvers = officeGql.resolvers
let testServer = new ApolloServer({
typeDefs,
resolvers,
});
const response = await testServer.executeOperation({
query: `query offices(limit: $limit,offset: $offset){name, address}`,
variables: { limit: 10,offset:0 },
});
expect(response.data).toBeTruthy();
});

Related

How to deep mock Prisma Client in JavaScript (not TypeScript)?

I'm want to unit test Next.js API route with Prisma in JavaScript. Unfortunately, the Prisma's unit testing guide is written for Typescript.
I have jest.config.js which will setup the mock in jest.setup.js
const nextJest = require("next/jest");
const createJestConfig = nextJest({
dir: "./",
});
const config = {
setupFilesAfterEnv: ["<rootDir>/jest.setup.js"],
moduleDirectories: ["node_modules", "<rootDir>/"],
moduleNameMapper: {
"#/(.*)$": "<rootDir>/$1",
},
};
module.exports = createJestConfig(config);
and this is how I configured the mock in jest.setup.js
import prisma from "#/utils/client";
jest.mock("#/utils/client", () => ({
__esModule: true,
default: {
user: {
findMany: jest.fn(),
},
// ... and each and everyone of the entities
},
}));
export const prismaMock = prisma;
and the following test case passed
describe("Calculator", () => {
it("renders a calculator", async () => {
await prismaMock.user.findMany.mockResolvedValue(["abc]);
const { req, res } = createMocks();
await handler(req, res);
expect(res._getStatusCode()).toBe(200);
expect(res._getData()).toBe('["abc"]');
});
});
With this approach, I have to mock each and everyone of the models and function in jest.setup.js. Is there a way to mock all the models and functions automatically? Is there a similar JavaScript library which provides mockDeep from jest-mock-extended?

Fastify CLI decorators undefined

I'm using fastify-cli for building my server application.
For testing I want to generate some test JWTs. Therefore I want to use the sign method of the fastify-jwt plugin.
If I run the application with fastify start -l info ./src/app.js everything works as expected and I can access the decorators.
But in the testing setup I get an error that the jwt decorator is undefined. It seems that the decorators are not exposed and I just can't find any error. For the tests I use node-tap with this command: tap \"test/**/*.test.js\" --reporter=list
app.js
import { dirname, join } from 'path'
import autoload from '#fastify/autoload'
import { fileURLToPath } from 'url'
import jwt from '#fastify/jwt'
export const options = {
ignoreTrailingSlash: true,
logger: true
}
export default async (fastify, opts) => {
await fastify.register(jwt, {
secret: process.env.JWT_SECRET
})
// autoload plugins and routes
await fastify.register(autoload, {
dir: join(dirname(fileURLToPath(import.meta.url)), 'plugins'),
options: Object.assign({}, opts),
forceESM: true,
})
await fastify.register(autoload, {
dir: join(dirname(fileURLToPath(import.meta.url)), 'routes'),
options: Object.assign({}, opts),
forceESM: true
})
}
helper.js
import { fileURLToPath } from 'url'
import helper from 'fastify-cli/helper.js'
import path from 'path'
// config for testing
export const config = () => {
return {}
}
export const build = async (t) => {
const argv = [
path.join(path.dirname(fileURLToPath(import.meta.url)), '..', 'src', 'app.js')
]
const app = await helper.build(argv, config())
t.teardown(app.close.bind(app))
return app
}
root.test.js
import { auth, build } from '../helper.js'
import { test } from 'tap'
test('requests the "/" route', async t => {
t.plan(1)
const app = await build(t)
const token = app.jwt.sign({ ... }) //-> jwt is undefined
const res = await app.inject({
method: 'GET',
url: '/'
})
t.equal(res.statusCode, 200, 'returns a status code of 200')
})
The issue is that your application diagram looks like this:
and when you write const app = await build(t) the app variable points to Root Context, but Your app.js contains the jwt decorator.
To solve it, you need just to wrap you app.js file with the fastify-plugin because it breaks the encapsulation:
import fp from 'fastify-plugin'
export default fp(async (fastify, opts) => { ... })
Note: you can visualize this structure by using fastify-overview (and the fastify-overview-ui plugin together:

Can I apply 'setupFilesAfterEnv' to specific file in jest config?

I'm working on appling prisma unit testing and Integration testing
I want to apply unit testing for *.service.test.ts files
and intergration testing for *.test.ts files.
I followed the Prisma document, but there is something that doesn't work.
singleton.ts
import { mockReset, mockDeep, DeepMockProxy } from "jest-mock-extended";
import { PrismaClient } from "#prisma/client";
import Prisma from "../src/db/prisma";
jest.mock("../src/db/prisma", () => {
return {
__esModule: true,
default: mockDeep<PrismaClient>(),
};
});
beforeEach(() => {
// eslint-disable-next-line no-use-before-define
mockReset(prismaMock);
});
export const prismaMock = Prisma as unknown as DeepMockProxy<PrismaClient>;
jest.config.ts
When turing off setupFilesAfterEnv option, testing *.test.ts files are working.
So I Want turn off setupFilesAfterEnv option in Integration testing
Is it applicable only when unit testing?
...
setupFilesAfterEnv: [
"./jest/singleton.ts"
]
I think your question is a bit incomplete, but I might know what you are talking about because I am running into a similar problem.
If you are trying to do the integration tests from prisma documentation, you need to unmock your prisma client on your integration tests. Otherwise it will still be mocked by your singleton.ts file
something like this:
myTest.test.js (integration test file)
jest.unmock("../src/db/prisma");
Another way of doing it, is just to remove the singleton from setupFilesAfterEnv and just import the prisma client from the singleton file inside your tests.
What I did:
I created 2 tests files (one for integration and another one for unit testing: CreateData.unit.test.ts and CreateData.int.test. I also created 2 singleton files:
singleton.unit.ts (I wanted that to be applied on my unit tests)
import { PrismaClient } from '#prisma/client';
import { mockDeep, mockReset, DeepMockProxy, mock } from 'jest-mock-extended';
import prismaClient from '../prismaClient';
jest.mock('../prismaClient', () => ({
__esModule: true,
default: mockDeep<PrismaClient>(),
}));
beforeEach(() => {
mockReset(prismaMock);
});
export const prismaMock = prismaClient as unknown as DeepMockProxy<PrismaClient>;
singleton.int.ts (I wanted that applied in my integration tests)
import prismaClient from '../prismaClient';
afterAll(async () => {
const deleteData = prismaClient.data.deleteMany();
await prismaClient.$transaction([
deleteData,
]);
await prismaClient.$disconnect();
});
export { prismaClient };
I removed setupFilesAfterEnv from jest.config.js
Then create your unit tests and integration tests. You don't need to unmock prisma client if you removed the singleton from setupFilesAfterEnv in jest.config.ts
myTest.unit.test.ts
import { prismaMock } from "<path>/singleton.unit";
import { CreateData } from "<path>/CreateData";
let createData;
let createDate = new Date();
const data = {
id: "randomId1234",
name: "Bob Singer",
email: "bob#gmail.com",
password: "123456",
};
beforeEach(() => {
createData = new CreateData();
});
describe('CreateData', () => {
it("should create new data", async () => {
const result = createData.execute(data);
prismaMock.data.create.mockResolvedValue(data);
await expect(result).resolves.toEqual({
id: "randomId1234",
name: "Bob Singer",
email: "bob#gmail.com",
password: "123456",
});
});
});
myTest.int.test.ts
import prismaClient from "<path>/singleton.int";
import { CreateData } from "<path>/CreateData"
let createData;
let createDate = new Date();
const data = {
id: "randomId1234",
name: "Bob Singer",
email: "bob#gmail.com",
password: "123456",
};
beforeEach(() => {
createData = new CreateData();
});
describe('CreateTrainer', () => {
it("should create new trainer", async () => {
const result = await createData.execute(data);
const newData = await prismaClient.data.findUnique({
where: {
email: "bob#gmail.com"
}
});
console.log(result);
expect(newData?.email).toEqual(data.email);
});
});

Mocking custom Hook with jest and enzyme results in 'xxx is not a function or its return value is not iterable' Error

I am very new to jest and enzyme. In my Project I will use a SPA React based Application. Containing a Context Provider for the data, also several hooks. I Using now Jest (with ts-jest and enzyme)
My jest.config looks like this
module.exports = {
"roots": [
"<rootDir>/src"
],
"transform": {
"^.+\\.tsx?$": "ts-jest"
},
"testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.tsx?$",
"moduleFileExtensions": [
"ts",
"tsx",
"js",
"jsx",
"json",
"node"
],
"snapshotSerializers": ["enzyme-to-json/serializer"]
So my first stept so test UI components works.
Next step was to test componentes with mocked data. But there I got the error described at the bottom.
I have a functional component like this:
export default function CurrentWeather(props: ICurrentWeatherProps) {
const [data, Reload] = useCurrentWeather(props.locationID);
return (<div>......</div>)
}
You will notice the useCurrentWeather hook, here is the code for this:
import { useEffect, useState } from 'react';
import { useLocationState } from '../context/locationContext';
import { ILocationData } from './useLocations';
import _ from 'lodash';
...
export default function useCurrentWeater(locationId: number) {
const locationState = useLocationState();
const Reload = () => { GetData() }
const [Data, SetData] = useState<IWeatherDataInfo>({Id:0,ConditionIcon:'',Date:new Date(),MaxTemp:0, MinTemp:0});
async function GetData() { .... }
useEffect(Reload, [locationState.data, locationId]);
return [Data, Reload] as const;
}
Now I wand to mock these Hook. I tried following
import React from 'react';
import { configure, shallow, mount } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import CurrentWeather from '../components/Weather/CurrentWeather';
import { IWeatherDataInfo } from '../Hooks/useWeaters';
configure({ adapter: new Adapter() });
const mockWeatherReload = jest.fn();
const mockdata: IWeatherDataInfo = { Date: new Date(), ConditionIcon: "", MinTemp: 0, MaxTemp: 10 };
jest.mock('../Hooks/useCurrentWeather', () => ({
useCurrentWeather: jest.fn(()=>{ [mockdata, mockWeatherReload]})
}));
describe("WeatherWidget", () => {
it("RenderOnlyAddButton", () => {
const container = shallow(<CurrentWeather locationID={1} hidden={false} />);
});
});
Now, when I execute this test, I will get this error result:
src/tests/WeatherWidget.test.tsx
● WeatherWidget › RenderOnlyAddButton
TypeError: (0 , _useCurrentWeather.default) is not a function or its return value is not iterable
9 |
10 | export default function CurrentWeather(props: ICurrentWeatherProps) {
> 11 | const [data, Reload] = useCurrentWeather(props.locationID);
| ^
12 | return (
What I'm doing wrong here? Is there what I'm missing?
Try like this:(below should be your functional component's test file)
const mockUseCurrentWeather = jest.fn();
jest.mock("put here the absolute path", () => ({
__esModule: true,
useCurrentWeather: (...args: any) => mockUseCurrentWeather(...args),
}));
describe("WeatherWidget", () => {
beforeEach(() => {
mockUseCurrentWeather.mockClear();
mockUseCurrentWeather.mockReturnValue([undefined, undefined]);
});
it("RenderOnlyAddButton", () => {
mockUseCurrentWeather.mockClear();
mockUseCurrentWeather.mockReturnValue([undefined, undefined]);
const container = shallow(<CurrentWeather locationID={1} hidden={false} />);
});
});

How to validate graphql schema with fragmented documents

i was thinking it would be cool to support schema validation at the unit test level so we could be aware of breaking changes to queries when we upgrade our api
i’d like to set up the test so that it supports auto-discovery of any new *.graphql files but in doing so, the jest process thinks the current working directory is in __tests__ so when i evaluate the graphql document manually with the loader, relative fragments in queries like this fail:
#import "./fragments/FullUserData.graphql"
query User(
$zid: String!
) {
user {
userData: get(
zid: $zid
) {
...FullUserData
}
}
}
failure message:
Error: Cannot find module './fragments/FullUserData.graphql' from 'schemaValidation-test.js'"
if i move fragments folder into the __tests__ dir, the test gets happy.
any ideas on what I can do to trick the evaluation to process the fragment as if I was relative to the fragment directory?
__tests__/
- schemaValidation-test.js
queries/
- someQuery.graphql
- fragments/someFragment.graphql
i tried process.chdir() to the queries dir from within jest but no dice
here is the validator:
// __tests__/schemaValidation-test.js
import glob from 'glob'
import { validate } from 'graphql/validation'
import loader from 'graphql-tag/loader'
import schema from 'api/lib/app/graphql/schema'
import path from 'path'
import fs from 'fs'
const gqlDir = path.join(__dirname, '..')
const queryDir = path.join(gqlDir, 'queries', 'shared')
const pattern = `${queryDir}/!(fragments)*.graphql`
const getGraphqlFiles = () => glob.sync(pattern)
describe('api schema', () => {
const files = getGraphqlFiles()
for(var file of files) {
const buffer = fs.readFileSync(file)
let document = (buffer || "").toString()
try {
document = eval(loader.call(
{ cacheable: () => ({}) },
document
))
} catch (e) {
fail(`could not parse ${file}, ${e}`)
}
it(`${file} passes validation`, () => {
const errors = validate(
schema,
document,
)
expect(errors).toEqual([])
})
}
})
How can I tell the loader I am in a different directory relative to the fragment?
I figured this out. The key was to use require instead of fs.readFileSync
import glob from 'glob'
import { validate } from 'graphql/validation'
import schema from 'api/lib/app/graphql/schema'
import path from 'path'
const gqlDir = path.join(__dirname, '..')
const queryDir = path.join(gqlDir, 'queries', 'shared')
const pattern = `${queryDir}/!(fragments)*.graphql`
const getGraphqlFiles = () => glob.sync(pattern)
describe('rent-js-api schema', () => {
const files = getGraphqlFiles()
files.forEach(file => {
/* eslint-disable import/no-dynamic-require */
const document = require(file)
it(`${file} passes validation`, () => {
const errors = validate(
schema,
document,
)
expect(errors).toEqual([])
})
})
})
here is jest.config.json
{
"setupFiles": [
"<rootDir>/test/jest/shim.js",
"<rootDir>/test/jest/setup.js"
],
"moduleDirectories": ["node_modules", "src", "test/jest", "test"],
"collectCoverage": false,
"testMatch": ["**/*-test.js"],
"collectCoverageFrom": [
"**/src/**/*.{js,ts,jsx,tsx}",
"!**/src/**/*-test.js",
"!**/index.{ts,js}",
"!**/src/**/const.{ts,js}",
"!**/ui/theme/**",
"!**/src/**/*.d.{ts,tsx}",
"!**/node_modules/**",
"!**/src/ui/*/themes/**"
],
"coverageDirectory": "./coverage",
"moduleNameMapper": {
"\\.(css|scss)$": "<rootDir>/test/jest/noop-styles",
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/test/jest/noop-binary",
"^.+\\.html$": "<rootDir>/test/jest/htmlLoader"
},
"moduleFileExtensions": [
"graphql",
"js",
"json",
"ts",
"tsx"
],
"transform": {
"^.+\\.jsx?$": "babel-jest",
"^.+\\.tsx?$": "babel-jest",
"^.+\\.graphql$": "jest-transform-graphql"
},
"testPathIgnorePatterns": [
"<rootDir>/node_modules/",
"^.*__tests__/__helpers__.*"
],
"snapshotSerializers": [
"enzyme-to-json/serializer",
"jest-serializer-html"
]
}

Resources