I have electron-util.js file I want to cover with tests:
const isElectron = "electron" in process.versions;
const isUsingAsar =
isElectron &&
process.mainModule &&
process.mainModule.filename.includes("app.asar");
export const fixPathForAsarUnpack = path =>
isUsingAsar ? path.replace("app.asar", "app.asar.unpacked") : path;
In order to test fixPathForAsarUnpack method I need to mock versions and mainModule from process. I tried to do it like this:
import { fixPathForAsarUnpack } from "../src/electron-util";
test("fixes path for Electron", () => {
process.versions = {
electron: "0.0.0",
mainModule: {
filename: "/app.asar/index.html"
}
};
const path =
"/Users/relix/My.app/Contents/Resources/app.asar/node_modules/foo/binary";
expect(fixPathForAsarUnpack(path)).toBe(
"/Users/relix/My.app/Contents/Resources/app.asar.unpack/node_modules/foo/binary"
);
});
But it throws an error:
● fixes path for Electron
TypeError: Cannot assign to read only property 'versions' of object '[object process]'
4 |
5 | test("fixes path for Electron", () => {
> 6 | process.versions = {
| ^
7 | electron: "0.0.0",
8 | mainModule: {
9 | filename:
at Object.<anonymous>.test (__test__/electron-util.test.js:6:3)
What am I doing wrong, how should I mock process object?
You should mock the process in this way:
const originalProcess = process
global.process = {...originalProcess, version: "your code here"}
// do the test
// ...
// restore the original process object for next tests
global.process = originalProcess
Related
My app uses the window object to inject environmental variables at runtime for docker, however my tests are coming back with the error below:
url config:
declare const window: Window &
typeof globalThis & {
_env_: any
}
export const url = window._env_.REACT_APP_API_URL;
export const baseUrl = url;
error in tests as the handlers use the baseUrl, but even if I change the handlers to hardcoded values the below error comes:
● Test suite failed to run
TypeError: Cannot read properties of undefined (reading 'REACT_APP_API_URL')
4 | }
5 |
> 6 | export const url = window._env_.REACT_APP_API_URL;
| ^
7 | export const baseUrl = url;
setupTests.tsx
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '#testing-library/jest-dom';
import { setLogger } from 'react-query'
import { server } from './mocks/server'
declare const window: Window &
typeof globalThis & {
_env_: any
}
window._env_.REACT_APP_API_URL = "https://wwww.xxxxx.com"
beforeAll(() => server.listen())
// Reset any request handlers that we may add during the tests,
// so they don't affect other tests.
afterEach(() => server.resetHandlers())
// Clean up after the tests are finished.
afterAll(() => server.close())
// silence react-query errors
setLogger({
log: console.log,
warn: console.warn,
error: () => {},
})
Any idea's? So where ever there is an api call I get this error in the tests for jest/React.
you can mock the global window var
test/setup.js
window.env. REACT_APP_API_URL = 'something';
then you need to add in package.json file
"jest": {
"setupFiles": [
"./test/setup.js"
],
OR
you can define global var in CLI like this
npm run jest -- --globals='{"window": "someUrl", "value": "someValue"}'
I am unit testing a particular method, and am having issues mocking another function that is called during the process. In my case, the method to test is defined in a class, and the function I'd like to mock is defined in a separate module. How do I mock this function? See below for my code.
In the past, I've used the Sinon package to mock/stub a dependency (example). But that doesn't work in this case. This is the first time I'm testing a method defined in a class, so perhaps that's why mocking the dependency isn't working.
My Code
Module Containing Test Function (myLib/myDir/combo.js)
const { externalFunction } = require('./external-function')
class Combo {
constructor(props) {}
async myMethod () {// The function under test.
externalFunction()
}
}
const myCombo = props => new Combo(props)
module.exports = { myCombo }
My Test File (test/myLib/myDir/combo.test.js); no attempt at mocking
const { myCombo } = require('../../../myLib/myDir/combo')
const comboObj = myCombo({}) // Instantiate object to expose method to test.
await comboObj.myMethod()// Call method that I want to test. This throws type error because myMethod function calls externalFunction, which throws an error in the test environment.
My Test File (test/myLib/myDir/combo.test.js); attempt to use Sinon package to mock
const sinon = require('sinon')
const dependencyModule = require('./external-function')// Defines the method dependencyModule.methodToMock
const myStub = sinon.stub(dependencyModule, 'methodToMock').returns(555) // Stubs dependencyModule.methodToMock and ensures it always returns the value: 555.
const comboObj = myCombo({}) // Instantiate object to expose method to test.
await comboObj.myMethod()// Call method that I want to test. This throws type error because myMethod function calls externalFunction, which throws an error in the test environment.
How? You need to follow "stubbed module can not be destructured." on the official guide How to stub a dependency of a module
For example I have file external-function.js, combo.js and test.js on the same directory. I choose to use console.log to show that stub works and fake function get called, because you are not expecting something returned on myMethod.
// File: external-function.js
function externalFunction () {
console.log('Original Called');
}
module.exports = { externalFunction };
// File: combo.js
// Note: "stubbed module can not be destructured."
const externalFunction = require('./external-function')
class Combo {
constructor(props) {}
async myMethod () {
externalFunction.externalFunction()
}
}
const myCombo = props => new Combo(props)
module.exports = { myCombo };
// File: test.js
const sinon = require('sinon');
const { myCombo } = require('./combo');
const dependencyModule = require('./external-function');
describe('myCombo', () => {
it('myMethod', async () => {
sinon.stub(dependencyModule, 'externalFunction').callsFake(() => {
console.log('Fake Called');
});
const comboObj = myCombo({});
await comboObj.myMethod();
});
});
When I run it using nyc and mocha on my terminal:
$ npx nyc mocha test.js
myCombo
Fake Called
✓ myMethod
1 passing (3ms)
----------------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------------------|---------|----------|---------|---------|-------------------
All files | 85.71 | 100 | 75 | 83.33 |
combo.js | 100 | 100 | 100 | 100 |
external-function.js | 50 | 100 | 0 | 50 | 2
----------------------|---------|----------|---------|---------|-------------------
I need to unit test the following method which takes one parameter. But I am unable to test.
Component Method:
resetAdd(qa: qaModel) {
const street = qa.children.find(temp => temp.token === 'street');
const zip = qa.children.find(temp => temp.token === 'zip');
const city = qa.children.find(temp => temp.token === 'city');
this.fGroup.controls[street.pathway].callReset();
this.fGroup.controls[zip.pathway].callReset();
this.fGroup.controls[city.pathway].callReset();
}
TestFile:
it('resetAdd Method is called', () => {
const param1= jest.fn();
expect(component.resetAdd).toHaveBeenCalledWith(param1);
});
I am not sure what's wrong and also please let me know what else test case I can write.
Here is the unit test solution, you can use jest.spyOn:
By default, jest.spyOn also calls the spied method. This is different behavior from most other test libraries.
That means when you can jest.spyOn to spy on a method of object without custom implementation, the original method of this object will be executed as usual. Beside, there is a spy on the method, so you can use assert of jest to check if it is executed or not. For your case, the method is callReset.
index.ts:
type qaModel = any;
export class SomeComponent {
private fGroup = {
controls: {
street: {
callReset() {}
},
zip: {
callReset() {}
},
city: {
callReset() {}
}
}
};
resetAdd(qa: qaModel) {
const street = qa.children.find(temp => temp.token === 'street');
const zip = qa.children.find(temp => temp.token === 'zip');
const city = qa.children.find(temp => temp.token === 'city');
this.fGroup.controls[street.pathway].callReset();
this.fGroup.controls[zip.pathway].callReset();
this.fGroup.controls[city.pathway].callReset();
}
}
index.spec.ts:
import { SomeComponent } from './';
describe('SomeComponent', () => {
it('should call resetAdd', () => {
const comp = new SomeComponent();
const streetCallResetSpy = jest.spyOn(comp['fGroup']['controls']['street'], 'callReset');
const zipCallResetSpy = jest.spyOn(comp['fGroup']['controls']['zip'], 'callReset');
const cityCallResetSpy = jest.spyOn(comp['fGroup']['controls']['city'], 'callReset');
const qaModel = {
children: [
{ token: 'street', pathway: 'street' },
{ token: 'zip', pathway: 'zip' },
{ token: 'city', pathway: 'city' }
]
};
comp.resetAdd(qaModel);
expect(streetCallResetSpy).toBeCalledTimes(1);
expect(zipCallResetSpy).toBeCalledTimes(1);
expect(cityCallResetSpy).toBeCalledTimes(1);
});
});
Unit test result with 100% coverage:
PASS src/stackoverflow/58818402/index.spec.ts
SomeComponent
✓ should call resetAdd (5ms)
----------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
----------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 100 | 100 | |
index.ts | 100 | 100 | 100 | 100 | |
----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 3.612s, estimated 8s
Source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/58818402
You are not calling resetAdd method in unit test, still expecting it should have been called.
Steps are :
Spy method which you want to be called,
call that method
then check for to have been called
it('resetAdd Method is called', () => {
const param: qaModel = null // define param of type which is expected. I dont know structure of qaModel, so for example put as null
spyOn(component, 'resetAdd') // spy first
component.resetAdd(param)
expect(component.resetAdd).toHaveBeenCalledWith(param);});
this is the code I have to test:
myFunction: function(data) {
var file = new Blob(data, {type: 'text/plain'});
window.open(window.URL.createObjectURL(file));
}
In order to test it, I thought to test if the window.open function is called, applying a 'spy' on window.open , in the following way:
sandbox.spy(window, 'open');
but, even leaving previous line as the unique line in the test, what I only get is the test failure and the following message:
global leaks detected: consoleLogging, open
Thus, in order to avoid that, I tried to re-define the function in the test in this way:
global.window = {
open: function (url) {}
};
In this case an exception raised:
Attempted to assign to readonly property
Then I tried to mock the 'open' via the following:
sandbox.mock(window, 'open');
objectUnderTest.myFunction();
expect(window.open.callCount).to.equal(1);
this way, I get no global or readonly errors, but an exception on the 'expect', telling that:
expected undefined to equal 1
Does someone knows a way to successfully test window.open?
Here is the unit test solution based on Node.js environment:
index.js:
const obj = {
myFunction: function(data) {
var file = new Blob(data, { type: 'text/plain' });
window.open(window.URL.createObjectURL(file));
}
};
module.exports = obj;
index.spec.js:
const obj = require('./');
const sinon = require('sinon');
const { expect } = require('chai');
describe('53524524', () => {
before(() => {
class Blob {}
global.Blob = Blob;
global.window = {
open() {},
URL: {
createObjectURL() {}
}
};
});
it('should test myFunction correctly', () => {
const openStub = sinon.stub(window, 'open');
const createObjectURLStub = sinon.stub(global.window.URL, 'createObjectURL').returns('fake object url');
obj.myFunction('fake data');
expect(createObjectURLStub.calledOnce).to.be.true;
expect(openStub.calledWith('fake object url')).to.be.true;
});
});
Unit test result with coverage report:
53524524
✓ should test myFunction correctly
1 passing (11ms)
---------------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
---------------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 66.67 | 100 | |
index.js | 100 | 100 | 100 | 100 | |
index.spec.js | 100 | 100 | 60 | 100 | |
---------------|----------|----------|----------|----------|-------------------|
Source code: https://github.com/mrdulin/mocha-chai-sinon-codelab/tree/master/src/stackoverflow/53524524
I want to use Gulp, Rollup and Babel to transpile ES6 app to ES5 (that use the module reveal pattern with IIFE).
The gulp file:
var gulp = require('gulp');
var rollup = require('gulp-better-rollup');
var babel = require('rollup-plugin-babel');
gulp.task('roll', function () {
return gulp.src('_01_src/js/form/*.js')
.pipe(rollup(
{plugins: [babel({presets: ['es2015-rollup']})]},
{format: 'iife',}
)
)
.pipe(gulp.dest('_02_build/js/form/'));
});
The controller import model and view and is transpiled ok:
var controller = (function (model) {
'use strict';
model = 'default' in model ? model['default'] : model;
var classCallCheck = function (instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
};
var Cat = function Cat(name) {
classCallCheck(this, Cat);
this.name = name;
};
return Cat;
}(model));
The problem that I have is when I want to group together (to avoid collision) like this is not working:
( function() { var model = function () { ... }()
var view = function () { ... }()
var controller = function (model, view) {
......
}(model, view) )}()
I have multiple app that contains MVC and I want first to group and app together than group all app;
So I start:
js
app1
- model.js
- view.js
- controller.js
app2
- model.js
- view.js
- controller.js
app3
- model.js
- view.js
- controller.js
After task run I want to have, which don't collide:
js
app1.js
app2.js
app3.js
I have partial working example from the rollup-stream in github team/users, but only works for an app(and not exactly transpiled as MVC), not with multiple apps.
const gulp = require('gulp');
const scss = require('gulp-sass');
const babel = require('gulp-babel');
const watch = require('gulp-watch');
const autopre = require('gulp-autoprefixer');
const uglify = require('gulp-uglify');
const rollup = require('rollup-stream');
const source = require('vinyl-source-stream');
const buffer = require('vinyl-buffer');
gulp.task('rollup', function () {
return rollup({
entry: '_01_src/js/form/app.js',
format: 'iife',
})
// turn the raw text stream into a stream containing one streaming Vinyl file.
.pipe(source('form.js'))
// buffer that file's contents. most gulp plugins don't support streaming files.
.pipe(buffer())
// transform the files.
.pipe(babel({
presets: ['es2015']
}))
// and output to _02_build/js/form.js as normal.
.pipe(gulp.dest('_02_build/js'));
});
would this help? I think create tasks for each app, you mentioned before group together it transpile OK
const path = require('path')
const fs = require('fs-extra')
const gulp = require('gulp')
const rollup = require('gulp-better-rollup')
const babel = require('rollup-plugin-babel');
// suppose your project looks like
// --project
// | +-gulpfile.js
// | +-src
// | | +-app1
// | | | +-controller.js
// | | | +-model.js
// | | | +-view.js
// | | +-app2
// the target path where your apps locates,
var targetPath = path.join(__dirname, 'src')
// files will build into
var destTargetPath = path.join(__dirname, 'dest')
// find app1,app2.... and exclude node_modules
var dirs = fs.readdirSync(targetPath).filter((filename) => {
if (filename === 'node_modules') return false
var stat = fs.statSync(path.join(targetPath, filename))
return stat.isDirectory()
})
// I want a task name for each app
var dir2task = dir => 'x_' + dir
// add tasks for each app
dirs.forEach((dir) => {
// as it worked for single app
gulp.task(dir2task(dir), () => {
//this return means tells gulp when job is done
return gulp.src(path.join(targetPath, dir) + '/**/*.js')
.pipe(rollup({
plugins: [babel({
presets: ['es2015-rollup']
})]
}, {
format: 'iife',
}))
.pipe(gulp.dest(path.join(destTargetPath, dir)))
})
})
// run them all and after all apps built,and copy or rename your built controller to appx.js, there's no need for return, my mistake
gulp.task('default', dirs.map(dir2task), () => {
dirs.forEach((dir) => {
fs.copySync(path.join(destTargetPath, dir, 'controller.js'), path.join(destTargetPath, dir + '.js'))
})
})
// result will be
// --project
// | +-gulpfile.js
// | +-src
// | | +-app1
// | | +-....
// | +-dist
// | | +-app1.js
// | | +-app2.js
You can use static variable.
Here's how to make model, view, and controller as static variable.
function a_project () {
}
a_project.model = function(){};
a_project.view = function(){};
a_project.controller = function(){};
var myInstance = new MyClass();
This will help you to call model, view and controller variable.