Why is my module not appearing in require.cache? - node.js

OS: Windows 10
Node version: 0.10.36
Mocha global version: 1.21.4
I'm attempting to use mocha to unit-test my code, but a local variable inside the code I'm trying to test is persisting between tests, causing problems.
When I look inside require.cache, between tests, I don't see my module in there. It is my understanding that I should be clearing the cache if I want to reset this module between tests.
I made a small node project to demonstrate this issue:
package.js:
{
"name": "cache-test",
"version": "0.0.1",
"dependencies": {
"lodash": "4.5.0"
},
"devDependencies": {
"chai": "1.9.2",
"mocha": "1.21.4",
"mockery": "1.4.0",
"sinon": "1.10.3",
"app-root-path":"*"
}
}
module.js:
var foo = "default value";
exports.init = function(){
foo = 'init';
}
exports.returnFoo = function(){
return foo;
}
test/test-module.js
var chai = require("chai"),
expect = chai.expect,
mockery = require("mockery"),
appRoot = require('app-root-path');
var module;
describe("module", function () {
before(function () {
mockery.enable({ useCleanCache: true });
});
beforeEach(function () {
mockery.registerAllowable(appRoot + "/module", true);
module = require(appRoot + "/module");
});
afterEach(function () {
console.log('deleting', require.cache[require.resolve(appRoot + "/module")]);
delete require.cache[require.resolve(appRoot + "/module")];
module = null;
mockery.deregisterAll();
});
after(function () {
mockery.disable();
});
describe("test",function(){
it("foo should be 'init' after running init()",function(){
module.init();
console.log('foo is ',module.returnFoo());
expect(module.returnFoo()).to.equal('init');
});
it("foo should be 'default value' if init() is not run",function(){
console.log('foo is ',module.returnFoo());
expect(module.returnFoo()).to.equal("default value");
});
});
});
running mocha prints
module
test
foo is init
√ foo should be 'init' after running init()
deleting undefined
foo is init
1 failing

Oh, I needed to add
mockery.resetCache() to my afterEach function. That solved it.
It seems like the useCleanCache option, and deleting the entry from require.cache aren't compatible with each-other, as the former keeps it from appearing in the latter.
So it's either:
Don't use useCleanCache
Delete it "manually" from require.cache
OR
Use useCleanCache
Use resetCache()
but don't attempt to mix and match.

Related

How To Mock a Dependency Class in Typescript Node Using Chai-Spies

Given something like my-module.ts that will be called by an external consumer that of course cannot do dependency injection to this module's constructor:
import Dependency from 'dependency-class';
import {myDataFormat} from '../types/my-module-types';
export default class MyModule {
private dependency: Dependency;
constructor() {
this.dependency = new Dependency();
}
getMyData(): myDataFormat {
const dependencyData = this.dependency.getDependencyData();
// parse/process data
// return modified data
}
}
How can we test that Dependency.getDependencyData() was called by MyModule.getMyData()... using only mocha, chai, and chai-spies?
my-module.test.ts:
import Dependency from 'dependency';
import MyModule from '../src/modules/my-module'
import 'mocha';
import chai = require('chai');
chai.should(); //enable chai should()
import spies = require('chai-spies');
chai.use(spies); //extend chai with spies plugin
describe('Tests.MyModule', () => {
let instance: MyModule | undefined;
before(() => {
// Create spies/stubs/mocks as needed
chai.spy.on(Dependency.prototype, ['getDependencyData'], () => {
// this is replacement function body if we want to test specific return data
});
// Create MyModule instance
instance = new MyModule();
});
after(() => {
// Reset MyModule instance
instance = undefined;
// Remove spies/stubs/mocks that were created
chai.spy.restore();
});
describe('getMyData()', () => {
it('should call Dependency.getDependencyData()', () => {
// Arrange
// - Set up spies/stubs/mocks (done above)
// - Create a MyModule instance (done above)
// Act
instance.getMyData();
// Assert
Dependency.prototype.getDependencyData.should.have.been.called();
});
});
});
Versions in Node package.json:
"#types/chai": "^4.3.0",
"#types/chai-spies": "^1.0.3",
"#types/mocha": "^9.1.0",
"chai": "^4.3.6",
"chai-spies": "^1.0.0",
"mocha": "^9.2.1",

Testing NodeJS with Mocha: 'Require is not defined'

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

running e2e testing with aurelia cli

I'm trying to implement a few e2e tests in my aurelia-cli app. I've tried looking for docs or blogs but haven't found anything on e2e setup for the cli. I've made the following adjustments to the project.
first I added this to aurelia.json
"e2eTestRunner": {
"id": "protractor",
"displayName": "Protractor",
"source": "test/e2e/src/**/*.ts",
"dist": "test/e2e/dist/",
"typingsSource": [
"typings/**/*.d.ts",
"custom_typings/**/*.d.ts"
]
},
Also added the e2e tasks on aurelia_project/tasks:
e2e.ts
import * as project from '../aurelia.json';
import * as gulp from 'gulp';
import * as del from 'del';
import * as typescript from 'gulp-typescript';
import * as tsConfig from '../../tsconfig.json';
import {CLIOptions} from 'aurelia-cli';
import { webdriver_update, protractor } from 'gulp-protractor';
function clean() {
return del(project.e2eTestRunner.dist + '*');
}
function build() {
var typescriptCompiler = typescriptCompiler || null;
if ( !typescriptCompiler ) {
delete tsConfig.compilerOptions.lib;
typescriptCompiler = typescript.createProject(Object.assign({}, tsConfig.compilerOptions, {
// Add any special overrides for the compiler here
module: 'commonjs'
}));
}
return gulp.src(project.e2eTestRunner.typingsSource.concat(project.e2eTestRunner.source))
.pipe(typescript(typescriptCompiler))
.pipe(gulp.dest(project.e2eTestRunner.dist));
}
// runs build-e2e task
// then runs end to end tasks
// using Protractor: http://angular.github.io/protractor/
function e2e() {
return gulp.src(project.e2eTestRunner.dist + '**/*.js')
.pipe(protractor({
configFile: 'protractor.conf.js',
args: ['--baseUrl', 'http://127.0.0.1:9000']
}))
.on('end', function() { process.exit(); })
.on('error', function(e) { throw e; });
}
export default gulp.series(
webdriver_update,
clean,
build,
e2e
);
and the e2e.json
{
"name": "e2e",
"description": "Runs all e2e tests and reports the results.",
"flags": []
}
I've added a protractor.conf file and aurelia.protractor to the root of my project
protractor.conf.js
exports.config = {
directConnect: true,
// Capabilities to be passed to the webdriver instance.
capabilities: {
'browserName': 'chrome'
},
//seleniumAddress: 'http://0.0.0.0:4444',
specs: ['test/e2e/dist/*.js'],
plugins: [{
path: 'aurelia.protractor.js'
}],
// Options to be passed to Jasmine-node.
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 30000
}
};
aurelia.protractor.js
/* Aurelia Protractor Plugin */
function addValueBindLocator() {
by.addLocator('valueBind', function (bindingModel, opt_parentElement) {
var using = opt_parentElement || document;
var matches = using.querySelectorAll('*[value\\.bind="' + bindingModel +'"]');
var result;
if (matches.length === 0) {
result = null;
} else if (matches.length === 1) {
result = matches[0];
} else {
result = matches;
}
return result;
});
}
function loadAndWaitForAureliaPage(pageUrl) {
browser.get(pageUrl);
return browser.executeAsyncScript(
'var cb = arguments[arguments.length - 1];' +
'document.addEventListener("aurelia-composed", function (e) {' +
' cb("Aurelia App composed")' +
'}, false);'
).then(function(result){
console.log(result);
return result;
});
}
function waitForRouterComplete() {
return browser.executeAsyncScript(
'var cb = arguments[arguments.length - 1];' +
'document.querySelector("[aurelia-app]")' +
'.aurelia.subscribeOnce("router:navigation:complete", function() {' +
' cb(true)' +
'});'
).then(function(result){
return result;
});
}
/* Plugin hooks */
exports.setup = function(config) {
// Ignore the default Angular synchronization helpers
browser.ignoreSynchronization = true;
// add the aurelia specific valueBind locator
addValueBindLocator();
// attach a new way to browser.get a page and wait for Aurelia to complete loading
browser.loadAndWaitForAureliaPage = loadAndWaitForAureliaPage;
// wait for router navigations to complete
browser.waitForRouterComplete = waitForRouterComplete;
};
exports.teardown = function(config) {};
exports.postResults = function(config) {};
and I added a sample test in my test/e2e/src folder it doesn't get executed. I've also tried implementing a e2e test within the unit test folder since when I run au test I see that a chrome browser opens up.
describe('aurelia homepage', function() {
it('should load page', function() {
browser.get('http://www.aurelia.io');
expect(browser.getTitle()).toEqual('Home | Aurelia');
});
});
But this throws the error browser is undefined. Am I missing something with e2e testing with the cli? I know aurelia-protractor comes pre-installed but I don't see any way to run it.
I know this is a very late answer, but perhaps for others looking for an answer, you could try to import from the aurelia-protractor plugin
import {browser} from 'aurelia-protractor-plugin/protractor';

require("electron").app is undefined. I npm installed fresh modules. Unsure what to do

Yesterday, I was developing on Electron perfectly fine. Then I hop onto my computer to realize Electron isn't working at all now.
I removed node_modules and did a fresh npm install
package.json:
...
"devDependencies": {
"devtron": "^1.4.0",
"electron": "^1.4.7"
},
"dependencies": {
"electron-debug": "^1.1.0"
}
...
This is the error I got.
I followed the suggestions used of previous issues of this problem. Nothing is resolving it.
Electron is not installed globally. Everything should be self contained in the directory.
npm list
Most of this code was taken from electron boilerplate
Edit:
main process:
'use strict';
const path = require('path');
const electron = require('electron');
const app = electron.app;
// adds debug features like hotkeys for triggering dev tools and reload
require('electron-debug')({
showDevTools: true
});
// prevent window being garbage collected
let mainWindow;
function onClosed() {
// dereference the window
// for multiple windows store them in an array
mainWindow = null;
}
function createMainWindow() {
const display = electron.screen.getPrimaryDisplay();
const win = new electron.BrowserWindow({
width: display.workArea.width,
height: display.workArea.height
});
const projectPath = path.dirname(path.dirname(__dirname));
win.loadURL(`file://${projectPath}/static/index.html`);
win.on('closed', onClosed);
return win;
}
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', () => {
if (!mainWindow) {
mainWindow = createMainWindow();
}
});
app.on('ready', () => {
mainWindow = createMainWindow();
});
So, in my case. Issue was resolved by using my original terminal rather than a plugin terminal for Atom.
For anyone out there. Double check with your vanilla terminal or even editor to double check.

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