jest.setMock not working for installed dependencies - javascript

I am having trouble using jest.setMock with installed dependencies
So, I have a feature to test which requires my-module dependency:
// my-module/index.js
import { hello } from 'my-dep';
export const doSomething = () => {
return hello();
};
// my-dep
// location: my-module/node_modules/my-dep/index.js
export const hello = () => {
return 'dude';
};
I have "npm linked" the my-module in my app:
cd my-module
npm link
cd app
npm link my-module
I wrote a test file as follows:
// app/feature.test.js
jest.setMock('my-dep', {
hello: () => 'world'
});
const { doSomething } = require('my-module');
it('should return dude', () => {
expect(doSomething()).toBe('dude');
});
I don't understand why mocking my-dep did not work and called the actual hello() function.
When I tried using the my-module file as relative import & installed my-dep dependency, the mocking worked:
// app/feature.test.js
jest.setMock('my-dep', {
hello: () => 'world'
});
const { doSomething } = require('./my-module');
it('should return world', () => {
expect(doSomething()).toBe('world');
});
What am I missing here?
Environment:
Binaries:
Node: 8.9.4
Yarn: 1.3.2
npm: 5.6.0
npmPackages:
jest: ^22.4.3 => 22.4.3
Some related issues:
https://github.com/facebook/jest/issues/701
https://github.com/facebook/jest/issues/796

Your problem is likely caused by the module loading order, but I'm not sure how changing from my-module to ./my-module changes it. I'd love to see the console output after adding console.log(require('path').join(__dirname, __filename)) to the top of my-module and my-dep.
This comment in the Jest issue you shared indicates that jest.setMock isn't hoisted before your import statements. If you switch it to use jest.mock that is hoisted, does it work?
jest.mock('my-dep', () => ({
hello: () => 'world'
}));
The Jest docs on manual mocks mention hoisting in this note:
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).
For more about hoisting in JavaScript, refer to You Don't Know JS: Scope & Closures - Chapter 4: Hoisting

Related

Test suite failed to run Jest (very simple test)

I have inherited a codebase that was created using #vue/cli originally there was no unit-test in the project and I am now trying to add it, so far I have created a very simple test and also added jest to the application.
My simple test looks like this,
import { createLocalVue, mount, shallowMount } from '#vue/test-utils';
import ModalRefactored from '../../src/components/modal2/ModalRefactored.vue';
const localVue = createLocalVue();
describe('ModalRefactored', () => {
let storeMocks, wrapper;
beforeEach(() => {
// Create a fresh store and wrapper instance for every test case.
wrapper = shallowMount(ModalRefactored, {
localVue
});
});
test('It should render a modal when open', () => {
});
test('It should not render an overlay when not open', () => {
});
test('It should close whn the user clicks the close button', () => {
});
test('It should close when the user presses the esc key', () => {
});
});
My jest config looks like this,
module.exports = {
preset: '#vue/cli-plugin-unit-jest'
}
When I run yarn test:unit I get the following,
ests/unit/ModalRefactored.spec.js
● Test suite failed to run
Cannot find module 'core-js/modules/es.error.cause.js' from 'Api.js'
However, Jest was able to find:
'./Api.d.ts'
'./Api.js'
'./Api.js.map'
You might want to include a file extension in your import, or update your 'moduleFileExtensions', which is currently ['js', 'jsx', 'json', 'vue'].
See https://jestjs.io/docs/en/configuration#modulefileextensions-array-string
However, Jest was able to find:
'../core/IPPComponent.js'
You might want to include a file extension in your import, or update your 'moduleFileExtensions', which is currently ['js', 'jsx', 'json', 'vue'].
See https://jestjs.io/docs/en/configuration#modulefileextensions-array-string
10 | this.method = "";
11 | this.code = 0;
> 12 | this.statusCode = 0;
| ^
13 | this.httpCode = 0;
14 | this.error = {};
15 | this.httpText = "";
at Resolver.resolveModule (../../node_modules/jest-resolve/build/index.js:259:17)
at Object.<anonymous> (../js/dist/Api.js:12:1)
I have no idea what the problem is? I would assume that using shallowMount would run the test standalone?
The file core-js/modules/es.error.cause.js is a recent addition to the core-js module. I ran into the exact same problem with a Vue.js project. The core-js version I experienced the error with was 3.16.0.
Updating the core-js version to the current version (3.21.1) fixed the problem.
The weird thing I experienced is, I grepped the package.json files of all modules in my node_modules directory, and couldn't find any core-js dependency with a newer version than 3.16.0. Also, I only got this error when running my test suite on Windows, not when running on Linux.

How to use should.js as global variable:

I am trying to write some unit test that using mocha and should.js since I would like to keep format for each of my unit test the same and each unit test require should.js to verify proerpty of object. How can I make it as globally variable so I do not need to require should.js on each test file so far I have tried
global.should = require('should');
describe('try gloabl'), () => {
it('should work'), () => {
let user = { name: 'test'};
global.should(user).have.property('name', 'test');
});
});
#error, global.should is not a function
and if i use this. it works
const should = require('should');
should = require('should');
describe('try gloabl'), () => {
it('should work'), () => {
let user = { name: 'test'};
global.should(user).have.property('name', 'test');
});
});
First of all, I'm tired of writing "require" is the worst reason to use the GLOBAL variable. There is a reason that using require is the conventional way of doing things, and it is not different from any other language where you have to import or type using in each and every file. It just makes it easier to understand what the code is doing later on.
See this for further explanation.
Now, that said, when requiring should, the module actually attaches itself to the GLOBAL variable, and makes describe, it and should accessible methods.
index.js
require('should');
describe('try global', () => {
it('should work with global', () => {
let user = { name: 'test' };
global.should(user).have.property('name', 'test');
});
it('should work without global', () => {
let user = { name: 'test' };
should(user).have.property('name', 'test');
});
});
//////
mocha ./index.js
try global
√ should work with global
√ should work without global
2 passing (11ms)
I fixed the typos in your code (eg. Removing the extra ) from describe and it function), and this code works just fine when running with mocha ./index.js. Make sure you have install mocha with npm i -g mocha to make the module available in the CLI.

Mocking node modules with Jest and #std/esm

I'm currently having an issue writing tests for a node application that uses #std/esm. I've setup a manual mock of a node module inside a __mocks__ directory and the following code shows a test for the file uses this mocked node module. (It's used in db.mjs)
const loader = require('#std/esm')(module, { cjs: true, esm: 'js' })
const Db = loader('../src/db').default
const db = new Db()
describe('getNotes', () => {
it('gets mocked note', () => {
db.getNote()
})
})
However, when I run Jest, my manual mock is not being used, it is using the real node module.
Does anyone have any thoughts on why this could be happening?
Jest is particular about the location of your mocks. From their documentation on mocking node modules:
For example, to mock a scoped module called #scope/project-name,
create a file at mocks/#scope/project-name.js, creating the
#scope/ directory accordingly.
In your case it would be __mocks__/#std/esm.js

Running tests .mjs / ESM on Node using Jasmine or any other alternative

My Node-based project is implemented using native ES module support on Node thanks to the --experimental-modules CLI switch (i.e. node --experimental-modules).
Obviously, when I run a spec using Jasmine node --experimental-modules ./node_modules/jasmine/bin/jasmine I get the following error:
Error [ERR_REQUIRE_ESM]: Must use import to load ES Module
Is it ever possible to use Jasmine using ES modules in Node?
If not, is there any alternative to don't use a framework (e.g. running tests with npm scripts)?
It was easier than I thought.
It's just about calling a file which you might call run.mjs as follows:
node --experimental-modules ./run.mjs
The whole file would look like this:
jasmine.mjs:
import Jasmine from "jasmine"
import JasmineConsoleReporter from "jasmine-console-reporter"
const jasmine = new Jasmine()
jasmine.loadConfigFile( "./support/jasmine.json" )
jasmine.env.clearReporters()
jasmine.addReporter( new JasmineConsoleReporter( {
colors: true,
cleanStack: true,
verbosity: 4,
listStyle: 'indent',
activity: false
} ) )
export default jasmine
And you would add specs as follows in separate files:
import jasmine from './my-project/spec/jasmine.mjs'
jasmine.env.describe('Foo', () => {
jasmine.env.it('Bar', () => {
// Expects, assertions...
})
})
Finally, you would run jasmine importing both configured jasmine instance and specs:
import jasmine from './my-project/spec/jasmine.mjs'
import someSpec1 from './my-project/spec/someSpec1.mjs'
import someSpecN from './my-project/spec/someSpecN.mjs'
someSpec1()
someSpecN()
jasmine.execute()
Simplifying the solution of #Matias_Fidemraizer, keeping only the important bits in one file:
import glob from 'glob';
import Jasmine from 'jasmine';
const jasmine = new Jasmine();
jasmine.loadConfigFile('tests/jasmine.json');
// Load your mjs specs
glob('**/*-test.mjs', function (er, files) {
Promise.all(
files
// Use relative paths
.map(f => f.replace('tests/specs/', './'))
.map(f => import(f)
.catch(e => {
console.error('** Error loading ' + f + ': ');
console.error(e);
process.exit(1);
}))
)
.then(() => jasmine.execute());
});
And execute it with
node --experimental-modules jasmine-run.mjs
You will have some problems in the logs, receiving a message like
[...] .split('\n') [...]
That message mean that you have an exception in the mjs code.
You can follow there: https://github.com/jasmine/jasmine-npm/issues/150

babel generated code breaking istanbul coverage

I'm using babel to enable ES6 imports in a node project. Also using mocha for testing, and istanbul for coverage. I end up with less than full coverage because babel generates code something like the following:
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _promise = require('babel-runtime/core-js/promise');
var _promise2 = _interopRequireDefault(_promise);
var _koa = require('koa');
var _koa2 = _interopRequireDefault(_koa);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
Specifically, the generated function _interopRequireDefault is copied into every code file, and the branches are not necessarily always executed, which skews the branch coverage number emitted for istanbul. Is there any way around this issue?
If you're using gulp, I have a gist with a gulpfile here that sets up the necessary hooks and filters. The relevant chunk is to load isparta, hook require, and let the tests run:
gulp.task('test:cover', (cb) => {
gulp.src('src/main/**/*.js')
.pipe(istanbul({
instrumenter: require('isparta').Instrumenter,
includeUntested: true
}))
.pipe(babel())
.pipe(gulp.dest('target/cover'))
.pipe(istanbul.hookRequire())
.on('finish', cb);
});
gulp.task('test:mocha', (cb) => {
gulp.src('target/test/**/Test*')
.pipe(mocha())
.pipe(istanbul.writeReports())
.on('end', cb);
});
gulp.task('test', (cb) => {
return runSequence('test:cover', 'test:mocha', cb);
});
The only frustrating part is that your tests must use the covered code:
import {
LinearInterpolator,
CosineInterpolator
} from '../../cover/random/Interpolators';
I haven't found a way to work around that yet without also covering the test scripts and skewing coverage, although you should be able to do that by merging streams:
gulp.task('test:cover', (cb) => {
const src = gulp.src('src/main/**/*.js')
.pipe(istanbul({
instrumenter: require('isparta').Instrumenter,
includeUntested: true
}));
const test = gulp.src('src/test/**/*.js');
merge(src, test)
.pipe(babel())
.pipe(gulp.dest('target/cover'))
.pipe(istanbul.hookRequire())
.on('finish', cb);
});
You need to combine it with isparta - https://github.com/douglasduteil/isparta - to get the coverage working correctly. I warn you its a bit trial and error at the moment! My npm script looks like -
"coverage": "node_modules/.bin/babel-node node_modules/.bin/isparta cover --include-all-sources --report html node_modules/.bin/_mocha -- --reporter $npm_package_config_bdd_reporter",
We've run into this and I finally got fed up and looked into what causes this line. It turns out that every time to use an import like:
import chai from 'chai';
this babel fill gets added to allow sane interaction with older export styles. The trouble is that none of the common libraries exhibit the "true" branch of the ternary. I build the following file coverInterrop.js that artificially trips the first branch using old-school exports:
module.exports = {
__esModule: true
};
and I include in any file where I want to use an undestructured import:
// eslint-disable-next-line no-unused-vars
import coverInterrop from 'coverInterrop';
Note that it has to assign to a variable to trip the coverage and good eslint rules won't like that

Categories