Loopback 4 - Connect to test database on Npm Test - loopbackjs

I want to run an in-memory database for my tests, but I'm unable to make my application connect to it when I run npm test.
When I run npm test i get:
Connection fails: Error: ER_ACCESS_DENIED_ERROR: Access denied for user ''#'172.21.0.1' (using password: NO)
This is happening because I'm not setting any env variables on npm test, but I don't want to use MySQL on my tests and just an in-memory database, here what I have.
testdb.datasources.ts
import {juggler} from '#loopback/repository';
export const testdb: juggler.DataSource = new juggler.DataSource({
name: 'testdb',
connector: 'memory',
});
country.controller.acceptance.ts
import {Client, expect} from '#loopback/testlab';
import {MyApplication} from '../..';
import {setupApplication} from './test-helper';
import {givenEmptyDatabase} from '../helpers/database.helpers';
describe('Country Controller', () => {
let app: MyApplication;
let client: Client;
before('setupApllication', async () => {
({app, client} = await setupApplication());
});
before(givenEmptyDatabase);
// before(givenRunningApp);
after(async () => {
await app.stop();
});
it('Should count 0 countries', async () => {
const res = await client.get('/countries/count').expect(200);
//assertations
expect(res.body.count).to.equal(0);
});
});
test-helper.ts
import {MyApplication} from '../..';
import {
createRestAppClient,
givenHttpServerConfig,
Client,
} from '#loopback/testlab';
import {testdb} from '../fixtures/datasources/testdb.datasource';
export async function setupApplication(): Promise<AppWithClient> {
const app = new MyApplication({
rest: givenHttpServerConfig({host: 'localhost'}),
});
app.dataSource(testdb); // <--- Hoped this would do the job
await app.boot();
await app.start();
const client = createRestAppClient(app);
return {app, client};
}
export interface AppWithClient {
app: MyApplication;
client: Client;
}
The Country controller is just a standard controller create using lb4 controller.
#get('/countries/count', {
responses: {
'200': {
description: 'country model count',
content: {'application/json': {schema: countschema}},
},
},
})
async count(
#param.query.object('where', getwhereschemafor(country)) where?: where,
): promise<count> {
return await this.countryrepository.count(where);
}
Any ideas on whats going wrong?

i Found this way that works for me
Change this:
app.dataSource(testdb);
To this:
await app.bind('datasource.config.db').to({
name: 'db',
connector: 'memory'
});
You are just changing the configurations of datasource to use a local database but is still the same datasource!
make sure that the string of bind is the same used in your main datasource!

Related

NestJS createTestingModule and getHttpServer() error 503

I'm using NestJS 7.6.0 and I'm experiencing a problem with E2E testing.
I have a very simple module that exposes a /health route in its controller. I'd like to test this route using the E2E testing feature of nestJs documented here:
https://docs.nestjs.com/fundamentals/testing#end-to-end-testing
I'm currently using the quite same code:
describe('AppController (e2e)', () => {
let app: INestApplication;
beforeAll(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule]
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
});
it('/health (GET)', async() => {
return await request(app.getHttpServer())
.get('/health')
.expect(200)
});
});
If I run this test with npm run test I have this error:
npm run test
> dv-ewok-api#0.0.1 test ....
> jest
[Nest] 54638 - 28/06/2021, 14:53:30 [ExceptionHandler] Health Check has failed! {"ready":{"status":"down","message":"connect ECONNREFUSED 127.0.0.1:3000"}}
FAIL src/health/health.controller.spec.ts
● AppController (e2e) › /health (GET)
expected 200 "OK", got 503 "Service Unavailable"
32 | return await request(app.getHttpServer())
33 | .get('/health')
> 34 | .expect(200)
| ^
35 |
36 | });
37 | });
It seems that the httpServer is not running.
To make it work I have to modify the code like this:
...
beforeAll(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule]
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
await app.listen(3000); // <===== ADD THIS LINE
});
...
I just added a app.listen(3000) and the test passed. I don't know if this is an oversight in the documentation or if I did it wrong.
Thank you for your help.
Edit for adding some more details
For the HealthCheck part, I'am using the NestJS Terminus integration described here.
So, my app.module looks like this :
import { Module } from '#nestjs/common';
import { TerminusModule } from '#nestjs/terminus';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { HealthController } from './health/health.controller';
#Module({
imports: [TerminusModule],
controllers: [AppController, HealthController],
providers: [AppService],
})
export class AppModule {}
As you can see, I import the TerminusModule and I declare my 2 controllers: AppController and HealthController.
In AppController I have just a /ping route that says "hello world"
import { Controller, Get } from '#nestjs/common';
import { AppService } from './app.service';
#Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
#Get('/ping')
getHello(): string {
return this.appService.getHello();
}
}
And in HealthController I have just the /health route that calls the /ping on localhost.
import { Controller, Get } from "#nestjs/common";
import { HealthCheck, HealthCheckService, HttpHealthIndicator } from "#nestjs/terminus";
#Controller('health')
export class HealthController {
constructor(
private health: HealthCheckService,
private http: HttpHealthIndicator,
) {}
#Get()
#HealthCheck()
check() {
return this.health.check([
() => this.http.pingCheck('ping', 'http://localhost:3000/ping'),
]);
}
}
And if you run tests with npm run test, it shows the 503 error linked above. To make it work, I need to add await app.listen(3000); in the beforeAll() method right after the init() (cf. above).
I think my problem comes from "terminus" because if I start a new NestJS project which does not use Terminus, e2e testing works fine, without the need to add the .listen().
Do I need to do something special in the createTestingModule() with TerminusModule?
Thank you

Vue Jest - Create Mock Api server

I want to create a Mock API Server for my Jest tests so that I can define all my backend endpoints and create responses and authentication checks.
I have managed to set up the server and routes by following some of the source code from Chris Fritz "Vue-Enterprice-boilerplate":
https://github.com/chrisvfritz/vue-enterprise-boilerplate/tree/master/tests/unit
// jest.config.js
const _ = require("lodash");
process.env.MOCK_API_PORT = process.env.MOCK_API_PORT || _.random(9000, 9999);
module.exports = {
preset: "#vue/cli-plugin-unit-jest",
setupFiles: ["./tests/unit/setup"],
globalSetup: "<rootDir>/tests/unit/global-setup",
globalTeardown: "<rootDir>/tests/unit/global-teardown",
testMatch: ["**/(*.)spec.js"],
moduleFileExtensions: ["js", "jsx", "json", "vue"],
transform: {
"^.+\\.vue$": "vue-jest",
"^.+\\.js$": "babel-jest",
".+\\.(css|scss|jpe?g|png|gif|webp|svg|mp4|webm|ogg|mp3|wav|flac|aac|woff2?|eot|ttf|otf)$":
"jest-transform-stub"
},
transformIgnorePatterns: ["/node_modules/(?!vue-spinner)"],
testURL: process.env.API_BASE_URL || `http://localhost:${process.env.MOCK_API_PORT}`
};
The server runs when the tests starts and I can console log the route files.
I just don't know how the axios call from my Vuex would go with the mock API instead of the real one.
Might need to import axios somewhere in the test to prevent the development URL to be used?
/tests/mock-api/routes/auth.js
const Users = require("../resources/users");
module.exports = app => {
console.log('I can see this during tests!');
app.post("/api/v1/login", async (req, res) => {
console.log("I don't see this..");
await Users.authenticate(req.body)
.then(user => {
res.json(user);
})
.catch(error => {
res.status(401).json({ message: error.message });
});
});
});
// /views/Login.spec.js
import Vue from "vue";
import Vuelidate from "vuelidate";
import Login from "#/views/Login";
import BaseButton from "#/components/Globals/_base-button.vue";
import BaseInput from "#/components/Globals/_base-input.vue";
import BaseLabel from "#/components/Globals/_base-label.vue";
import flushPromises from "flush-promises";
import store from "#/store";
import { shallowMount } from "#vue/test-utils";
Vue.use(Vuelidate);
describe("#/views/Login", () => {
// other tests..
it("redirects to posts on successful login", async () => {
const wrapper = shallowMount(Login, { store, stubs: { BaseInput, BaseButton, BaseLabel } });
wrapper.vm.$v.$touch();
const spyDispatch = jest.spyOn(wrapper.vm.$store, "dispatch");
const username = wrapper.find("#username");
const password = wrapper.find("#password");
username.element.value = "johndoe#email.com";
password.element.value = "passwordz";
username.trigger("input");
password.trigger("input");
await wrapper.find("#submitBtn").trigger("click.prevent");
await wrapper.vm.$nextTick();
await flushPromises();
await expect(spyDispatch).toHaveBeenCalledWith("auth/login", {
username: username.element.value,
password: password.element.value
});
// #TODO add expect for redirect as well
});
// /store/auth.js (vuex)
export const actions = {
async login({ commit }, { username, password }) {
console.log("I see this");
const response = await axios.post("/login",
{ username, password }, { withCredentials: true });
console.log("I don't see this");
// #TODO error handling
if (!response) return;
commit("setUser", { ...response.data.user });
router.push({ name: "Posts" });
},
The login action gets called but I don't get passed the axios.post.
Do I need to import axios somewhere to make sure I get a fresh instance? (Vuex uses one I set the baseURL and headers)
All the other tests and logic works except this.

Jest with moxios keeps timing out when I use custom axios instance

I have a service that uses a custom axios instance that I am trying to test but I keep getting an error.
Here is the error:
: Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout.
Here is the test:
import moxios from 'moxios';
import NotificationService, { instance } from '../NotificationService';
beforeEach(() => {
moxios.install(instance);
});
afterEach(() => {
moxios.uninstall(instance);
});
const fetchNotifData = {
data: {
bell: false,
rollups: []
}
};
describe('NotificationService.js', () => {
it('returns the bell property', async done => {
const isResolved = true;
const data = await NotificationService.fetchNotifications(isResolved);
moxios.wait(() => {
let request = moxios.requests.mostRecent();
console.log(request);
request
.respondWith({
status: 200,
response: fetchNotifData
})
.then(() => {
console.log(data);
expect(data).toHaveProperty('data.bell');
done();
});
});
});
});
And here is the code that I'm trying to test:
import axios from 'axios';
// hardcoded user guid
const userId = '8c4';
// axios instance with hardcoded url and auth header
export const instance = axios.create({
baseURL: 'hidden',
headers: {
Authorization:
'JWT ey'
});
/**
* Notification Service
* Call these methods from the Notification Vuex Module
*/
export default class NotificationService {
/**
* #GET Gets a list of Notifications for a User
* #returns {AxiosPromise<any>}
* #param query
*/
static async fetchNotifications(query) {
try {
const res = await instance.get(`/rollups/user/${userId}`, {
query: query
});
console.log('NotificationService.fetchNotifications()', res);
return res;
} catch (error) {
console.error(error);
}
}
}
I've tried shortening the jest timeout and that did not work. I think it is moxios not installing the axios instance properly, but I can't find any reason why it wouldn't.
Any help is appreciated, thanks in advance.
have you tried changing the Jest environment settings by adding this to your test file?
/**
* #jest-environment node
*/
import moxios from 'moxios';
...
Jest tends to prevent the requests from going out unless you add that. Either way, I use nock instead of moxios and I recommend it.

Cannot log after tests are done in jestjs

I have written test cases for signin API using jest. After completing all five test of a test suit jest give me following error in log.
Can any body tell Why it is So and how to fix it?
CODE:(signup.test.ts)
import request from 'supertest';
import { TYPES } from '../src/inversify.types'
import { Application } from '../src/app/Application'
import { container } from '../src/inversify.config'
import dotenv from 'dotenv'
import { RESPONSE_CODE } from '../src/utils/enums/ResponseCode'
import { RESPONSE_MESSAGES } from '../src/utils/enums/ResponseMessages'
import { UserSchema } from '../src/components/user/User';
// import jwt from 'jsonwebtoken';
var application: Application
describe("POST / - SIGNUP endpoint", () => {
// var testusers: any;
//This hook is executed before running all test cases, It will make application instance, make it to listen
// on it on port 3000 and add test document in DB
beforeAll(async () => {
// Make enviroment variables available throughout the application
dotenv.config();
// Getting application instance using iversify container
application = container.get<Application>(TYPES.Application);
// Initialize frontside of application
await application.bootstrap();
// Starting Application server on given port
await application.listen(3000);
});
afterAll(
//This hook is executed after running all test cases and delete test document in database
async () =>{
const res = await UserSchema.deleteMany({ Name: { $in: [ "Test User", "Test" ] } });
// `0` if no docs matched the filter, number of docs deleted otherwise
console.log('---------------------->>>>>>>>>>>>>>>>>>>', (res as any).deletedCount);
}
)
it("Signup for user that don\'t exists", async () => {
const response = await request(application.getServer()).post('/user/signup')
.send({
"Email": JSON.parse(process.env.TEST_USER).Email,
"Name": "Test User",
"Password": process.env.TEST_ACCOUNTS_PASSWORD
})
expect(response.status).toBe(RESPONSE_CODE.CREATED);
expect(JSON.parse(response.text)).toEqual(expect.objectContaining({
Message: RESPONSE_MESSAGES.ADDED_SUCESSFULLY,
Data: expect.objectContaining({
Name: 'Test User',
Country: '',
PhoneNumber: '',
// Password: '$2b$10$nIHLW/SA73XLHoIcND27iuODFAArOvpch6FL/eikKT78qbShAl6ry',
Dob: '',
Role: 'MEMBER',
IsEmailVerified: false,
IsBlocked: 'ACTIVE',
IsTokenSent: false,
twoFAStatus: false,
// _id: '5c812e2715e0711b98260fee',
Email: JSON.parse(process.env.TEST_USER).Email
})
})
);
console.log('*** Signup for user that don\'t exists *** response', response.text, 'response status', response.status);
});
it("Signup for user that exists", async () => {
const response = await request(application.getServer()).post('/user/signup')
.send({
"Email": JSON.parse(process.env.TEST_USER).Email,
"Name": "Test User",
"Password": process.env.TEST_ACCOUNTS_PASSWORD
})
expect(response.status).toBe(RESPONSE_CODE.CONFLICT);
expect(JSON.parse(response.text)).toEqual({
Message: RESPONSE_MESSAGES.ALREADY_EXISTS
})
console.log('*** Signup for user that don\'t exists *** response', response.text, 'response status', response.status);
});
});
Jest did not exit one second after the test run has completed.
This usually means that there are asynchronous operations that weren't
stopped in your tests. Consider running Jest with
--detectOpenHandles to troubleshoot this issue.
Cannot log after tests are done. Did you forget to wait for something
async in your test?
Attempted to log "{ accepted: [ 'unverifiedtestuser#abc.com' ],
rejected: [],
envelopeTime: 621,
messageTime: 867,
messageSize: 906,
response: '250 2.0.0 OK 1551945300 f6sm5442066wrt.87 - gsmtp',
envelope:
{ from: 'abc#gmail.com',
to: [ 'unverifiedtestuser#abc.com' ] },
messageId: '<45468449-b5c8-0d86-9404-d55bb5f4g6a3#gmail.com>' }".
at CustomConsole.log (node_modules/jest-util/build/CustomConsole.js:156:10)
at src/email/MailHandler.ts:2599:17
at transporter.send.args (node_modules/nodemailer/lib/mailer/index.js:226:21)
at connection.send (node_modules/nodemailer/lib/smtp-transport/index.js:247:32)
at callback (node_modules/nodemailer/lib/smtp-connection/index.js:435:13)
at stream._createSendStream (node_modules/nodemailer/lib/smtp-connection/index.js:458:24)
at SMTPConnection._actionSMTPStream (node_modules/nodemailer/lib/smtp-connection/index.js:1481:20)
at SMTPConnection._responseActions.push.str (node_modules/nodemailer/lib/smtp-connection/index.js:968:22)
at SMTPConnection._processResponse (node_modules/nodemailer/lib/smtp-connection/index.js:764:20)
at SMTPConnection._onData (node_modules/nodemailer/lib/smtp-connection/index.js:570:14)
I was using the react-native default test case (see below) when Cannot log after tests are done happened.
it('renders correctly', () => {
renderer.create(<App />);
});
Apparently, the problem was that the test ended but logging was still needed. So I tried to make the callback in the test case async, hoping that the test won't terminate immediately:
it('renders correctly', async () => {
renderer.create(<App />);
});
And it worked. However, I have very little clue what the inner working is.
If you are using async/await type in your code, then this error can occur when you are calling async function without await keyword.
In my case, I have defined a function like this below,
async getStatistics(headers) {
....
....
return response;
}
But I have called this method like getStatistics(headers) instead of await getStatistics(headers).
When I included await, it worked fine and the issue resolved.
In my case while using nodejs + jest + supertest the problem was that when I import app from "./app" to my test file to do some stuff with supertest (request(app)), I actually import with app.listen() , because when I'm exporting app, export takes in account app.listen() too, but we don't need app.listen() in tests and it throws an error
"Cannot log after tests are done.Did you forget to wait for something async in your test?"
Here is all in one file(that's the problem!)
const app = express();
app.use(express.json());
// ROUTES
app.get("/api", (req, res) => {
res.json({ message: "Welcome to Blog API!" });
});
app.use("/api/users", usersRoutes);
app.use("/api/blogs", blogsRouter);
// The server will start only if the connection to database is established
mongoose
.connect(process.env.MONGO_URI!)
.then(() => {
console.log("MongoDB est connecté");
const port = process.env.PORT || 4000;
app.listen(port, () => console.log(`The server is running on port: ${port}`));
})
.catch(err => {
console.log(err);
});
export default app;
To solve this issue I created 2 separate folders:
// 1) app.ts
Where I put all stuff for my const app = express(), routes etc and export app
dotenv.config();
const app = express();
app.use(express.json());
// ROUTES
app.get("/api", (req, res) => {
res.json({ message: "Welcome to Blog API!" });
});
app.use("/api/users", usersRoutes);
app.use("/api/blogs", blogsRouter);
export default app;
// 2) index.ts
Where I put app.listen() and mongoose.connection() and import app
*import mongoose from "mongoose";
import app from "./app";
// The server will start only if the connection to database is established
mongoose
.connect(process.env.MONGO_URI!)
.then(() => {
console.log("MongoDB est connecté");
const port = process.env.PORT || 4000;
app.listen(port, () => console.log(`The server is running on port: ${port}`));
})
.catch(err => {
console.log(err);
});*
For me I needed to add an await before the expect() call also to stop this error (and an async before the test() callback function).
Also caused and fixed Jest not detecting coverage on the lines in the code throwing the error!
test("expect error to be thrown for incorrect request", async () => {
await expect(
// ^ added this
async () => await getData("i-made-this-up")
).rejects.toThrow(
"[API] Not recognised: i-made-this-up"
);
});
getData() returns an Axios call and in this case an error is caught by catch and re-thrown.
const getData = async (id) => {
return await axios
.get(`https://api.com/some/path?id=${id}`)
.then((response) => response.data)
.catch((error) => {
if (error?.response?.data?.message) {
console.error(error) // Triggered the error
throw new Error("[API] " + error.response.data.message);
}
throw error;
});
};
This happened to me because I had an infinite loop while (true). In my case, I was able to add a method for setting the value of the loop based on user input, rather than defaulting to true.
In my case, the error was caused by asynchronous Redis connection still online. Just added afterall method to quit Redis and could see the log again.
Working on Typescript 4.4.2:
test("My Test", done => {
let redisUtil: RedisUtil = new RedisUtil();
let redisClient: Redis = redisUtil.redis_client();
done();
});
afterAll(() => {
redisClient.quit();
});
I solved it with the env variables:
if (process.env.NODE_ENV !== 'test') {
db.init().then(() => {
app.listen(PORT, () => {
console.log('API lista por el puerto ', PORT)
})
}).catch((err) => {
console.error(err)
process.exit(1)
})
} else {
module.export = app
}
I faced same warnings. However the fix is bit weird:
The jest unit test script import a function (which is not export from src/). After I added the export to the function to be tested. The error disappears.
I had a similar issue:
Cannot log after tests are done. Did you forget to wait for something async in your test?
Attempted to log "Warning: You seem to have overlapping act() calls, this is not supported. Be sure to await previous act() calls before making a new one. ".
It was due to a missing static keyword. This code caused the issue:
class MyComponent extends React.Component<Props, State> {
propTypes = {
onDestroy: PropTypes.func,
}
}
It should have been:
class MyComponent extends React.Component<Props, State> {
static propTypes = {
onDestroy: PropTypes.func,
}
}

Im trying to mock a function from a service but Jest keeps calling the actual function instead of the mock function

I'm using Jest to test a function from a service that uses axios to make some api calls. The problem is that Jest keeps calling the actual services function instead of the mocked service function. Here is all of the code:
The tests:
// __tests__/NotificationService.spec.js
const mockService = require('../NotificationService').default;
beforeEach(() => {
jest.mock('../NotificationService');
});
describe('NotificationService.js', () => {
it('returns the bell property', async () => {
expect.assertions(1);
const data = await mockService.fetchNotifications();
console.log(data);
expect(data).toHaveProperty('data.bell');
});
});
The mock:
// __mocks__/NotificationService.js
const notifData = {
bell: false,
rollups: [
{
id: 'hidden',
modifiedAt: 123,
read: true,
type: 'PLAYLIST_SUBSCRIBED',
visited: false,
muted: false,
count: 3,
user: {
id: 'hidden',
name: 'hidden'
},
reference: {
id: 'hidden',
title: 'hidden',
url: ''
}
}
],
system: [],
total: 1
};
export default function fetchNotifications(isResolved) {
return new Promise((resolve, reject) => {
process.nextTick(() =>
isResolved ? resolve(notifData) : reject({ error: 'It threw an error' })
);
});
}
The service:
import axios from 'axios';
// hardcoded user guid
export const userId = 'hidden';
// axios instance with hardcoded url and auth header
export const instance = axios.create({
baseURL: 'hidden',
headers: {
Authorization:
'JWT ey'
}
});
/**
* Notification Service
* Call these methods from the Notification Vuex Module
*/
export default class NotificationService {
/**
* #GET Gets a list of Notifications for a User
* #returns {AxiosPromise<any>}
* #param query
*/
static async fetchNotifications(query) {
try {
const res = await instance.get(`/rollups/user/${userId}`, {
query: query
});
return res;
} catch (error) {
console.error(error);
}
}
}
I've tried a couple of variations of using require instead of importing the NotificationService, but it gave some other cryptic errors...
I feel like I'm missing something simple.
Help me please :)
The problem is that Jest keeps calling the actual services function instead of the mocked service function.
babel-jest hoists jest.mock calls so that they run before everything else (even import calls), but the hoisting is local to the code block as described in issue 2582.
I feel like I'm missing something simple.
Move your jest.mock call outside the beforeEach and it will be hoisted to the top of your entire test so your mock is returned by require:
const mockService = require('../NotificationService').default; // mockService is your mock...
jest.mock('../NotificationService'); // ...because this runs first
describe('NotificationService.js', () => {
it('returns the bell property', async () => {
...
});
});