Jest SuperTest: Giving error in Open Handler when using setTimeout in the class-under-test - jestjs

i am writing a E2E in Jest/Supertest in Nest JS (node) environment.
I have searched extensively about this pretty common error:
Jest has detected the following 1 open handle potentially keeping Jest from exiting:
Implemented every solutions suggested.
However getting this in a setTimeout method intermittently as follows:
Test Suites: 2 passed, 2 total
Tests: 6 passed, 6 total
Snapshots: 0 total
Time: 135.464 s
Ran all test suites.
Jest has detected the following 1 open handle potentially keeping Jest from exiting:
● Timeout
49 | },
50 | delay(millisecond: number) {
> 51 | return new Promise((resolve) => setTimeout(resolve, millisecond));
| ^
52 | },
53 | };
54 |
Here is my setTimeout code:
// The API
async create<T>(key: string, item: T): Promise<T> {
await this.delay(5000);
return this.createWithoutDelay(key, item);
}
// The delay method code
delay(millisecond: number) {
return new Promise((resolve) => setTimeout(resolve, millisecond));
},
My tests are very simple:
beforeAll(async () => {
jest.clearAllMocks();
......
app = moduleUnderTest.createNestApplication();
await app.init();
// Reference the server instance
server = app.getHttpServer();
});
it('Test: Create Object', async () => {
const response = await request(server).post('/object')
.send({
name: 'new-object',
});
expect(response.status).toEqual(HttpStatus.CREATED);
expect(response.body).toBeDefined();
expect(Array.isArray(response.body)).toBeFalsy();
const jsonResponse = JSON.parse(response.text);
expect(jsonResponse.name).toEqual('new-object');
});
afterAll(async () => {
await app.close();
await server.close();
});
The following are my Jest config:
module.exports = {
moduleFileExtensions: ["js", "json", "ts"],
verbose: true,
preset: "ts-jest",
rootDir: ".",
testTimeout: 15000,
fakeTimers: {
timerLimit: 15000,
},
testEnvironment: "node",
testRegex: ".e2e-spec.ts$",
transform: {
"^.+\\.(t|j)s$": "ts-jest"
},
moduleNameMapper: {
'^axios$': require.resolve('axios'),
},
};
In the Jest cmd, I am passing: --runInBand --forceExit --detectOpenHandles
Any clue shall be helpful.

Related

Jasmine expecting a spy, but got a Function

I'm stuck being stubborn to give up and find a workaround for something that looks so simple and should be working out of the box...
I'm dealing with a Jasmine test suite using the Node environment, that is unable to spy upon an object that is clearly there/provided/imported.
I assume it's about the module resolution, and the writable property...
Can someone point me out in the proper direction, please?
For what it's worth: using Angular with testBed and stuff I never had such issues with spies, they work like...they're out of the box.
Notes:
using prototype to spy on did not fix: spyOn(client.prototype,'clone').and.callThrough();
and as you can see below, nothing gets overwritten.
Below a simplified implementation to demo.
index.js:
`
let state = null;
const templates = { obj: { a:1, b:2} },
init = () => state = clone(templates.obj),
clone = (obj) => JSON.parse(JSON.stringify(obj));
export {
init,
clone
}`
index.spec.js:
`
import * as client from '../../index.js';
describe("test", () => {
it("should spy on the clone method", () => {
console.log(Object.getOwnPropertyDescriptors(client));
spyOn(client, 'clone').and.callThrough();
client.init();
expect(client.clone).toHaveBeenCalled();
});
})`
test result:
`
> client#1.0.0 test
> jasmine
Randomized with seed 24629
Started
client.clone: [Function: clone]
{
clone: {
value: [Function: clone],
writable: true,
enumerable: true,
configurable: false
},
init: {
value: [Function: init],
writable: true,
enumerable: true,
configurable: false
},
[Symbol(Symbol.toStringTag)]: {
value: 'Module',
writable: false,
enumerable: false,
configurable: false
}
}
F
Failures:
1) test should spy on the clone method
Message:
Error: <toHaveBeenCalled> : Expected a spy, but got Function.
Usage: expect(<spyObj>).toHaveBeenCalled()`
index.spec.js
import * as importedModule from '../../index.js';
const client = Object.assign({}, importedModule);
describe("test", () => {
it("should spy on the clone method", () => {
spyOn(client, 'clone').and.callThrough();
client.clone({a:1,b:2});
expect(client.clone).toHaveBeenCalled();
});
})
test result:
client#1.0.0 test
jasmine
Randomized with seed 17205
Started
.
1 spec, 0 failures
Finished in 0.007 seconds
Randomized with seed 17205 (jasmine --random=true --seed=17205)
index.js
let state = null;
const templates = { obj: { a:1, b:2} },
init = () => state = clone(templates.obj),
clone = (obj) => JSON.parse(JSON.stringify(obj));
export {
init,
clone
}
index.spec.js
import * as client from '../../index.js';
describe("test", () => {
it("should spy on the clone method", () => {
spyOn(client, 'clone').and.callThrough();
client.clone({a:1,b:2});
expect(client.clone).toHaveBeenCalled();
});
});
test result
client#1.0.0 test
jasmine
Randomized with seed 76297
Started
F
Failures:
1. test should spy on the clone method
Message:
Error: : Expected a spy, but got Function.
Usage: expect().toHaveBeenCalled()
Stack:
at
at UserContext. (file:///home/masterjones/Projects/test/spec/support/index.spec.js:7:30)
at

How to turn of module reloading in JEST?

I am using Jest to testing express REST api written in express.js and typescript using ts-jest. My problem is Jest loads app module (express) in every test suite, it lasts only 3-4 seconds, but there is ~80 test suites each containing a multiple test cases.
My first thought was to remove jest.resetModules() from global afterAll() function, but it didn't helped. Is there another way how to change this behaviour or it is feature by design?
setup.ts
import { sequelize } from '../src/db/models'
import seeds from '../src/db/seeders/test'
// multiple mocks of services
jest.mock('../some/custom/module/mocks')
beforeAll(async () => {
await sequelize.sync({ force: true }) // basically drop db and create clean one
await seeds() // seed database with data
})
afterAll(async () => {
jest.clearAllMocks()
// jest.resetModules() //! This line was removed
await sequelize.close()
global.gc()
})
jest.setTimeout(10000)
global.ts
import { createJwt } from '../src/utils/authorization'
export default async () => {
// create some acces tokens and add to process.env
process.env.jwttoken = await createJwt({ uid: 1 }, { audience: 'users' })
}
jest.config.js
module.exports = {
transform: {
'^.+\\.ts?$': 'ts-jest'
},
roots: [
'<rootDir>/tests/'
],
moduleFileExtensions: [
'ts',
'js'
],
setupFilesAfterEnv: [
'<rootDir>/tests/setup.ts'
],
globalSetup: '<rootDir>/tests/global.ts',
testEnvironment: 'node'
}
example test
import supertest from 'supertest'
import app from '../../../../../src/app'
describe(`[GET] ${endpoint(':id')})`, () => {
const request = supertest(app). // every time jest hits this line in test, it load app again
it('no authorization token | code 401', async () => {
const response = await request.get('/something')
.set('Content-Type', 'application/json')
expect(response.status).toBe(401)
})
start script
POSTGRESQL_URL=postgresql://postgres:root#127.0.0.1:5432/database JWT_SECRET=secret node --expose-gc \"./node_modules/jest/bin/jest.js\" --runInBand --passWithNoTests --logHeapUsage

Nest can't resolve dependencies in the RootTestModule context when I use Bazel Test

I need to run my tests in the bezel. how can I solve this mysterious problem?
I have a nestjs project contains multiple apps and libs. When I run the test yarn jest --config ./jest.config.json libs/lib1, it works perfectly. However when I run with bezel bazel test //libs/lib1/... it gives me an error "Nest can't resolve dependencies ... Please make sure that the argument dependency at index ... is available in the RootTestModule context.".
REPO
https://github.com/smhmayboudi/bazel_jest_nestjs
I find out that the order of mapping at jest.config.json is important.
this one works well ( shows test + coverage ), but dependency problem
"moduleNameMapper": {
"#melo/lib1": "<rootDir>/libs/lib1/src",
"#melo/lib1/(.*)": "<rootDir>/libs/lib1/src/$1",
},
this one works ( show just pass message with out actual test result and coverage !? )
"moduleNameMapper": {
"#melo/lib1/(.*)": "<rootDir>/libs/lib1/src/$1",
"#melo/lib1": "<rootDir>/libs/lib1/src",
},
Jest Config
{
"coverageReporters": ["lcov", "text-summary"],
"moduleNameMapper": {
"#melo/libs1": "<rootDir>/libs/libs1/src",
"#melo/libs1/(.*)": "<rootDir>/libs/libs1/src/$1",
},
"modulePathIgnorePatterns": ["/bazel-out/", "/node_modules/"],
"preset": "ts-jest",
"testEnvironment": "node"
}
Bazel Config
ts_library(
name = "lib1_test_ts_library",
srcs = glob(["*spec.ts"]),
runtime = "nodejs",
deps = [
":lib1_ts_library",
"#npm//#nestjs/common",
"#npm//#nestjs/testing",
"#npm//#types/jest",
"#npm//rxjs",
"#npm//ts-jest",
],
)
jest_test(
name = "lib1_jest_test",
srcs = glob(["*spec.ts"]),
jest_config = "//:jest.config.json",
deps = [
":lib1_test_ts_library",
],
coverage = True,
)
Error Log
INFO: Invocation ID: 84f45d55-c6e4-4c2a-b05d-367d0f84baf7
INFO: Analyzed target //libs/lib1/src:lib1_jest_test (633 packages loaded, 19569 targets configured).
INFO: Found 1 test target...
WARNING: failed to create one or more convenience symlinks for prefix 'dist/':
cannot create symbolic link bazel-out -> /Users/WHOAMI/Developer/MY_PROJECT/bazel-out/execroot/melo/bazel-out: /Users/WHOAMI/Developer/MY_PROJECT/bazel-out (File exists)
FAIL: //libs/lib1/src:lib1_jest_test (see /Users/WHOAMI/Developer/MY_PROJECT/bazel-out/execroot/melo/bazel-out/darwin-fastbuild/testlogs/libs/lib1/src/lib1_jest_test/test.log)
INFO: From Testing //libs/lib1/src:lib1_jest_test:
==================== Test output for //libs/lib1/src:lib1_jest_test:
PASS libs/lib1/src/lib1.util.spec.ts (23.866 s)
PASS libs/lib1/src/lib1.interceptor.spec.ts (23.977 s)
FAIL libs/lib1/src/lib1.service.spec.ts (24.717 s)
● ApmService › should be defined
Nest can't resolve dependencies of the ApmService (?). Please make sure that the argument dependency at index [0] is available in the RootTestModule context.
Potential solutions:
- If dependency is a provider, is it part of the current RootTestModule?
- If dependency is exported from a separate #Module, is that module imported within RootTestModule?
#Module({
imports: [ /* the Module containing dependency */ ]
})
at Injector.resolveSingleParam (../../../../../../../../node_modules/#nestjs/core/injector/injector.js:134:19)
at resolveParam (../../../../../../../../node_modules/#nestjs/core/injector/injector.js:102:49)
at Array.map (<anonymous>)
at Injector.resolveConstructorParams (../../../../../../../../node_modules/#nestjs/core/injector/injector.js:117:58)
at Injector.loadInstance (../../../../../../../../node_modules/#nestjs/core/injector/injector.js:81:20)
at Injector.loadProvider (../../../../../../../../node_modules/#nestjs/core/injector/injector.js:38:20)
at ../../../../../../../../node_modules/#nestjs/core/injector/instance-loader.js:43:62
at Array.map (<anonymous>)
at InstanceLoader.createInstancesOfProviders (../../../../../../../../node_modules/#nestjs/core/injector/instance-loader.js:43:36)
at ../../../../../../../../node_modules/#nestjs/core/injector/instance-loader.js:28:24
● ApmService › should be defined
expect(received).toBeDefined()
Received: undefined
56 |
57 | it("should be defined", () => {
> 58 | expect(service).toBeDefined();
| ^
59 | });
60 |
61 | it("start should be called", () => {
at Object.<anonymous> (libs/lib1/src/lib1.service.spec.ts:58:21)
...
Test Suites: 2 failed, 2 passed, 4 total
Tests: 27 failed, 3 todo, 6 passed, 36 total
Snapshots: 0 total
Time: 26.102 s
Ran all test suites within paths "libs/lib1/src/lib1.decorator.spec.ts", "libs/lib1/src/lib1.interceptor.spec.ts", "libs/lib1/src/lib1.service.spec.ts", "libs/lib1/src/lib1.util.spec.ts".
================================================================================
Target //libs/lib1/src:lib1_jest_test up-to-date:
dist/bin/libs/lib1/src/lib1_jest_test.sh
dist/bin/libs/lib1/src/lib1_jest_test_loader.js
dist/bin/libs/lib1/src/lib1_jest_test_require_patch.js
INFO: Elapsed time: 83.878s, Critical Path: 59.53s
INFO: 4 processes: 4 local.
INFO: Build completed, 1 test FAILED, 12 total actions
//libs/lib1/src:lib1_jest_test FAILED in 28.2s
/Users/WHOAMI/Developer/MY_PROJECT/bazel-out/execroot/melo/bazel-out/darwin-fastbuild/testlogs/libs/lib1/src/lib1_jest_test/test.log
INFO: Build completed, 1 test FAILED, 12 total actions
Add providers to your RootTestModule. Nest doesn't automatically include the services in your test, depending on if you used the cli vs creating the files/folders directly.
const module: TestingModule = await Test.createTestingModule({
providers: [/** Services goes here **/],
controllers: [CustomersController],
}).compile();
Not working vs Working below
import { Test, TestingModule } from '#nestjs/testing';
import { CustomersController } from './customers.controller';
describe('CustomersController', () => {
let controller: CustomersController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [CustomersController],
}).compile();
controller = module.get<CustomersController>(CustomersController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
it('shoud return customer', async () => {
const tcase = await controller.d({});
expect(tcase).toHaveProperty('firstName');
})
});
Working (I had the exact error message with different filenames ofcourse)
import { Test, TestingModule } from '#nestjs/testing';
import { CustomersController } from './customers.controller';
import { CustomersService } from './customers.service';
describe('CustomersController', () => {
let controller: CustomersController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [CustomersService],
controllers: [CustomersController],
}).compile();
controller = module.get<CustomersController>(CustomersController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
it('shoud return customer', async () => {
const tcase = await controller.d({});
expect(tcase).toHaveProperty('firstName');
})
});
A potential workaround is to declare "empty"
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [{
provide: CustomerService,
useValue: {},
}],
controllers: [CustomersController],
}).compile();
The solution that worked for me was following the unit test examples of the library.
import { Test, TestingModule } from '#nestjs/testing';
import { CreateUserDto } from './dto/create-user.dto';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';
const createUserDto: CreateUserDto = {
firstName: 'firstName #1',
lastName: 'lastName #1',
};
describe('UsersController', () => {
let usersController: UsersController;
let usersService: UsersService;
beforeEach(async () => {
const app: TestingModule = await Test.createTestingModule({
controllers: [UsersController],
providers: [ // <---------- THIS IS THE MOST IMPORTANT SECTION TO SOLVE THIS ISSUE.
{
provide: UsersService,
useValue: {
create: jest
.fn()
.mockImplementation((user: CreateUserDto) =>
Promise.resolve({ id: '1', ...user }),
),
findAll: jest.fn().mockResolvedValue([
{
firstName: 'firstName #1',
lastName: 'lastName #1',
},
{
firstName: 'firstName #2',
lastName: 'lastName #2',
},
]),
findOne: jest.fn().mockImplementation((id: string) =>
Promise.resolve({
firstName: 'firstName #1',
lastName: 'lastName #1',
id,
}),
),
remove: jest.fn(),
},
},
],
}).compile();
usersController = app.get<UsersController>(UsersController);
usersService = app.get<UsersService>(UsersService);
});
it('should be defined', () => {
expect(usersController).toBeDefined();
});
describe('create()', () => {
it('should create a user', () => {
expect(usersController.create(createUserDto)).resolves.toEqual({
id: '1',
...createUserDto,
});
expect(usersService.create).toHaveBeenCalled();
expect(usersService.create).toHaveBeenCalledWith(createUserDto);
});
});
describe('findAll()', () => {
it('should find all users ', () => {
usersController.findAll();
expect(usersService.findAll).toHaveBeenCalled();
});
});
describe('findOne()', () => {
it('should find a user', () => {
usersController.findOne('1');
expect(usersService.findOne).toHaveBeenCalled();
expect(usersController.findOne('1')).resolves.toEqual({
firstName: 'firstName #1',
lastName: 'lastName #1',
id: '1',
});
});
});
describe('remove()', () => {
it('should remove the user', () => {
usersController.remove('2');
expect(usersService.remove).toHaveBeenCalled();
});
});
});
The key change was to update the 'providers' property to use an object instead of just using: providers: [CustomersService],.

how to configure all cmmon file in jest?

how to load all common file in jest.config
how to load all common file and 3td party library's like jquery
{
"name": "my-project",
"jest": {
setupFiles:[../src/assert]
}
}
You can use setupFiles in jest.config.js. Assign the jquery and moment to node.js global object. Then, you can get the them in each test cases using global.$ and global.moment.
E.g.
setup.js:
const jquery = function () {
return "I'm fake jquery";
};
const moment = function () {
return "I'm fake moment";
};
global.$ = jquery;
global.moment = moment;
jest.config.js:
module.exports = {
preset: 'ts-jest/presets/js-with-ts',
testEnvironment: 'enzyme',
setupFilesAfterEnv: [
'jest-enzyme',
'./jest.setup.js',
],
setupFiles: [
'/Users/ldu020/workspace/github.com/mrdulin/react-apollo-graphql-starter-kit/stackoverflow/61727628/setup.js',
],
testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'],
verbose: true,
};
a.test.js:
describe('61727628', () => {
describe('a', () => {
it('should pass', () => {
console.log('global.$:', global.$);
console.log('global.moment:', global.moment);
expect(1 + 1).toBe(2);
});
});
});
b.test.js:
describe('61727628', () => {
describe('b', () => {
it('should pass', () => {
console.log('global.$:', global.$);
console.log('global.moment:', global.moment);
expect(1 + 1).toBe(2);
});
});
});
unit test results:
PASS stackoverflow/61727628/a.test.js
61727628
a
✓ should pass (28ms)
console.log
global.$: function () {
return "I'm fake jquery";
}
at Object.<anonymous> (stackoverflow/61727628/b.test.js:4:15)
console.log
global.$: function () {
return "I'm fake jquery";
}
at Object.<anonymous> (stackoverflow/61727628/a.test.js:4:15)
console.log
global.moment: function () {
return "I'm fake moment";
}
at Object.<anonymous> (stackoverflow/61727628/a.test.js:5:15)
console.log
global.moment: function () {
return "I'm fake moment";
}
at Object.<anonymous> (stackoverflow/61727628/b.test.js:5:15)
PASS stackoverflow/61727628/b.test.js
61727628
b
✓ should pass (28ms)
Test Suites: 2 passed, 2 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 6.026s, estimated 16s

React unit test with jest in es6

I am pretty new in react world and trying to write simple friendslist application. I wrote my friends store in es6 style and using babel as transpiler from es5 to es6.
import AppDispatcher from '../dispatcher/app_dispatcher';
import { EventEmitter } from 'events';
import FRIENDS_CONST from '../constants/friends';
const CHANGE_EVENT = 'CHANGE';
let friendsList = [];
let add = (name) => {
let counter = friendsList.length + 1;
let newFriend = {
id: counter,
name: name
};
friendsList.push(newFriend);
}
let remove = (id) => {
let index = friendsList.findIndex(e => e.id == id);
delete friendsList[index];
}
let FriendsStore = Object.assign({}, EventEmitter.prototype, {
getAll: () => {
return friendsList;
},
emitChange: () => {
this.emit(CHANGE_EVENT);
},
addChangeListener: (callback) => {
this.on(CHANGE_EVENT, callback);
},
removeChangeListener: (callback) => {
this.removeListener(CHANGE_EVENT, callback);
}
});
AppDispatcher.register((action) => {
switch (action.actionType) {
case FRIENDS_CONST.ADD_FRIENDS:
add(action.name);
FriendsStore.emitChange();
break;
case FRIENDS_CONST.REMOVE_FRIENDS:
remove(action.id);
FriendsStore.emitChange();
break;
}
});
export default FriendsStore;
Now I want to test my store and wrote the unit test also in es6
jest.dontMock('../../constants/friends');
jest.dontMock('../friends_store');
describe('FriendsStore', () => {
import FRIENDS from '../../constants/friends';
import AppDispatcher from '../../dispatcher/AppDispatcher';
import FriendsStore from '../friends_store';
let FakeAppDispatcher;
let FakeFriendsStore;
let callback;
let addFriends = {
actionType: FRIENDS.ADD_FRIENDS,
name: 'Many'
};
let removeFriend = {
actionType: FRIENDS.REMOVE_FRIENDS,
id: '3'
};
beforeEach(function() {
FakeAppDispatcher = AppDispatcher;
FakeFriendsStore = FriendsStore;
callback = AppDispatcher.register.mock.calls[0][0];
});
it('Should initialize with no friends items', function() {
var all = FriendsStore.getAll();
expect(all).toEqual([]);
});
});
When I execute the test with statement npm test, I've got the error message:
> react-starterify#0.0.9 test /Volumes/Developer/reactjs/app5
> echo "Error: no test specified"
Error: no test specified
What am I doing wrong? The file structure looks as follow:
I did it following the tutorial:
npm install --save-dev jest babel-jest babel-preset-es2015 babel-preset-react react-test-renderer
then
add to package.json
"scripts": {
"test": "jest"
},
"jest": {
"testPathDirs": [
"src/main/resources/web_pages/__tests__"
]
},
Result:
PASS src/main/resources/web_pages/__tests__/modules/utils/ValidationUtil.spec.js (5.214s)
✓ ValidateEmail (5ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 6.092s, estimated 7s
Ran all test suites.
To test ES6 syntax and JSX files, they need to be transformed for Jest. Jest has a config variable where you can define a preprocessor (scriptPreprocessor). You can use the babel-jest preprocessor:
Make the following changes to package.json:
{
"devDependencies": {
"babel-jest": "*",
"jest-cli": "*"
},
"scripts": {
"test": "jest"
},
"jest": {
"scriptPreprocessor": "<rootDir>/node_modules/babel-jest",
"testFileExtensions": ["es6", "js"],
"moduleFileExtensions": ["js", "json", "es6"]
}
}
And run:
$ npm install

Resources