I am trying to organise my mocha tests to run through a separate test runner. Whenever I run the test, console.log outputs a proper connection in the top level before block, but straight after outputs me null in the separate required file it block. The hook is being executed, and it sets the connection variable correctly, but somehow it does not get passed to the required file.
How come the connection does not get properly set? Confusingly, according to my debugger it block is executed before the before hook which contradicts the order of console.logs I see
describe.only('test-suite', async () => {
let connection; // undefinded at this point
before(async () => {
connection = await getConnection();
console.log(connection); -> proper connection instance
});
after(async () => {
await closeConnection();
});
require('./some/test')(
connection
);
});
./some/test.js
module.exports = async (
connection,
) => {
describe('my-method', async () => {
it('does things', async () => {
console.log(connection); // somehow undefined
});
});
};
This is because of the way JS handles references to objects. Reassigning the variable does not just change the value the reference points to, but instead creates a brand new reference pointing to a brand new value.
This is the workaround:
describe.only('test-suite', async () => {
let options = { connection: null};
before(async () => {
options.connection = await getConnection();
console.log(connection);
});
after(async () => {
await closeConnection();
});
require('./some/test')(
options
);
});
./some/test.js
module.exports = async (
options,
) => {
describe('my-method', async () => {
it('does things', async () => {
console.log(options.connection);
});
});
};
Related
I am trying to use await in my test and when I run the test runner the test becomes stuck and no test result is returned. Here is my code
describe("retrieveCandidate", () => {
describe("when settings are found", () => {
beforeEach(() => {
configurationApi = new ConfigurationApi(BaseUrl);
});
afterEach(() => {
configurationApi = undefined;
});
it("should return set of configuration values", () => {
const configurationValueSet: IConfigurationValueSet | undefined =
await configurationApi?.retrieveCandidate(controlConfigurationValueSet.specializationKey);
expect(true).toEqual(true);
});
});
So in my testsetup file I had
jest.useFakeTimers();
This caused my async functions to never return anything and caused my test to be in a frozen state
I have been trying to write a test suite to my node.js API project and one of their requirements is to control when the server starts and stops. For that, I wrote this code below with two functions: initializeWebServer and stopWebServer.
express.js
const initializeWebServer = () => {
return new Promise((resolve, reject) => {
app = express();
/* some middlewares */
app.use('/', userRoutes);
httpServer = http.createServer(app);
httpServer.listen(3000, (err) => {
if (err) {
reject(err);
return;
}
resolve(app);
});
});
};
const stopWebServer = () => {
return new Promise((resolve, reject) => {
httpServer.close(() => { resolve(); });
});
};
Using mocha to run my tests, I choose to manage my server connection with before and after hooks, using async/await syntax.
user.spec.js
let axiosAPIClient;
before(async () => {
await initializeWebServer();
const axiosConfig = {
baseURL: `http://localhost:3000`,
validateStatus: () => true
};
axiosAPIClient = axios.create(axiosConfig);
});
after(async () => {
await stopWebServer();
});
describe('POST /api/user', () => {
it('when add a new user, then should get back approval with 200 response', async () => {
const userData = {
/* user props */
};
const response = await axiosAPIClient.post('/api/user', userData);
expect(response).to.containSubset({
status: 200,
data: { message: 'User signed up.' }
});
When axios (I tried fetch too) submit any HTTP request, mocha returns the following error: Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.. I tried to increase the timeout interval, but it didn't work.
If I execute my test suite without hooks, initializing my server with nodemon pointing to my index file with the same await initializeWebServer();, HTTP requests work as it should.
I really don't understand why this is happening, I think it's something with mocha.
I did some debug tests and figured out that I forgot to set up a db connection when I run my test suite, including in before and after hooks. It was a simple mistake.
user.spec.js
let axiosAPIClient;
before(async () => {
await initializeWebServer();
db.setUp();
/* axios config etc. */
after(async () => {
await stopWebServer();
await db.closeConnection();
});
I need to call the external function inside the Cypress process.
I need to do this because the element id is dynamic, so I get it in my external API.
Follow the exeple.
The async function is "helper.ElementToClick".
const helper = require("../../../helper")
describe('Test', function() {
it('First', async function() {
cy.visit('https://www.*****.com/');
const idElement = await helper.ElementToClick();
cy.get('#' + idElement).click();
})
})
But this code do not work.
Does it help if you get the element to click in before()?
const helper = require("../../../helper")
describe('Test', function() {
let idElement;
before(async () => {
idElement = await helper.ElementToClick();
});
it('First', function() {
cy.visit('https://www.*****.com/');
cy.get('#' + idElement).click();
});
});
You could wrap it in a custom command. We are using async commands inside of custom commands similar to this:
const helper = require("../../../helper");
describe('Test', function() {
it('First', async function() {
cy.visit('https://www.*****.com/');
cy.getElementId().then(idElement => {
cy.get('#' + idElement).click();
});
});
});
Cypress.Commands.add("getElementId", async () => {
return await helper.ElementToClick();
});
I am trying to mock a module (which has an exported function[and this is also mocked]).
I would like to spy on the exported function to check that it was called with something.
This is my code...
import { addNewPaymentMethod } from '../src/service'
jest.mock('../src/service', () => ({
addNewPaymentMethod : (paymentMethodInfoModel) => {
let responseFromApi = {responseStatus:{name:'blah'}};
return Promise.resolve(responseFromApi);
}
}))
import { createNewPaymentMethod } from '../src/actions/paymentMethod'
test('test expect', () => {
createNewPaymentMethod({owNickName:'nName',id:22})();
//this is the bit I don't know how to do
//...
jest.spyOn(addNewPaymentMethod);
expect(addNewPaymentMethod).toBeCalledWith({some:object});
});
You can define the mock using jest.fn().mockResolvedValue(value)
jest.mock('../src/service', () => ({
addNewPaymentMethod : jest.fn().mockResolvedValue({
responseStatus: {
name:'blah'
}
})
}))
and then since it's a jest.fn() you don't need to spyOn it :)
but note that since it's async (uses promises) you either have to chain then to the method (and use return):
it('should have been called with data', () => {
const data = {};
return service.methodWhichCallsDependencysaddNewPaymentMethod(data).then(() => {
return expect(addNewPaymentMethod).toHaveBeenCalledWith(data);
});
});
or use async/await:
it('should have been called with data', async () => {
const data = {};
await service.methodWhichCallsDependencysaddNewPaymentMethod(data);
expect(addNewPaymentMethod).toHaveBeenCalledWith(data);
});
I've set up a working example
I have the following TypeScript and nodeJS file which registers a series of socket.io namespaces. I then have some mocha tests which test those namespaces, however, my first test always fails, unless I am live reloading, in which case the second time around the tests will pass. NB: the db.Line.findAll() and similar functions are sequelize functions returning promises.
export class RealTimeStations {
public complete: Promise<boolean>;
server: any;
constructor(io: any) {
this.server = io;
this.complete = Promise.resolve(db.Line.findAll()).then(lines => {
// do some mapping to generate routes and cells
this.registerEndPoints(routes, [], cells);
}).catch(err => console.log(err))
}
registerEndPoints(routes: Array<string>, nsps: Array<any>, cells: Array<string>) {
for (let i = 0; i < routes.length; i++) {
nsps.push(this.server.of('/api/testNamespace/' + routes[i]));
let that = this;
nsp.on('connection', function (socket) {
that.emitInitialPackage(nsps[i], routes[i], cells[i]);
});
}
}
emitInitialPackage(nsp: any, name: string, cell: any) {
return db.Line.find({/* Some sequelize params*/}).then(results => {
nsp.emit('value', results);
}).catch(err => console.log(err));
}
}
I have seen this problem before, where the socketio setup wasn't being completed before the test executed, hence I moved to the promise based approach, however, this test now fails first time around but then on 2nd and subsequent livereloads, it passes...
describe('Stations Socket /api/getLine/stations', function () {
beforeEach(function (done) {
models.sequelize.sync({ force: true }).then(function () { //setup SQLite db
sequelize_fixtures.loadFile('C:/Users/George/Source/Repos/Coty%20Monitoring%20System/server/seeders/default.json', db) //seed the db
.then(function () {
app.set('port', '3000');
server = http.createServer(app);
server.listen('3000', function () { });
var io = require('socket.io').listen(server);
new RealTimeStations(io).complete.then(() => { //config socketio namespaces
socket = require('socket.io-client')('http://localhost:3000/api/testNamespace/2');
done();
});
});
})
})
describe('Setup', () => {
it('Should connect and receive correct state', function (done) {
socket.on('connect', function () {
socket.on('value', function (test) {
test.stations.should.equal('"expected value"');
done();
});
});
});
});
afterEach(function () {
socket.disconnect();
server.close();
socket = null;
})
})
Update:
The issue is that the nsp.emit('value', results); line doesn't get executed before the complete promise resolves. This means that when the test begins and connects to the server, the value event isn't triggered, and as the connect event is only sent once, the test just timeouts.
So I need a way of linking the complete promise to the emitInitialPackage promise.