I have an application that uses vue and express and I have tests written for each, I can run either a vue test on it's own or the express test on it's own.
Below is the jest config file this will run fine for Vue, if I remove the preset it will work fine for my supertest/express test .
module.exports = {
// TODO: This line is needed for vue tests but breaks js tests
preset: "#vue/cli-plugin-unit-jest",
coverageDirectory: "test-reports/",
coveragePathIgnorePatterns: [
"/node_modules/"
],
reporters: [
"default",
[
"jest-junit",
{
outputName: "vue_junit.xml",
outputDirectory: "test-reports/",
},
],
],
};
For reference here is both the tests if it helps
// Libraries
import Vuetify from "vuetify";
import Vue from "vue";
// Components
import UserBar from "#/components/userBar.vue"
// Utilities
import { createLocalVue, shallowMount } from "#vue/test-utils";
// Setup Up
const localVue = createLocalVue();
const vuetify = new Vuetify();
Vue.use(Vuetify);
describe("userBar.vue", () => {
let wrapper;
beforeEach(() => {
wrapper = shallowMount(UserBar, {
localVue,
vuetify,
});
});
afterEach(() => {
wrapper.destroy();
});
it("wrapper created properly", () => {
expect(typeof wrapper).toBe("object");
});
});
express test
var request = require('supertest');
import * as local from '../../server';
var express = require('express');
var app = express();
describe('loading express', function () {
var server;
beforeEach(function () {
delete require.cache[require.resolve('../../server/index')];
server = local.default(app);
});
afterEach(function (done) {
server.close(done);
});
it('responds to /client', function testSlashHealth(done) {
request(server)
.get('/client')
.expect(200, done);
});
it('404 everything else', function testPath(done) {
request(server)
.get('/foo/bar')
.expect(404, done);
});
});
Got around this by specifying 2 config files when testing
jest -c jest.config.express.js
jest -c jest.config.vue.js
Related
Here is my code:
babel.config.cjs
module.exports = {
plugins: ['#babel/plugin-transform-modules-commonjs'],
presets: [['#babel/preset-env', { targets: { node: 'current' } }]]
}
jest.config.cjs
module.exports = {
globals: {
"ts-jest": {
isolatedModules: true,
},
},
};
Setup.ts
import awilix from 'awilix';
import express from 'express';
import CognitoSC from './Service/Cognito.js'
import authController from './Controller/Auth.js';
const app = express();
const container = awilix.createContainer({
injectionMode: awilix.InjectionMode.CLASSIC
});
auth.test.ts
import request from 'supertest';
import app from '../Setup.js';
describe('Testing Authentication/Authorization', function() {
it('responds with json', async function() {
const response = await request(app)
.post('/signin')
.send('username=Test')
.send('password=Testtest123$')
expect(response.status).toEqual(200);
expect(response.body.data.success).toEqual(true);
});
});
and I build it with tsc ("module": "es2022"). When I run npx jest I get an error saying
TypeError: Cannot read properties of undefined (reading 'createContainer')
> 8 | const container = awilix.createContainer({
Interesting thing that I noticed is that if I open Setup.js file which is generated by tsc and change the code from
import awilix from 'awilix';
to
const awilix = require('awilix');
and run npx jest, the tests pass without a problem. I'm little lost and can't figure out what is the problem. I also checked inside Setup.js and express is imported without a problem using ES6 syntax but why it can't do the same with awilix?
It doesn't appear that awilix supports modules.
From their readme they say to use const awilix = require('awilix') .
See here for more information about import/require/ES modules/commonJS.
I am testing a nodejs express API with a PG db wrapped with Prisma ORM
I configured a testing singleton instance as described by Prisma docs
Since the API is implemented in CommonJS and not TS, I had to make some changes as described in this beautiful page.
Here is a synthesis of what I did, will try to make it short, so it's easier to read
orgs.js (A GET route served by the mock server later on ...)
const Router = require('express-promise-router')
const router = new Router()
const PrismaPool = require('../db/PrismaPool');
module.exports = router
router.get('/assessments', async (req, res) => {
try{
const prisma = PrismaPool.getInstance();
const data = await prisma.org.findUnique({
select:{
assessments:true,
},
where: {
id: res.locals.orgId,
},
})
res.send(data)
}
catch(err){
handleError(err, "[GET]/orgs/assessments", 400, req, res)
}
})
PrismaPool.js (A wrapper to access the unique prisma client instance)
const prisma = require('./PrismaClientInstance').default
class PrismaPool {
constructor() {
throw new Error('Use PrismaPool.getInstance()');
}
static getInstance() {
return prisma
}
}
module.exports = PrismaPool;
PrismaClientInstance.js The unique instance of PrismaClient class. This is the tricky part stitching between the CommonJS world and the TS world.
'use strict';
exports.__esModule = true;
const { PrismaClient } = require('#prisma/client')
const prisma = new PrismaClient()
exports['default'] = prisma;
All this configuration works GREAT at runtime, now, when wrapping it with JEST in unit tests, things go south quickly ...
mock_server.js (a simplified server to expose the orgs API above)
const http = require('http');
const express = require('express');
var orgsRouter = require('../orgs');
const app = express();
app.use('/orgs', orgsRouter);
const port = 3011
app.set('port', port);
const server = http.createServer(app);
function onError(error) {
// herror handling
}
function onListening() {
// some debug messages
}
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);
module.exports = server
PrismaSingletonForTesting.ts (A jest deep mock of the PrismaClient instance)
import { PrismaClient } from '#prisma/client'
import { mockDeep, mockReset, DeepMockProxy } from 'jest-mock-extended'
import prisma from './PrismaClientInstance'
jest.mock('./PrismaClientInstance', () => ({
__esModule: true,
default: mockDeep<PrismaClient>()
}))
beforeEach(() => {
mockReset(prismaMock)
})
export const prismaMock = prisma as unknown as DeepMockProxy<PrismaClient>
orgs.test.js (The tests of the orgs API)
const Request = require("request")
const { prismaMock } = require('../../db/PrismaSingletonForTesting')
const TEST_ORGID = 1
describe('egrest server', () => {
let server
beforeAll(() => {
server = require('./test_server')
})
afterAll(() => {
server.close()
})
describe('assessments', () => {
let data = {}
beforeAll(() => {
const testorg = {
id: TEST_ORGID,
name: 'jestest',
admin:33,
avail_tests: 1234
}
prismaMock.org.findUnique.mockResolvedValue(testorg)
})
it(`read remaining assessments for org ${TEST_ORGID}`, (done) => {
Request.get("http://localhost:3011/orgs/assessments", (error, response, body) => {
data.status = response.statusCode
data.body = body
data.error = error
console.dir(body)
done()
})
})
})
})
I also configured .jest.config with the required line setupFilesAfterEnv: ['./db/PrismaSingletonForTesting.ts']
When I run this test, I get data=undefined in orgs.js, even-though I mocked prisma.org.findUnique by doing prismaMock.org.findUnique.mockResolvedValue(testorg) as described by prisma docs.
Any help would be appreciated.
I added tests to my node js project using jest but for each test suite there's a beforeAll method that creates a new test server and connects to a mongo database and an afterAll method that closes both test server and the database. I would like to perform the above tasks globally for all the test suites not one at a time. Below is a sample of my code.
app.js
const express = require("express");
const app = express();
const { connectToDb } = require("./startup/db");
require("./startup/routes")(app);
connectToDb();
...
const port = process.env.PORT || 3000;
if (process.env.NODE_ENV !== "test") {
app.listen(port, () => winston.info(`Listening on port ${port}...`));
}
module.exports = app;
auth.test.js
const request = require("supertest");
const http = require("http");
const { disconnectDb } = require("../../startup/db");
describe("auth middleware", () => {
let server;
beforeAll((done) => {
const app = require("../../app");
server = http.createServer(app);
server.listen(done);
});
afterAll((done) => {
server.close(done);
disconnectDb();
});
it("should return 401 if no token is provided", async () => {
const res = request(server)
.post("/api/genres")
.set("x-auth-token", "")
.send({ name: "genre1" });
expect(res.status).toBe(401);
});
...
jest.config.js
module.exports = {
testEnvironment: "node",
};
Try with this jest.config.js:
module.exports = {
testEnvironment: "node",
globalSetup: '<rootDir>/src/testSetup.ts'
};
And in testSetup.ts you can do:
// testSetup.ts
module.exports = async () => {
const app = require("../../app");
server = http.createServer(app);
server.listen(done);
};
use this config: setupFiles: ['./tests/setup.js']
your setup file should look like this:
// setup.js
(async () => {
const app = require('../app.js')
global.app = app
})()
then you will be able to use app globally in every test suite
I had the same problem, I wanted to make one database connection before all test files and close the connection after all tests in all files.
But....I did not achieve what I wanted and MAYBE we don't need to do this.
I found a solution to launch functions beforeAll(),afterAll() etc... really before ALL TEST FILES and after ALL TEST FILES etc..
So you define these functions once in a certain file and they run for every test file.
To do that, all we need is to create a setupFile.ts and add path to this file in jest.config or in package.json "setupFilesAfterEnv": ["<rootDir>/__tests__/settings/setupTests.ts"],
Here is an example of my jest configuration.
"jest": {
"preset": "ts-jest",
"testEnvironment": "node",
"setupFilesAfterEnv": ["<rootDir>/__tests__/settings/setupTests.ts"],
"rootDir": "src",
"verbose": true,
"clearMocks": true,
"testMatch": [
"**/**/*.test.ts"
]
},
Here is an example of setupFile.ts
import usersCollection from "../../database/user-schema";
import mongoose from "mongoose";
beforeAll(async () => {
try {
await mongoose.connect(process.env.MONGODB_URL!);
await usersCollection.deleteMany({});
} catch (error) {
console.log(error);
}
});
afterAll(async () => {
try {
await mongoose.disconnect();
} catch (error) {
console.log(error);
}
});
It means that we will establish a connection to the database FOR EVERY TEST FILE BEFORE ALL TESTS IN THAT FILE and close connection after all tests in every test file.
What I realized for myself:
In real life we have many test files and not every file needs a connection to a database.
It's perfectly fine to open a connection to a database in files which need a connection and close after all tests in that file, for example integration tests when we test API endpoints.
In other tests to not use real database for many unit tests we can consider to mock(simulate) a database. It's another very interesting topic 😊
If I say something wrong you can correct me
P.S
I also want to mention what is written in the Mongoose documentation
Do not use globalSetup to call mongoose.connect() or
mongoose.createConnection(). Jest runs globalSetup in a separate
environment, so you cannot use any connections you create in
globalSetup in your tests.
https://mongoosejs.com/docs/jest.html
I'm testing the Express application with Jest and came across a slight problem - the module uses variable that is initialized before the test execution, here is my app.js file:
const app = express();
const isDev = process.env.NODE_ENV === 'development';
app.get('*', (req, res, next) => {
if (isDev) {
res.status(404).json({ error: 'Wrong URL' });
} else {
res.sendFile(path.join(__dirname, '../index.html'));
}
});
app.use(errorHandler);
module.exports = app;
When I run Jest tests, my process.env.NODE_ENV is equal to test, that's why I can't cover the first if condition, where isDev is true.
I've tried to reassign process.env.NODE_ENV before the test request - it works, but as isDev variable initialization has been done before test execution, it didn't work.
This is my test:
const request = require('supertest');
const app = require('../app');
describe('GET /*', () => {
const OLD_ENV = process.env;
beforeEach(() => {
// Clear JEST cache
jest.resetModules();
process.env = { ...OLD_ENV };
Reflect.deleteProperty(process.env, 'NODE_ENV');
});
test('Not existing path (development env) - 404 status', async () => {
process.env.NODE_ENV = 'development';
const response = await request(app).
get('/wrongUrl');
expect(response.status).toBe(404);
});
});
How can I mock the isDev variable inside my test?
you can use jest.isolateModules(fn) to app in isolation like this:
describe("GET /*", () => {
describe("on development", () => {
let app;
beforeAll(() => {
process.env.NODE_ENV = "development";
jest.isolateModules(() => {
app = require("../app");
});
});
it("should to this", () => {
expect(app).....
});
});
describe("on production", () => {
let app;
beforeAll(() => {
process.env.NODE_ENV = "production";
jest.isolateModules(() => {
app = require("../app");
});
});
it("should to that", () => {
expect(app())....
});
});
});
You could create an .env file just for testing.
If you are using express you could also use dotenv.
With this package you can import env variables from different files.
Just add this line at the top of your file.
require('dotenv').config({ path: process.cwd() + '/path/to/test.env' });
This way you can always change the env variables you want to use, before every test.
So basically what I'm trying to do is to make the browser refresh whenever there is a change in the files using browserSync, compiles Pug templates, then Parceljs does the bundling. And Gulp is to watch for changes.
The overall objective is a static website/page.
The problem:
If parcel fails building. browserSync exits. Watch stops.
[12:31:41] 'parcel' errored after 3.83 s
[12:31:41] Error in plugin "gulp-parcel"
[12:31:41] The following tasks did not complete: browserSync
[12:31:41] Did you forget to signal async completion?
OS: Windows 10
Thanks!!
Gulpfile.js content:
"use strict";
var gulp = require('gulp');
var parcel = require('gulp-parcel');
var pug = require('gulp-pug');
var browserSync = require('browser-sync').create();
gulp.task('html', function () {
return gulp.src('src/templates/*.pug')
.pipe(pug())
.pipe(gulp.dest('build/html'))
.pipe(browserSync.reload({
stream: true
}));
});
gulp.task('parcel', function () {
return gulp.src('build/html/*.html', {
read: false
})
.pipe(parcel())
.pipe(browserSync.reload({
stream: true
}));
});
gulp.task('browserSync', function () {
browserSync.init({
server: {
baseDir: 'dist'
},
});
});
gulp.task('watch', gulp.parallel('browserSync',gulp.series('html', 'parcel')), function () {
gulp.watch('src/templates/**/*.pug', gulp.series('html', 'parcel'));
});
gulp.task('default', gulp.series('watch'), function(){
console.log('Started default');
});
After investigating a bit, the gulp-parcel plugin had a bug which is still being worked on. Meanwhile, I was able to come up with a workaround.
Upgraded to es6
Implemented 'gulp-run-command' to run Parcel in watch mode
Here is my new solution:
'use strict';
import gulp from 'gulp';
import babel from 'gulp-babel';
import browserSync from 'browser-sync';
import run from 'gulp-run-command';
import log from 'fancy-log';
import errorHandler from 'gulp-error-handle';
const server = browserSync.create();
const paths = {
parcel: {
dist: 'dist/*'
}
};
gulp.task('parcel', run('parcel watch src/templates/index.pug --public-url ./ --no-cache'));
const reload = done => {
server.reload();
done();
};
const serve = done => {
server.init({
server: {
baseDir: 'dist/'
}
});
done();
};
const watch = done => {
gulp.watch(paths.parcel.dist, gulp.series(reload));
done();
};
const dev = gulp.parallel('parcel', serve, watch);
export default dev;