Jest spyOn not works - javascript

So I have a file that consists of a couple of functions. And I wrote a test for getOpeningHours.
The getOpeningHours use isLocationOrderAheadAvailable function
The isLocationOrderAheadAvailable function is not using getOpeningHours so it's not a circular dep issue
Both getOpeningHours and isLocationOrderAheadAvailable are exported functions
Both functions came from same file.
import * as locationUtils from './location';
import {
isLocationOrderAheadAvailable,
getProductItemsFromMenu,
resolveOpeningHours,
getOpenedTimeRanges,
getOpeningHours,
} from './location';
describe('getOpeningHours', () => {
it('should return same locationHours if isLocationOrderAheadAvailable is false', () => {
jest.spyOn(locationUtils, 'isLocationOrderAheadAvailable').mockImplementation(
jest.fn().mockReturnValue(false)
);
const openingHours = getOpeningHours(validLocationEntry);
expect(openingHours).toEqual(locationHours);
});
});
So eventually the isLocationOrderAheadAvailable not been mocked at all. The wired thing is that I did exactly the same in a different test and it worked.
Sorry, I can't give a working example of this.

Changing from export function isLocationOrderAheadAvailable(...) to export const isLocationOrderAheadAvailable = (...): => makes jest.spyOn work.
But I still don't understand why. I think it might be connected with JS hoisting but I don't know how jest interacts with it.

Related

How to mock a nested function with Jest

I've tried a couple of approaches but I'm not sure if it's possible to mock a nested function in Jest. I read the official docs where they use axios, but I'm trying to mock a function that is imported, not a module.
for example, I have some function A, and within A I call B. I want to mock B so that it returns true once and then false once.
B is a function I imported that I wrote in another file, and I'm testing A.
so far I have tried to import function B into my test file and create a mock of it, but I don't think it's working how I thought it would
for example I have tried this
import { b } from "../util"
jest.mock("b", () => jest.fn().mockReturnValue(true));
and then my tested file would be
function a() => {
while b() {
do something;
}
}
should I rewrite my test or function I'm testing?
Not sure what kind of results you are getting, but for mocking b you could do:
jest.mock("../util", () => ({
b:jest.fn().mockReturnValueOnce(true).mockReturnValueOnce(false),
}));

How to mock JSON file in Jest

I am using Jest to test my API and when I run my tests, my JSON file results.json gets written to due to the following line in my API app.js (which I don't want happening):
fs.writeFile('results.json', JSON.stringify(json), (err, result) => {
if (err) console.log('error', err);
});
This is what my Jest file looks like:
const request = require('supertest');
const app = require('./app');
// Nico Tejera at https://stackoverflow.com/questions/1714786/query-string-encoding-of-a-javascript-object
function serialise(obj){
return Object.keys(obj).map(k => `${encodeURIComponent(k)}=${encodeURIComponent(obj[k])}`).join('&');
}
describe('Test /addtask', () => {
test('POST /addtask Successfully redirects if newDate and newTask filled in correctly', () => {
const params = {
newTask: 'Example',
newDate: '2020-03-11'
};
return request(app)
.post('/addtask')
.send(serialise(params))
.expect(301);
});
});
I tried creating a mock of the JSON file and placed it outside the describe statement to prevent the actual results.json file being written to:
jest.mock('./results.json', () => ({ name: 'preset1', JSONtask: [], JSONcomplete: [] }, { name: 'preset2', JSONtask: [], JSONcomplete: [] }));
But this doesn't change anything. Does anyone have any suggestions?
I have seen other solutions to similar problems but they don't provide the answer I'm looking for.
EDIT: Although not a very good method, one solution to my problem is to wrap the fs.writeFile within the statement
if (process.env.NODE_ENV !== 'test') {
//code
};
although this would mean that fs.writeFile cannot be tested upon.
NOTE: I am still accepting answers!
Your issue is that the code you want to test has a hard-coded I/O operation in it, which always makes things harder to test.
What you'll want to do is to isolate the dependency on fs.writeFile, for example into something like a ResultsWriter. That dependency can then be injected and mocked for your test purposes.
I wrote an extensive example on a very similar case with NestJS yesterday under how to unit test a class extending an abstract class reading environment variables, which you can hopefully adapt to your needs.
jest.mock(path, factory) is for mocking JS modules, not file content.
You should instead mock fs.writeFile and check that it has been called with the expected arguments. The docs explain how to do it.

Error: Not implemented: window.scrollTo. How do we remove this error from Jest test?

The error:
console.error node_modules/jsdom/lib/jsdom/virtual-console.js:29
Error: Not implemented: window.scrollTo
at module.exports (/Users/me/Projects/my-project/node_modules/jsdom/lib/jsdom/browser/not-implemented.js:9:17)
at /Users/me/Projects/my-project/node_modules/jsdom/lib/jsdom/browser/Window.js:594:7
Because we are using window.scrollTo(0,0).
Also getting Not implemented Navigation for:
window.location.replace(externa_link) same error happens with .assign.
I tried googling for solutions with react-router, but all examples use some component which contains window.location.
Is there a way to avoid this error? Or hide it?
I did some research and found that the Facebook team isn't going to address it. Is there a way to suppress these errors/warnings when running jest test?
Our code doesn't break and all tests pass otherwise.
Try running the jest command with --env=jsdom. This will mock most browser functions and will solve your issues.
There are more ways of setting the test environment, take a look at:
https://jestjs.io/docs/en/configuration#testenvironment-string
Update
This worked for the window.scrollTo errors
https://qiita.com/akameco/items/0edfdae02507204b24c8
At the top of the test file after the imports mock it like this :
window.scrollTo = jest.fn();
Then inside the describe add this:
afterAll(() => {
jest.clearAllMocks();
});
so if you are also reseting all mocks after each you will end up with this ::
afterEach(() => {
jest.resetAllMocks();
});
afterAll(() => {
jest.clearAllMocks();
});
In *.test.js file that calls a component that has window.scrollTo
it("renders without crashing", () => {
window.scrollTo = jest.fn()
})
If you are just trying to get window.scrollTo(0,0) to work properly in your test environment, you can implement it in any of your setupFilesAfterEnv files:
window.scrollTo = (x, y) => {
document.documentElement.scrollTop = y;
}
When using create-react-app it would be best to add the functionality to the setup test file:
<project_name>\src\setupTests.js
I chose to implement zgreen's answer:
window.scrollTo = (x, y) => {
document.documentElement.scrollTop = y;
}
This way, any component that uses that functionality will resolve correctly.
The --env=jsdom that #Chris's answer recommended didn't work for me with the latest cra (using react-scripts#5.0.1), but the cra testing documentation mentioned another method that worked for me:
Within setupTests.ts:
const windowMock = {
scrollTo: jest.fn(),
};
Object.assign(global, global, windowMock);
Basically, we are defining the window functions we want to ignore in a mock object, and then we reassign the global object to contain all the properties of the global object and then all the properties of our window mock object (in this case, the "properties" are functions).
After adding this and rerunning my tests, the ...Not implemented... error went away. The benefit of this approach is that you can add other window-functions-to-ignore into the windowMock object as well and they will stay grouped together to reduce code-clutter.
Side note: Based on what the cra documentation says here about overriding --env, it seems that cra already uses --env=jsdom by default.
It's working for me.
beforeEach(() => {
global.window = window
window.scroll = jest.fn()
window.HTMLElement.prototype.scrollIntoView = jest.fn()
})

Mocha / Sinon Unit test JS Class and Instance issue

I'm trying to unit test some express middleware which has dependencies on some classes I've made.
Middleware.js
const MyClass = require('../../lib/MyClass');
const myClassInstance = new MyClass();
function someMiddleware(req, res) {
myClassInstance.get().then(function(resp) {
res.render('somefile.html');
});
};
Test.js
const MyClass = require('../../lib/MyClass');
const sinon = require('sinon');
const chai = require('chai');
const expect = chai.expect;
// File we are testing
const someMiddleware = require('path/to/middleware');
MyClassMock = sinon.spy(function() {
return sinon.createStubInstance(MyClassMock);
});
describe('My Middleware', function() {
let renderSpy, classStub;
let res = {
render: function() {}
}
beforeEach(function() {
renderSpy = sinon.stub(res, 'render');
})
afterEach(function() {
renderSpy.restore();
})
it('should call render function', function() {
someMiddleware.someMiddleware(req, res);
expect(renderSpy.calledOnce);
});
});
I have tried a bunch of things however I can't quite seem to actually mock this class and mock the instance created! when it runs it will try to run the actual class with it's related dependencies inside.
Cheers folks!
Your Intent: replace/stub the dependencies of your middleware.
Your problem: this isn't possible from the outside of your module. You somehow need to "intercept" which code will be used.
There are two options, both of which I have written at length about in other places (1, 2, 3), so I'll do the shortform here:
Manual dependency injection
In your middleware module, provide a __setMyClassInstance(stubbedInstance) method that is only called from tests.
Pro: very easy, no frameworks needed
Con: test-only code that opens up your module
Link seams
This is about replacing require() calls in your code with another module loader that returns objects of your liking. In your test code:
const myMiddleware = proxyquire('../../src/middleware', { '../../lib/MyClass': myStubbedInstance })
Pro: Achieves the same as DI, but requires no code changes in the module
Con: not as clear cut what is going on, requires learning a new dependency
If you are stuck after looking at these very brief explanations, I suggest looking at my provided links for long-form explanations and links to tutorials on Link Seam libraries such as proxyquire and rewire.

How to sinon spy module export utility functions

In javascript (ES6), I have a utility module which just contains some functions, and then in the end of the file, I export them like so:
module.exports = {
someFunction1,
someFunction2,
someFunction3,
}
Then I want to write unit tests for those functions. Some of the functions depend on each other; they call each other in a way, that for example, someFunction1 might call someFunction2. There's no circular problems.
Everything works well, until I need to spy that one of the functions is called. How can I do it? Currently I'm using Chai and Sinon.
In the test file, I have imported the whole file as a module:
const wholeModule = require('path/to/the/js/file')
And finally, my test looks like following:
it('should call the someFunction2', (done) => {
const spy = sinon.spy(wholeModule, 'someFunction2')
wholeModule.someFunction1() // someFunction2 is called inside someFunction1
assert(spy.calledOnce, 'someFunction2 should be called once')
done()
})
The problem is, that the test fails, because in someFunction1, the someFunction2 function is used directly. I apply the spy to the module object's function. But that's a different object. Here's an example of the someFunction1:
function someFunction1() {
someFunction2()
return 2;
}
I know the reason why it won't work, but I don't know what would be the best practise in this case to make it work? Please help!
You can use rewire module. Here is an example:
Source code:
function someFunction1() {
console.log('someFunction1 called')
someFunction2();
}
function someFunction2() {
console.log('someFunction2 called')
}
module.exports = {
someFunction1: someFunction1,
someFunction2: someFunction2
}
Test case:
'use strict';
var expect = require('chai').expect;
var rewire = require('rewire');
var sinon = require('sinon');
var funcs = rewire('../lib/someFunctions');
it('should call the someFunction2', () => {
var someFunction2Stub = sinon.stub();
funcs.__set__({
someFunction2: someFunction2Stub,
});
someFunction2Stub.returns(null);
funcs.someFunction1();
expect(someFunction2Stub.calledOnce).to.equal(true);
});
As you are already aware this happens because you are stubbing the exported reference not the actual method in the module. It works in any module that includes the one you are replacing since they include the exported references but when inside the same module it's just calling the local function.
The easiest solution I've found is just to call the reference:
function someFunction1() {
this.someFunction2()
return 2;
}

Categories

Resources