How can you correctly require a module in mocha.opts? - node.js

I'm using mocha-mongoose to automatically clear mongo in between tests. In the documentation it says to require the module in your spec file or globally in your spec helper.
Doing it per spec works great, but I want to do it from mocha.opts to keep my code DRY.
Requiring it with mocha.opts doesn't work. Mongo is not cleared in between specs
mocha.opts:
--require ./test/common.js
--reporter spec
--ui bdd
--recursive
--colors
--timeout 60000
--slow 300
common.js:
require('mocha-mongoose')('mongodb://your-mongodb-url-here');
Requiring it in each spec file works
test.js
var should = require('chai').should()
, require('mocha-mongoose')('mongodb://your-mongodb-url-here');
describe("Example test", function() {
it(' Mongo will be automatically clear all collections',);
});
How can I correctly require mocha-mongoose in mocha.opts so I don't have to repeat it in every test?

The way it works is by checking for beforeEach and registering itself as a beforeEach hook. Here is the relevant source:
if (!options.noClear && !beforeEachRegistered) {
if ('function' == typeof beforeEach && beforeEach.length > 0) {
// we're in a test suite that hopefully supports async operations
beforeEach(clearDB);
beforeEachRegistered = true;
}
}
The problem is that beforeEach is not available to modules loaded by --require. So you cannot do it with --require.
The only thing I can imagine the author of mocha-mongoose to mean with "your spec helper" would be a module that contains a bunch of utility functions that each spec requires: instead of adding require('mocha-mongoose')(...); to each spec, you'd add it just once to the module that is required by each spec.

Related

Trying to use mocha, ES6 modules, and ts-node with the --experimental-loader option

I'm trying to get the ts-node option --experimental-loader working along with mocha, and having no luck. Until I started trying to compile ES6 modules, I used to be able to run mocha tests this way:
"test": "nyc --reporter=html mocha --require ts-node/register src/**/*.spec.ts"
That doesn't work anymore when generating ES6 modules.
I'd use the TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\" }' solution for testing, but that won't work for me because of another complication: I'm generating ES6 modules as a first step in my build, but also generating ES5/CommonJS modules using webpack and babel. That last step doesn't work unless I add .js to the end of my local TypeScript import statements.
But adding those .js extensions turns out to break the TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\" }' solution, which will work, however, if I go back and delete all of the .js extensions. I obviously don't want a test and build process where I have to keep going back and forth between adding and removing those extensions.
To simplify for now, I've taken out nyc, and I'm trying to run tests like this:
mocha -r ts-node/register --experimental-loader ./ts-loader.mjs src/**/*.spec.ts
I get no errors this way, but nothing happens either. It's like the src/**/*.spec.ts doesn't exist.
My do-nothing (for now) dummy loader looks like this:
console.log('ts-loader loaded');
export async function resolve(specifier, context, defaultResolve) {
console.log('resolve');
return defaultResolve(specifier, context, defaultResolve);
}
export async function getFormat(url, context, defaultGetFormat) {
console.log('getFormat');
return defaultGetFormat(url, context, defaultGetFormat);
}
export async function getSource(url, context, defaultGetSource) {
console.log('getSource');
return defaultGetSource(url, context, defaultGetSource);
}
export async function transformSource(source, context, defaultTransformSource) {
console.log('transformSource');
return defaultTransformSource(source, context, defaultTransformSource);
}
export function getGlobalPreloadCode() {
console.log('getGlobalPreloadCode');
return '';
}
I can tell it gets loaded because the 'ts-loader loaded' message appears, but none of the functions ever get called.
I've tried other permutations, but just get errors like src/**/*.spec.ts being treated as a literal file name instead of a glob, or errors about modules not being found.
I was hoping to see my loader invoked for every import being handled, and then figuring out how to manipulate the file extensions, but I haven't managed to get that far yet. Any suggestions?
I'm using node v14.15.1. The full code for my project, with a working build, but broken tests, can be found here: https://github.com/kshetline/tubular_math
I finally found a solution, although it wasn't along the lines I was originally looking for. I gave up on trying to make mocha happy with the extra .js extensions, and found a way to make webpack happy without them. So...
import { Angle, Mode, Unit } from './angle.js';
...went back to...
import { Angle, Mode, Unit } from './angle';
My test script looks like this:
"scripts": {
"build": "rimraf dist/ && tsc && webpack && webpack --env target=umd",
"prepublishOnly": "npm run build",
"lint": "eslint 'src/**/*.ts'",
"test": "TS_NODE_COMPILER_OPTIONS='{\"module\":\"commonjs\"}' nyc --reporter=html mocha --require ts-node/register src/**/*.spec.ts"
},
And finally, most importantly, I figured out how to make webpack 5.x (4.x didn't have this issue) happy with local JavaScript imports that don't have a .js extension, which webpack now otherwise insists upon if your package.json says "type": "module":
module: {
rules: [
{ test: /\.js$/, use: 'babel-loader', resolve: { fullySpecified: false } }
]
}
...where setting fullySpecified to false is the key to the solution.
UPDATE: The above example was done working on a deliberately simple project, something easy for a starter to generate an npm package with ESM modules. Now that I'm trying something a little more advanced, I've run into a snag again running unit tests. As soon as a *.spec.ts file directly or indirectly imports external code, module loading fails. I can only test code with no external dependencies until I figure out how to fix that problem. Apparently using "TS_NODE_COMPILER_OPTIONS='{\"module\":\"commonjs\"}' is only letting me go one level deep into fixing the basic problem with running mocha along with ts-node.

Mocha --require wait until migration

I have a challenge to run a database migration before start my mocha suite test.
I am using the flag --require to load a bootstrap.js module that I have created and where I run the database migration.
The problem is that migration is an async function and as you know it returns immediately. So, there is any way to wait for the module to be ready until everything it's finished?
I can use some sync lib to convert migration to be synchronous but I would like to hear more strategies.
You could define a global before hook, like so:
import {runMigration} from './bootstrap';
before(done => {
runMigration().then(done);
});
describe('some part of my suite', () => {
/* ... */
If you write this hook in "root suite" - that is, outside any of your own describe blocks - it will run before all tests, regardless of which file you put this block in.
Docs on root level hooks
Note, that your hooks take too long, you might start getting an error like "Timeout of 2000ms exceeded", which can be solved by running mocha with --timeout flag - like mocha --timeout 10000

Why can't get the global variable which was set in globalSetup in test code?

I use Jest to do unit test in node.
And I use the new feature globalSetup which come in Jest v22.
I have defined a global variable in globalSetup.
But I can't get it in the test code. Console log is undefined.
Anyone in this question?
Thanks.
Jest version: 22.0.0
node version: 8.9.0
yarn version: 1.3.2
OS: mac High Sierra 10.13.2
The code as follow:
// package.json
{
"jest": {
"globalSetup": "<rootDir>/src/globalSetupTest.js"
}
}
// globalSetupTest.js
module.exports = async function() {
global.foo = 'foo';
console.log(`global setup: ${global.foo}`);
};
// App.test.js
describe('APP test', () => {
it('renders without crashing', () => {
console.log({ foo: global.foo });
});
});
// test result
yarn run v1.3.2
$ node scripts/test.js --env=node --colors
global setup: foo
PASS src/App.test.js
APP test
✓ renders without crashing (5ms)
console.log src/App.test.js:3
{ foo: undefined }
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 0.354s, estimated 1s
Ran all test suites.
There's a solution offered from Jest people themselves: https://jestjs.io/docs/en/puppeteer.html. Note that if you're using CRA, this won't work out of the box (solution below), cause it currently doesn't support testEnvironment option for Jest.
Anyways:
In Jest config file you setup paths to global setup, teardown and test environment scripts
In global setup script, you create a browser and write it's WSEndpoint to a file
In global teardown script, you simply close the browser
In testEnvironment script, you read WSEndpoint from the file you saved before and then use it to connect to the running browser - after this, browser is available in your tests by using a global variable
If you're using CRA, you can use a custom setup for these tests and run them completely separately. And if you're using Puppeteer for e2e tests, this is probably what you want to do anyway.
You just add another script to your package.json: "test:e2e": "jest -c ./jest-e2e.config.js" and set it up as you want. Now you will have npm run test for unit tests and npm run test:e2e for end to end tests.
For what I understood is a design decision of Jest because is considered a bad practice to share state across different tests. Tests run in parallel and they should keep their own state.
See:
https://github.com/facebook/jest/issues/6007#issuecomment-381743011
https://github.com/facebook/jest/issues/4118
Can you try..
global.foo = 'foo';
console.log(`global setup: ${global.foo}`);
(remove the exports)
You can try changing globalSetup to setupFiles. That one, won't expect a function.
https://facebook.github.io/jest/docs/en/configuration.html#setupfiles-array

npm + Mocha + RequireJS

I'm having quite the time trying to set up node/npm with Mocha and RequireJS. Here's what I've done.
I've created a testing/ directory, with this structure:
testing/
|
+-- package.json
|
+-- README.md
|
+-- test/
|
+-- mocha.opts
|
+-- widgets/
|
+--mywidget/
|
+-- test.js
Here is what each relevant file contains:
package.json:
{
"name":"testing-project",
"version":"2.5.0",
"description":"Testing Project",
"keywords":["test"],
"engines": { "node": ">= 0.7.0 < 0.11.0" },
"scripts" : {
"test": "./node_modules/.bin/mocha"
},
"devDependencies": {
"mocha": ">= 1.18.2",
"requirejs": ">= 2.1.11",
"should": ">= 3.2.0",
"expect.js": ">= 0.3.1"
}
}
test/mocha.opts:
--require expect.js
--require should
--require requirejs
--require mocha
--reporter spec
--ui tdd
--recursive
--growl
test/widgets/mywidget/test.js
'use strict';
var requirejs = require('requirejs');
// Also tried with: require('../../../r.js') which I downloaded from RequireJS' site
requirejs.config({
baseUrl: '../../../../',
nodeRequire: require
});
console.log('before require');
requirejs(['mywidget/trunk/src/mywidgetfile.js'], function(myObj){
console.log('after require');
var expect = require('expect.js');
// Instead of the above "requirejs['mywidget..." line, I've also tried:
// var myObj = requirejs('mywidget/trunk/src/mywidgetfile.js'); AND:
// var myObj = requirejs('../../../../mywidget/trunk/src/mywidgetfile.js');
describe('My Widget', function(){
describe('my-widget#getInfo', function(){
it('should pass this test', function(done){
expect( myObj.returnString('test') ).to.equal( 'test' );
done();
})
})
});
});
console.log('done');
It will output the console lines "before require" and "done", but as long as I have the requirejs(['mywidget... line in, it will not hit the after require. If I remove the requirejs line (and the corresponding closing brace/paren line), and instead use the direct "var myObj =" line, I get "cannot find module", and if I use the second "var myObj" line, I get "Reference Error: define is not defined".
I'm trying to package this all, for convenience for other developers, with npm, such that I'm running the command "npm test" from within the top "testing/" directory.
I've been scouring for answers and trying so many things, but I can't seem to require a file using RequireJS and have "define()" defined. I can execute tests, that's not a problem... it's just trying to insert RequireJS into the mix that is when I start having the issues.
Any help would be tremendous!
Thank you!
There are multiple problems going on in what you are showing us. You are incorrectly using both RequireJS and Mocha.
RequireJS
I am pretty sure your baseUrl is incorrect. You seem to think that Mocha's current working directory will be set to test/widgets/mywidget/ when it executes the tests in test/widgets/mywidget/test.js. That's not the case. The working directory is wherever you happen to be when you run npm test. According to your description you are in testing/ when you run it. It is not clear to me what value your baseUrl should be because you do not provide enough information in your question but I trust that from the explanation I just gave you can figure it out.
Now you may think "surely, my baseUrl is correct because when I execute requirejs(['mywidget/trunk/src/mywidgetfile.js'], function(myObj){ I don't get an error". This would be an incorrect inference. While this requirejs invocation schedules the loading of your module, RequireJS does not get the opportunity to try loading it because Mocha exits before RequireJS tries to load it. You can check this by replacing your module path with complete garbage and you won't get an error.
Once you fix this baseUrl issue, using var myObj = requirejs('mywidget/trunk/src/mywidgetfile.js') will work as you expect. So you'll be able to avoid using the asynchronous form of require (this is the form that uses an array of dependencies as the first argument). (The requirejs function you use is just an alias for the function normally called require in RequireJS' documentation.)
Mocha
Your tests are not running because Mocha does not see them. The way Mocha works is by reading all of the test files it finds and then executing them. The callbacks to each describe calls are executed right away, and the callbacks to each it calls are recorded as tests to be run. Once Mocha is done figuring out what tests exist, it runs them.
What happens with your test file is that Mocha executes it, as usual. However, there is no call to describe or it in the top scope of your test file. There are calls in your callback to requirejs but remember what I said above: RequireJS does not get the opportunity to load the module so it does not get the opportunity to run the callback. (And even if it did run it, it would be too late.) So Mocha does not see any tests and exits right away.
The Way Forward
Figure out the baseUrl you need, and then this should work:
'use strict';
var requirejs = require('requirejs');
requirejs.config({
baseUrl: <whatever value is needed here>,
nodeRequire: require
});
var myObj = requirejs('mywidget/trunk/src/mywidgetfile.js');
describe('My Widget', function() {
// etc...
You might also consider dropping RequireJS entirely from your test suite. I've written a library that works just as well in Node as in the browser. It is composed of AMD modules and is loaded by RequireJS in the browser, but I don't use RequireJS in the test suite. This is the loader I use to load my modules in Node. There's also amdefine which I've not used but should give similar capabilities.

How to run jasmine-node tests with RequireJS

How can I properly run jasmine tests using jasmine-node and RequireJS?
I already tried something like this, but doesnt work (CoffeeScript):
requirejs = require 'requirejs'
requirejs.config { baseUrl: __dirname + '/../' }
requirejs ['MyClasses', 'FooClass'], (MyClasses, FooClass) ->
describe "someProp", ->
it "should be true", ->
expect(MyClasses.FooClass.someProp).toEqual true
Finished in 0 seconds 0 tests, 0 assertions, 0 failures
My goal is to write modular classes using RequireJS, CoffeeScript and classes must be testable with jasmine-node (CI server).
How can I do that please?
Thank you!
EDIT:
I executing tests with command (at directory with tests):
jasmine-node ./
Jonathan Tran is right, it's the spec in the file name for me.
I have this:
"scripts": {
"install": "cake install",
"test": "node_modules/jasmine-node/bin/jasmine-node --verbose --coffee --runWithRequireJs --captureExceptions spec"
},
in my package.json and I installed jasmine-node from inside the project npm install jasmine-node
Minimal test file called RingBuffer.spec.coffee
require ["disrasher"], (mod) ->
describe "A test", ->
it "should fail", ->
expect(1).toEqual 0
It doesn't actually work at the moment because I haven't got the project hooked up with require properly I don't think. I'll post back here when it does.
If anyone is running into this, much has changed since this question was asked. The first thing to check is still that you're naming your files like thing.spec.coffee.
But if you're running the tests and still seeing the output "0 tests", you need to make a JavaScript file with your requirejs config. This must be JavaScript, not CoffeeScript.
// requirejs-setup.js
requirejs = require('requirejs');
requirejs.config({ baseUrl: __dirname + '/../' });
Then tell jasmine to use this setup file:
jasmine-node --coffee --requireJsSetup requirejs-setup.js ./
One nice thing about this is that you don't need to include the requirejs config in every spec file.
I've tested this on node v12.16, jasmine-node v3.0.0, and requirejs v2.3.6.
It seems that jasmine-node and require.js are completely incompatible. That said, it is possible to run jasmine tests on require.js modules in node using a bit of extra code. Take a look at https://github.com/geddski/amd-testing to see how.

Resources