Testing NodeJS with Mocha: 'Require is not defined' - node.js

EDIT:
As per the comment on the answer below: removing "type": "module" from package.json, which as I understand it is what makes Node understand 'import' and 'export' statements, and reverting everything to 'require' and 'module.exports' solved the issue.
Is there a way to keep 'import' and 'export' and still make Mocha work?
I have a very simple Node file that I'm trying to test with Mocha/Chai. The actual code is trivial, this is just to learn a bit about Mocha and how to use it. But when I run the Mocha test, I get the error ERROR: ReferenceError: require is not defined
`
I did some googling for people experiencing the same problem but the examples that I came up with were when they were running the test in the browser (see, for example, Mocha, "require is not defined" when test in browser).
The file I want to test, index.js
const argv = require('minimist')(process.argv.slice(2));
const digitTester = /\d/g;
const capTester = /[A-Z]/g;
const dict = {
length:10,
must_have_numbers: true,
must_have_caps: true
}
export default function passwordCheck(password) {
if (!password) return false;
if (typeof password !== "string") return false;
if (password.length < dict.length) return false; // assumes that 10 is a MINIMUM length
if (dict.must_have_numbers && !digitTester.test(password)) return false;
return !(dict.must_have_caps && !capTester.test(password));
}
if (argv._.length) {
console.log(passwordCheck(argv._[0]))
}
/**
* alternate version to check a lot of passwords:
*
* if (argv._.length) {
* for (const pwd of argv_) console.log(passwordCheck(pwd)
*}
*
*/
the mocha file, test/index.test.js
const chai = require('chai')
const expect = chai.expect
const passwordCheck = require('../index.js')
const tooShort = "A2a"
const noCaps = "a2abcdefghijklmnop"
const noNumber = "Aabcdefghijklmnop"
const good = "A2abcdefghijklmnop"
describe('password checking', () => {
it('should return false for passwords less than length 10', () => {
expect(passwordCheck(tooShort)).to.be.false;
});
it('should return false for passwords without a capital letter', () => {
expect(passwordCheck(noCaps)).to.be.false;
});
it('should return false for passwords without a number', () => {
expect(passwordCheck(noNumber)).to.be.false;
});
it('should return true for passwords that match criteria', () => {
expect(passwordCheck(good)).to.be.true;
});
});
and package.json
{
"name": "codetest",
"version": "1.0.0",
"main": "index.js",
"type": "module",
"scripts": {
"test": "mocha"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": "",
"dependencies": {
"#types/minimist": "^1.2.1",
"#types/node": "^14.14.20",
"chai": "^4.2.0",
"minimist": "^1.2.5",
"mocha": "^8.2.1"
}
}
and the error message is
✖ ERROR: ReferenceError: require is not defined
at file:///Users/r/Documents/Projects/sandbox/pwd_checker/index.js:2:14
at ModuleJob.run (node:internal/modules/esm/module_job:152:23)
at async Loader.import (node:internal/modules/esm/loader:166:24)
at async exports.handleRequires (/Users/r/Documents/Projects/sandbox/pwd_checker/node_modules/mocha/lib/cli/run-helpers.js:94:28)
at async /Users/r/Documents/Projects/sandbox/pwd_checker/node_modules/mocha/lib/cli/run.js:341:25
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
Node 15.

Remove this line - "type": "module" from package.json and check whether it’s working or not.

Prepend your tests with the following:
import { createRequire } from 'module';
const require = createRequire(import.meta.url);
This is because you cannot require from an ESM module; for more info please see this comment on a nodejs issue.
Documentation: https://nodejs.org/api/esm.html#differences-between-es-modules-and-commonjs

Related

What is the solution to the error I encountered while using the soap module in Node.js

I am using the soap module in Node.js as follows
import soap from "soap";
const ADRES = 'https://tckimlik.nvi.gov.tr/service/kpspublic.asmx?WSDL';
let degerler = {
TCKimlikNo: 11111111111,
Ad: 'YUSUF SEFA',
Soyad: 'SEZER',
DogumYili: 1111
};
soap.createClient(ADRES, (err, client) =\> {
client.TCKimlikNoDogrula(degerler, (err, result) =\> {
if (result.TCKimlikNoDogrulaResult) {
console.log('information is correct');
} else {
console.log('Information is incorrect');
}
});
});
However, I am getting the following error as output.
PS C:\\laragon\\www\\node-soap\> npm start
> soap#1.0.0 start
> node app.js
C:\\laragon\\www\\node-soap\\node_modules\\soap\\lib\\http.js:40
this.\_request = options.request || req\["default"\].create();
^
TypeError: Cannot read properties of undefined (reading 'create')
at new HttpClient (C:\\laragon\\www\\node-soap\\node_modules\\soap\\lib\\http.js:40:59)
at Object.open_wsdl (C:\\laragon\\www\\node-soap\\node_modules\\soap\\lib\\wsdl\\index.js:1270:48)
at openWsdl (C:\\laragon\\www\\node-soap\\node_modules\\soap\\lib\\soap.js:70:16)
at C:\\laragon\\www\\node-soap\\node_modules\\soap\\lib\\soap.js:48:13
at \_requestWSDL (C:\\laragon\\www\\node-soap\\node_modules\\soap\\lib\\soap.js:76:9)
at Object.createClient (C:\\laragon\\www\\node-soap\\node_modules\\soap\\lib\\soap.js:94:5)
at file:///C:/laragon/www/node-soap/app.js:12:6
at ModuleJob.run (node:internal/modules/esm/module_job:193:25)
at async Promise.all (index 0)
at async ESMLoader.import (node:internal/modules/esm/loader:526:24)
PS C:\\laragon\\www\\node-soap\>
package.json content
{
"name": "soap",
"version": "1.0.0",
"description": "",
"main": "app.js",
"type": "module",
"scripts": {
"test": "echo "Error: no test specified" && exit 1",
"start": "node app.js"
},
"author": "",
"license": "ISC",
"dependencies": {
"express": "^4.18.2",
"nodemon": "^2.0.20",
"soap": "^0.45.0"
}
}
What is the reason for this error? Despite all the searches on the internet, I could not find the solution for this. Is there a problem with the version? Where is the error, I can't try this soap module of the believer because of this error.
If I am not mistaken import soap from soap is python syntax. This seems to work without any problem:
var soap = require('soap');
const ADRES = 'https://tckimlik.nvi.gov.tr/service/kpspublic.asmx?WSDL';
let degerler = {
TCKimlikNo: 11111111111,
Ad: 'YUSUF SEFA',
Soyad: 'SEZER',
DogumYili: 1111
};
soap.createClient(ADRES, (err, client) => {
client.TCKimlikNoDogrula(degerler, (err, result) => {
if (result.TCKimlikNoDogrulaResult) {console.log('True'); }
else {console.log('False'); }
});
});

Can't mock a ES6 imported function in NodeJS

I've been trying for some time to mock the fetchLiveMatches imported function with no success. I've been browsing for some ideas but I think I ran out of it, so I could use some help. Any ideas of what I am doing wrong?
live.test.js
import * as liveController from "./live";
import { jest } from "#jest/globals";
import * as liveService from "../service/live";
import { buildReq, buildRes, buildNext } from "../utils/testingHelper";
jest.mock("../service/live");
beforeEach(() => {
jest.clearAllMocks();
});
describe("Live Controller", () => {
test("calls fetchLiveMatches function to fetch from external API", async () => {
const req = buildReq();
const res = buildRes();
const next = buildNext();
await liveController.getLiveMatches(req, res, next);
expect(next).not.toHaveBeenCalled();
expect(liveService.fetchLiveMatches).toHaveBeenCalled();
expect(res.status).toHaveBeenCalledWith(500);
expect(res.status).toHaveBeenCalledTimes(1);
});
});
service/live.js
import axios from "axios";
async function fetchLiveMatches() {
// Some hidden code
return axios({
method: "get",
url: `${API_FOOTBALL_BASE_URL}${GET_EVENTS}${MATCH_LIVE}${WIDGET_KEY}${TIMEZONE}${DETAILS}`,
headers: {}
}).then(res => res.data);
}
export { fetchLiveMatches };
jest.config.js
export default {
testEnvironment: "jest-environment-node",
transform: {}
};
package.json
{
"name": "server",
"version": "1.0.0",
"main": "index.js",
"type": "module",
"license": "MIT",
"dependencies": {
"express": "^4.18.2"
},
"devDependencies": {
"axios": "^1.1.3",
"eslint": "^8.26.0",
"jest": "^29.2.2",
"prettier": "^2.7.1"
},
"scripts": {
"start": "node --watch index.js",
"start:no-watch": "node index.js",
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js --watch"
}
}
Test Error Output
Live Controller › calls fetchLiveMatches function to fetch from external API
expect(received).toHaveBeenCalled()
Matcher error: received value must be a mock or spy function
Received has type: function
Received has value: [Function fetchLiveMatches]
Just posting the solution I found for anyone who eventually is facing the same problem:
First, since I'm using ES6/module imports without Babel I changed the mock function to unstable_mockModule, and then based on the docs I decided to try dynamic imports in test scope after mocking the modules.
If you're using ES module imports then you'll normally be inclined to put your import statements at the top of the test file. But often you need to instruct Jest to use a mock before modules use it. For this reason, Jest will automatically hoist jest.mock calls to the top of the module (before any imports). To learn more about this and see it in action, see this repo.
The test component works with the following code:
import { jest } from "#jest/globals";
import { buildReq, buildRes, buildNext } from "../utils/testingHelper";
describe("Live Controller", () => {
test("calls fetchLiveMatches function to fetch from external API", async () => {
jest.unstable_mockModule("../service/live", () => ({
fetchLiveMatches: jest.fn(() => [])
}));
const { getLiveMatches } = await import("./live");
const { fetchLiveMatches } = await import("../service/live");
const req = buildReq();
const res = buildRes();
const next = buildNext(msg => console.log(msg));
await getLiveMatches(req, res, next);
expect(fetchLiveMatches).toHaveBeenCalled();
expect(res.status).toHaveBeenCalledWith(200);
expect(res.status).toHaveBeenCalledTimes(1);
});
});

Create React App doesn't properly mock modules from __mocks__ directory

I have a working example with Jest and mocks from __mocks__ directory that works :
With simple Jest setup
// package.json
{
"name": "a",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"test": "jest"
},
...
"devDependencies": {
"jest": "^26.6.3"
},
"dependencies": {
"#octokit/rest": "^18.0.12"
}
}
And then /index.js :
const { Octokit } = require("#octokit/rest");
const octokit = new Octokit();
module.exports.foo = function() {
return octokit.repos.listForOrg({ org: "octokit", type: "public" })
}
with its test (/index.test.js):
const { foo } = require("./index.js");
test("foo should be true", async () => {
expect(await foo()).toEqual([1,2]);
});
and the mock (/__mocks__/#octokit/rest/index.js):
module.exports.Octokit = jest.fn().mockImplementation( () => ({
repos: {
listForOrg: jest.fn().mockResolvedValue([1,2])
}
}) );
This works quite well and tests pass.
With Create React App
However doing the same with Create React App seems to be giving me a weird result:
// package.json
{
"name": "b",
"version": "0.1.0",
"dependencies": {
"#octokit/rest": "^18.0.12",
"#testing-library/jest-dom": "^5.11.4",
"#testing-library/react": "^11.1.0",
"#testing-library/user-event": "^12.1.10",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-scripts": "4.0.1",
"web-vitals": "^0.2.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
...
}
And then /src/foo.js:
import { Octokit } from "#octokit/rest";
const octokit = new Octokit();
module.exports.foo = function() {
return octokit.repos.listForOrg({ org: "octokit", type: "public" })
}
with its test (/src/foo.test.js):
const { foo} = require("./foo.js");
test("foo should be true", async () => {
expect(await foo()).toEqual([1,2]);
});
and the very same mock (under /src/__mocks__/#octokit/rest/index.js):
export const Octokit = jest.fn().mockImplementation( () => ({
repos: {
listForOrg: jest.fn().mockResolvedValue([1,2])
}
}) );
This makes the test fail:
FAIL src/foo.test.js
✕ foo should be true (2 ms)
● foo should be true
expect(received).toEqual(expected) // deep equality
Expected: [1, 2]
Received: undefined
2 |
3 | test("foo should be true", async () => {
> 4 | expect(await foo()).toEqual([1,2]);
| ^
5 | });
6 |
7 |
at Object.<anonymous> (src/foo.test.js:4:25)
After reading a lot it seems that I can't make __mocks__ work inside Create React App. What's the problem?
The problem is that CRA's default Jest setup automatically resets the mocks, which removes the mockResolvedValue you set.
One way to solve this, which also gives you more control to have different values in different tests (e.g. to test error handling) and assert on what it was called with, is to expose the mock function from the module too:
export const mockListForOrg = jest.fn();
export const Octokit = jest.fn().mockImplementation(() => ({
repos: {
listForOrg: mockListForOrg,
},
}));
Then you configure the value you want in the test, after Jest would have reset it:
import { mockListForOrg } from "#octokit/rest";
import { foo } from "./foo";
test("foo should be true", async () => {
mockListForOrg.mockResolvedValueOnce([1, 2]);
expect(await foo()).toEqual([1, 2]);
});
Another option is to add the following into your package.json to override that configuration, per this issue:
{
...
"jest": {
"resetMocks": false
}
}
This could lead to issues with mock state (calls received) being retained between tests, though, so you'll need to make sure they're getting cleared and/or reset somewhere.
Note that you generally shouldn't mock what you don't own, though - if the interface to #octokit/rest changes your tests will continue to pass but your code won't work. To avoid this issue, I would recommend either or both of:
Moving the assertions to the transport layer, using e.g. MSW to check that the right request gets made; or
Writing a simple facade that wraps #octokit/rest, decoupling your code from the interface you don't own, and mocking that;
along with higher-level (end-to-end) tests to make sure everything works correctly with the real GitHub API.
In fact, deleting the mocks and writing such a test using MSW:
import { rest } from "msw";
import { setupServer } from "msw/node";
import { foo } from "./foo";
const server = setupServer(rest.get("https://api.github.com/orgs/octokit/repos", (req, res, ctx) => {
return res(ctx.status(200), ctx.json([1, 2]));
}));
beforeAll(() => server.listen());
afterAll(() => server.close());
test("foo should be true", async () => {
expect(await foo()).toEqual([1, 2]);
});
exposes that the current assumption about what octokit.repos.listForOrg would return is inaccurate, because this test fails:
● foo should be true
expect(received).toEqual(expected) // deep equality
Expected: [1, 2]
Received: {"data": [1, 2], "headers": {"content-type": "application/json", "x-powered-by": "msw"}, "status": 200, "url": "https://api.github.com/orgs/octokit/repos?type=public"}
13 |
14 | test("foo should be true", async () => {
> 15 | expect(await foo()).toEqual([1, 2]);
| ^
16 | });
17 |
at Object.<anonymous> (src/foo.test.js:15:25)
Your implementation should actually look something more like:
export async function foo() {
const { data } = await octokit.repos.listForOrg({ org: "octokit", type: "public" });
return data;
}
or:
export function foo() {
return octokit.repos.listForOrg({ org: "octokit", type: "public" }).then(({ data }) => data);
}

How to resolve this "at Object.<anonymous> " error in jest.js

I have lots of global objects and say a function which is to be tested but when I run npm test i get
at Object. (index.js:2:1)
at Object. (index.test.js:1:1)
this error
this error points at the object "." of gblob.aobj={};
for simplicity sake i have created a index.js and index.test.js file ,im new to jest.js
note: im working on SPA: single page application where this variable is created in another js
index.js
gblob.aobj={};
function sum(a, b) {
return a + b;
}
module.exports = sum;
"""
"""
index.test.js
const sum = require('./index');
test('adds 1 + 2 to equal 3', () => {
expect(a.hy()).toBe(3);
});
"""
"""
package.json if i need to add something here
{
"name": "my-app",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "jest"
},
" author": "",
"license": "ISC",
"devDependencies": {
"jest": "^24.7.1"
}
}
"""
If I understand your question correctly you are saying your test is failing because the global gblob doesn't exist when index.js runs.
You can fix that by creating a global gblob using the Node's global.
If you create global.gblob at the beginning of your test then it will be available when your code runs:
index.test.js
global.gblob = {}; // <= create the global "gblob"
const sum = require('./index'); // <= now require index.js
test('adds 1 + 2 to equal 3', () => {
// ...
});

Mocha and Selenium WebDriver error thrown while assertion

I developing a web program with TDD methodology. Therefore, tests are using Selenium WebDriver library. But I wonder my program doesn't work correctly. Following is source codes.
public/index.html:
<h1 class="hello">Hello, world!</h1>
test/test.js:
'use strict'
var assert = require('assert')
var webdriver = require('selenium-webdriver'),
By = webdriver.By,
until = webdriver.until
var driver;
before(() => {
driver = new webdriver.Builder()
.forBrowser('chrome')
.build()
})
describe('Index page', () => {
before(() => {
driver.get('http://localhost:8080/')
})
it('should show hello greetings', () => {
let hello = driver.findElement(By.css('h1.hello'))
assert.equal(hello.getText(), 'Hello, world!')
})
})
package.json:
{
"name": "foobar",
"version": "1.0.0",
"description": "",
"main": "index.js",
"devDependencies": {
"mocha": "^2.5.3"
"selenium-webdriver": "^2.53.2"
},
"scripts": {
"start": "http-server public",
"test": "mocha"
},
"author": "",
"license": "ISC"
}
And I've run following command.
C:\Projects\foobar>npm install
So, when I run all tests with npm test command, it always failed regardless to npm start command was run or not.
C:\Projects\foobar>npm test
> foobar#1.0.0 test C:\Projects\foobar
> mocha
Index page
1) should show hello greetings
0 passing (62ms)
1 failing
1) Index page should show hello greetings:
AssertionError: ManagedPromise {
flow_:
ControlFlow {
propagateUnhandledRejections_: true,
activeQueue_:
TaskQueue {
== 'Hello, world!'
at Context.<anonymous> (C:\Projects\foobar\test\test.js:22:16)
at callFn (C:\Projects\foobar\node_modules\mocha\lib\runnable.js:326:21)
at Test.Runnable.run (C:\Projects\foobar\node_modules\mocha\lib\runnable.js:319:7)
at Runner.runTest (C:\Projects\foobar\node_modules\mocha\lib\runner.js:422:10)
at C:\Projects\foobar\node_modules\mocha\lib\runner.js:528:12
at next (C:\Projects\foobar\node_modules\mocha\lib\runner.js:342:14)
at C:\Projects\foobar\node_modules\mocha\lib\runner.js:352:7
at next (C:\Projects\foobar\node_modules\mocha\lib\runner.js:284:14)
at Immediate._onImmediate(C:\Projects\foobar\node_modules\mocha\lib\runner.js:320:5)
npm ERR! Test failed. See above for more details.
I think the main issue is in hello.getText() snippet. The getText() method couldn't get text of hello element.
Most driver operations are asynchronous and return a promise, not the actual return value of that operation. In your case you should change findElement but also getText to work in that manner. Also, you should set a timeout since the default one is set to 2000 ms and most pages won't have finished loading by then. Try this:
'use strict'
var assert = require('assert');
var webdriver = require('selenium-webdriver');
var By = webdriver.By;
var until = webdriver.until;
var driver;
before(function () {
driver = new webdriver.Builder()
.forBrowser('chrome')
.build();
});
describe('Index page', function () {
this.timeout(5000);
before(function () {
driver.get('http://localhost:8080/');
});
it('should show hello greetings', function (done) {
driver.findElement(By.css('h1.hello'))
.then(elem => elem.getText() )
.then(text => assert.equal(text, 'Hello, world!') )
.then(done);
});
});
Note that arrow function don't define their own this object. You have to use regular callback functions to access this.timeout. Their use is, in any case, discouraged in mocha, see following:
Passing arrow functions to Mocha is discouraged. Their lexical binding of the this value makes them unable to access the Mocha context, and statements like this.timeout(1000); will not work inside an arrow function.

Resources