How correctly require in Jest file ? (with a module pattern js file) - jestjs

Probably a stupid question
I start using JEST for testing.
I have my js app :
var app={ init:function{ //some code}, ... }
module.exports = app;
And my app.test.js :
const {app} = require('../js/index.js')
test('type of variable', () => {
expect(typeof app.someFunction(app.someVar)).toBe("'number");
});
And I have the classic error :
TypeError: Cannot read property 'someFunction' of undefined
It seem very stupid, but I never understand clearly these require on client side...
It work perfectly with the Jest getStarted example
My arbo is
-js
----index.js
-tests
----app.text.js

module.exports = app
The above line returns object {}, and you are trying to pick app from object in your destructing line var {app}
Remove {}
const app = require('../js/index.js')
test('type of variable', () => {
expect(typeof app.someFunction(app.someVar)).toBe("'number");
});

Related

how to refer to a function when unit testing with NodeJS / Mocha

I'm making my first test with Mocha.
Dummy test is passing, but when I want to refer to my actual function that is in another file, it won't find it:
ReferenceError: main is not defined
I have a single index.js with :
async function main() {
function comparer(otherArray) {
return function (current) {
return otherArray.filter(function (other) {
return other.prm === current.prm && other.conso_prod === current.conso_prod
}).length === 0;
}
}
}
module.exports = main();
and in my test.js file, I do:
const {expect} = require('chai');
describe('Sum numbers', () => {
it('Compare 2 existing array', () => {
const meters = []
const perimeter = []
const onlyInMeters = meters.filter(main.comparer(perimeter));
expect(onlyInMeters).to.equal([]);
});
});
But when I refer to main.comparer, it can't find it:
ReferenceError: main is not defined
What am I forgetting? Sorry, I'm a NodeJS Noob!
It seems like you did not import the index.js file in test.js file. You are returning noting from main function as well.
Also, why are you exporting it like module.exports = main(); Instead you can do this:
// index.js
module.exports = {
comparer: (otherArray) => { ... }
}
// test.js
cosnt main = require('PATH_OF_index.js');
main.comparer();

Node JS Error: Cannot find module './services'

I am trying to create stubs using sinon to run the test but its throwing error.
Its trying to find a module services and saying the module is not found but I have the services.js in the same folder as test. So I am not sure why its failing.
Can someone please advise what is wrong in the code.
1) the car-lookup controller "before each" hook for "should return filtered TAP response":
Error: Cannot find module './services' from 'C:\nodejsworkspace\olive\server\api\car-lookup'
at Function.module.exports [as sync] (node_modules\proxyquire\node_modules\resolve\lib\sync.js:40:15)
at Proxyquire._resolveModule (node_modules\proxyquire\lib\proxyquire.js:137:20)
at Proxyquire.<anonymous> (node_modules\proxyquire\lib\proxyquire.js:205:35)
at Array.reduce (<anonymous>)
at Proxyquire._withoutCache (node_modules\proxyquire\lib\proxyquire.js:204:6)
at Proxyquire.load (node_modules\proxyquire\lib\proxyquire.js:129:15)
at Context.<anonymous> (test\unit\server\api\car-lookup\car-lookup.controller.test.js:30:18)
controller = proxyquire('../../../../../server/api/car-lookup/car-lookup.controller.js', {
'./services': { taClient: new MockTaClient() }
});
});
Below is how I think the services are being exported from the car-lookup.controller.js
Below is car lookup controller.js
If you see in the first line it is trying to import services and services is not a direct js file. I have an index.js file in the ../../directory which is what the first line is referring to. Directory structure is also below. Please advise.
>server
> api
> car-lookup
>car-lookup.controller.js
>server
>services
>index.js
'use strict';
const services = require('../../services');
const { ApiError, ValidationError } = require('../../errors');
const AccountModel = require('../../models/account-model');
const lookupAccount = (req, res, next) => {
const retrieveAccount = (oktaToken) => {
return services.tapClient.retrieveAccount(req.body);
};
const sendResponse = (account) => {
res.send(new AccountModel(account));
};
const onError = (err) => {
next(err instanceof ValidationError ?
new ApiError(err, 400) :
err);
};
retrive()
.then(retrieveAccount)
.then(sendResponse)
.catch(onError);
};
module.exports = {
lookupAccount
};
Make sure you are exporting the file services properly

nodejs/mocha/chai as promise : variables used in expected async function initialized outside

I am brand new to mocha/chai and I spent 2 days trying to solve the following issue without any success (please note that the code below is just to present the concept, it is not the real one).
I have got a JS file called "api.js" in which some variables such as SERVER_URL are initialized at the top of the file through dotenv framework.
api.js :
const SERVER_URL = process.env.SERVER_URL;
async function startAPI () {
return new Promise ( (resolve, reject) => {
console.log(`${SERVER_URL}`);
resolve();
});
exports = {startAPI};
Now I have got "test.js" file in which :
test.js:
require('../api');
it('the test', async () => {
return await expect(api.startAPI()).to.be.fulfilled;
});
The problem is that SERVER_URL is undefined during the test and I cannot modify the api.js (as I am not the owner), just the test.js.
How can I run the test with the SERVER_URL variable set correctly (to process.env.SERVER_URL value from api.js) ?
Is there a solution without any refactoring ?
And if not what is the best solution ?
Experts, thanks in advance for your precious help
A way to improve testability is to use process.env.SERVER_URL instead of SERVER_URL where possible - or getServerUrl():
const getServerUrl = () => process.env.SERVER_URL;
This way process.env.SERVER_URL can be mocked at any point.
An alternative is to import module after process.env.SERVER_URL was mocked. This should involve decaching if there's more than one test that uses this module, because it won't be re-evaluated otherwise.
const decache = require('decache');
...
let originalServerUrl;
beforeEach(() => {
originalServerUrl = process.env.SERVER_URL;
});
beforeEach(() => {
process.env.SERVER_URL = originalServerUrl;
});
it('the test', async () => {
decache('../api');
process.env.SERVER_URL = '...';
const api = require('../api');
await expect(api.startAPI()).to.be.fulfilled;
});
If it's expected that there's no SERVER_URL in tests, it can be just discarded after it was mocked:
The easiest way would be just to set these variables when you run your test from CLI:
e.g. in npm scripts:
"scripts": {
"test": "SERVER_URL='http://example.com' mocha"
}
or directly from terminal:
$ SERVER_URL='http://example.com' npm test
But better solution would be mock environment variables in your tests with little refactoring. And need proxyquire to be installed. And actually async/await is not needed here.
const proxyquire = require('proxyquire').noPreserveCache() // noPreserveCache is important to always have refreshed script with new process.env.SERVER_URL in each test
const MOCKED_SERVER_URL = 'http://example.com'
describe('example', () => {
let initialServerUrl
let api
beforeEach(() => {
initialServerUrl= process.env
})
afterEach(() => {
process.env = initialServerUrl
})
it('fulfilled', () => {
process.env.USE_OTHER_CODE_PATH = MOCKED_SERVER_URL
api = proxyquire('../api', {})
return expect(api.startAPI()).to.be.fulfilled
})
it('rejected', () => {
process.env.USE_OTHER_CODE_PATH = ''
api = proxyquire('../api', {})
return expect(api.startAPI()).to.be.rejected
})
})
You can set .env variables with mocha using the following line:
env SERVER_URL=htt://api.yourserver.com/ mocha test
This way mocha knows what to expect from your process.env.SERVER_URL

Error trying to unit test Mongoose schema validation with Jest

I'm new to Jest and try to set up a simple test script:
"use strict"
// Local dependencies
const userModel = require('./user.model');
// Setting controllers
describe("Users endpoint", () => {
describe("Validating user schema", () => {
it("Should return an error if name property is missing", () => {
const user = new userModel();
user.validate((error) => {
expect(error.errors.name.kind).toBe("required");
});
});
});
});
When running Jest, I got the following error:
SyntaxError: Identifier 'global' has already been declared
2 |
3 | // Local dependencies
> 4 | const userModel = require('./user.model');
I search on Google and didn't find anything related to the 'global' identifier.
Any help would be really appreciate.
Thanks,
Steve
Ok so after digging a bit more, I figured out that the issue was with a const global inside my require script:
const global = require(path.join(__dirname, '..', 'config', 'config'));
If I change the "global" name for anything else (i.e. globalTest), it will works.
Sousing "global" seems to be not allowed.

Unit testing in Typescript with Dependency Injection

I'm running into problems with typescript during unit testing. I try to unit test an module like the following code example:
import {Express} from 'express'
export interface BootClassInterface {
setup(): Express
}
export class BootClass implements BootClassInterface {
constructor(private express: () => Express ){
}
public test(){
const app = this.express()
app.get("/test", (req, res) => {
res.send("Hello World")
})
return app;
}
}
For testing propose I want to know if express().get() was called and if the first parameter was '/test'. Before I switched to typescript I always used the module sinonJS to spy or stub the functionality so that I could test of a certain dependency was used correctly. Now with Typescript I run into problems with the strict types that I've set in the module. By example:
import * as chai from 'chai'
import 'mocha'
import * as sinon from 'sinon'
import * as express from 'express'
import * as Boot from '../../src/Core/Boot'
const assert = chai.assert
suite('[CORE] unit test /Core/Boot.ts', () => {
let BootClass: Boot.BootClassInterface
setup(() => {
const expressApp = () => {
const app = express()
app.get = sinon.spy()
return app
}
const Services: Boot.BootServicesInterface = { express: expressApp }
BootClass = new Boot.BootClass(Services)
})
test('Do first test', () => {
const app = BootClass.setup()
chai.assert.equal(app.get.called, 1)
})
})
The code example above results in the following Typescript compiling error:
error TS2339: Property 'called' does not exist on type 'ApplicationRequestHandler<Express>'.
I can see why typescript returns this error and somehow it is even obvious. I even know a possible solution where I accept Express as any in the module.
But I'm looking for a more elegant way to be able to spy/stub/mock my dependencies for testing propose, but at the same time have the advantages of strict typing.
AS you have stated you specify Express as the return type on BootClassInterface interface. Therefore app would be considered Express and it will look up its properties instead of your mock.
You can also just cast one property of app as any. Try:
chai.assert.equal((<any>app.get).called, 1)
Or using Sinon type definition:
chai.assert.equal((<SinonSpy>app.get).called, 1)

Resources