Have a simple scroll to Element function utilizing getBoundingClientRect & window.scrollTo, but can't get any iteration of the Jest test to get any coverage beyond branch: 100. All other test coverage is at 0.
Function to be tested:
export default function scrollToEl(el) {
let elRect = el.getBoundingClientRect();
return window.scrollTo(
elRect.left + document.documentElement.scrollLeft,
elRect.top + document.documentElement.scrollTop
);
}
Jest test that doesn't provide 100% coverage in all categories:
import * as scroll from "../scrollToEl";
describe("scrollToEl test", () => {
let element;
let ev = jest.fn();
scroll.scrollToEl = jest.fn(() => {
ev;
});
beforeEach(() => {
element = document.createElement("div");
});
it("should be called", () => {
ev(element)
expect(ev).toHaveBeenCalled();
});
});
How do I get full 100% coverage across the board?
Help please.
EDIT - now with 100% coverage across the board:
JS function:
I had to alter the export default function to export const scrollToEl = function (el)
export const scrollToEl = function (el) {
// ...
};
Test:
I had to simplify the test to get 100% coverage across the board. I added and checked for window.scrollTo along with calling the actual function on newly created element (el).
import * as scroll from "../scrollToEl";
describe("scrollToEl test", () => {
beforeEach(() => {
window.scrollTo = jest.fn();
});
it("`window.scrollTo` should be called", () => {
const el = document.createElement("span");
scroll.scrollToEl(el);
expect(window.scrollTo).toHaveBeenCalled();
});
});
So, first I have an issue with your goal of wanting 100% code coverage. But this isn’t a question about that, so we’ll skip over that for now.
To get Jest to not complain about scrollTo, you have to mock it.
window.scrollTo = jest.fn();
Related
I've got a controller that calls two functions and in one test I would like to see if the child function is called and in my second test see if it returns the correct number.
// ./utilities/sayMyName.js
exports.sayMyName = (name) => name;
// ./utilities/doubleNum.js
exports.doubleNum = (num) => num * 2;
// ./utilities/index.js
const { doubleNum } = require("./doubleNum");
const { sayMyName } = require("./sayMyName");
module.exports = {
doubleNum,
sayMyName,
};
// myController.js
const { doubleNum, sayMyName } = require("./utilities");
exports.doubleMyNum = (num, name) => {
const myName = sayMyName(name);
return doubleNum(num);
};
// myController.test.js
const myController = require("./myController");
const utilities = require("./utilities");
jest.mock("./utilities");
describe("doubleNum", () => {
test("should call sayMyName", () => {
myController.doubleMyNum(2, "test name");
expect(utilities.sayMyName).toHaveBeenCalledWith("test name");
});
test("should double my number", () => {
const { doubleNum } = jest.requireActual("./utilities");
expect(myController.doubleMyNum(2, "test name")).toBe(4);
});
});
First test passes however it's the second one that fails because I originally mocked the utilities module, I followed the docs and using jest.requireActual should bring back the original function but it isn't. I did read that mapping the exports like I did in index.js and using deconstructing can cause issues with intercepting a function to mock it. How can I go about getting this to work?
SORTED
After much reading and testing I'd like to think I've sorted the issue by realising that I was trying to change the implementation of function from index.js that was already imported into the myController.js file that contained the function I was testing
So for example if I want to change the implementation of only one of the exported functions from my index.js file I need to mock the module and reimport myController.js for it to have an affect. So this works:
const myController = require("./myController");
const utilities = require("./utilities");
jest.mock("./utilities");
describe("doubleNum", () => {
beforeEach(() => {
jest.resetModules();
});
test("should call sayMyName", () => {
const spy = jest.spyOn(utilities, "sayMyName");
myController.doubleMyNum(2, "test name");
expect(spy).toHaveBeenCalledWith("test name");
});
test("should double my number", () => {
jest.mock("./utilities", () => {
const { sayMyName, doubleNum } = jest.requireActual("./utilities");
return {
sayMyName,
doubleNum,
};
});
// Reimport myController for the actual utilities module to be restored
const myController = require("./myController");
expect(myController.doubleMyNum(2, "test name")).toBe(4);
});
});
Of course this could be simplified had I not used deconstruction in myController.js, hopefully someone else finds this helpful.
I think this might be because you aren’t doing the mocking in a beforeEach or beforeAll block.
Alternatively, you can look at the resetModules example here, and mock or require the module within each test block instead of for all tests: jest.resetModules
I've read all of the relevant questions on this topic, and I realize this will probably be marked as a duplicate, but I simply cannot for the life of me figure out how to get this working.
I have this simple function that lazily loads elements:
export default function lazyLoad(targets, onIntersection) {
const observer = new IntersectionObserver((entries, self) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
onIntersection(entry.target);
self.unobserve(entry.target);
}
});
});
document.querySelectorAll(targets).forEach((target) => observer.observe(target));
return observer;
}
Example usage:
lazyLoad('.lazy-img', (img) => {
const pictureElement = img.parentElement;
const source = pictureElement.querySelector('.lazy-source');
source.srcset = source.getAttribute('data-srcset');
img.src = img.getAttribute('data-src');
});
Now, I'm trying to test the lazyLoad function using jest, but I obviously need to mock IntersectionObserver since it's a browser API, not a native JavaScript one.
The following works for testing the observe method:
let observe;
let unobserve;
beforeEach(() => {
observe = jest.fn();
unobserve = jest.fn();
window.IntersectionObserver = jest.fn(() => ({
observe,
unobserve,
}));
});
describe('lazyLoad utility', () => {
it('calls observe on each target', () => {
for (let i = 0; i < 3; i++) {
const target = document.createElement('img');
target.className = 'lazy-img';
document.body.appendChild(target);
}
lazyLoad(
'.lazy-img',
jest.fn(() => {})
);
expect(observe).toHaveBeenCalledTimes(3);
});
});
But I also want to test the .isIntersecting logic, where the callback fires... Except I don't know how to do that. How can I test intersections with jest?
Mocking stuff is so easy when you pass it as an argument:
export default function lazyLoad(targets, onIntersection, observerClass = IntersectionObserver) {
const observer = new observerClass(...)
...
}
// test file
let entries = [];
const observeFn = jest.fn();
const unobserveFn = jest.fn()
class MockObserver {
constructor(fn) {
fn(entries,this);
}
observe() { observeFn() }
unobserve() { unobserveFn() }
}
test('...',() => {
// set `entries` to be something so you can mock it
entries = ...something
lazyLoad('something',jest.fn(),MockObserver);
});
Another option is to use mockObserver mentioned above and mock in window.
window.IntersetionObserver = mockObserver
And you don't necessary need to pass observer in component props.
The important point to test if entries isIntesecting is to mock IntersectionObserver as class like mentioned above.
I am trying to debug my unit tests or run them one at a time but for some reason can't. What I figured out is jest seems to be behaving abnormally or is not able to process arrow functions normally. Both way of mocking are same and test pass but the uncommented approach disables the debug mode for some reason
// index.js
const dependency = require("./dependency");
const ModuleUnderTest = () => {
const data = dependency()();
return data;
};
module.exports = ModuleUnderTest;
// dependency.js
module.exports = () => () => "some data";
// index.test.js
const index = require("./index");
const { getData } = require("./dependency");
// This doesn't let you debug the test
jest.mock("./dependency.js", () => () => () => "some other data");
// This lets you debug
// jest.mock("./dependency.js", () => {
// return () => () => "some other data";
// });
describe("test suite", () => {
test("test1", () => {
expect(index()).toBe("some other data");
});
});
In case you would like to repro locally, here is the repo which reproduces the steps: https://github.com/cravi24/test-js/blob/master/index.test.js
For VS code, jest runner has a plugin which enables you to run jest test with left-clicks or one ui button like option. Both fail for debugging
I have some test code like this:
test('Test', async () => {
const someData = await setup()
const actual = myFunc(someData.x)
expect(actual.a).toEqual(someData.y)
expect(actual.b).toEqual(someData.y)
... many more like this
}
I would like to break apart the code into multiple test blocks (because I can't even add a description message to each expect statement).
If Jest supported async describe, I could do this:
describe('Some group of tests', async () => {
const someData = await setup()
test('Test1', async () => {
const actual = myFunc(someData.x)
expect(actual.a).toEqual(someData.y)
}
test('Test2', async () => {
const actual = myFunc(someData.x)
expect(actual.b).toEqual(someData.y)
}
})
I could duplicate the setup call for each test of course, but that would slow down the test considerable (I'm populating MongoDB there).
So, any way I can improve the structure of my test with Jest?
It's correct that describe callback function isn't supposed to be asynchronous. It synchronously defines tests for a suite, any asynchronous operations in its scope will be discarded.
Previously Jasmine and Jest allowed to access common test context with regular functions and this. This feature was deprecated in Jest; common variables need to be user-defined.
Shared code can be moved into helper function that internally uses beforeAll, beforeEach, etc:
const setupWithTestContext = (testContext = {}) => {
beforeAll(async () => {
const setupData = await setup();
Object.assign(testContext, setupData);
});
return testContext; // sets up a new one or returns an existing
});
const anotherSetupWithTestContext = (testContext = {}) => {
beforeEach(() => {
testContext.foo = 0;
});
return testContext;
});
...
describe('Some group of tests', async () => {
const sharedTestData = setupTestContext();
// or
// const sharedTestData = {}; setupTestContext(sharedTestData);
anotherSetupWithTestContext(sharedTestData);
test('Test1', async () => {
// context is filled with data at this point
const actual = myFunc(sharedTestData.x)
...
}
...
})
I have two modules which contain exported functions. "ModuleB" uses a function from "ModuleA". Now I want to test "ModuleB" and mock the used function from "ModuleA".
I use ES6 with babel. For testing I use karma and jasmine.
I tried using babel-rewire and inject-loader, but it just does not work. I'm kind of new to all this and I guess I'm just doing something wrong. So any help is appreciated!
moduleA.js
export function getResult() {
return realResult;
}
moduleB.js
import * as ModuleA from './moduleA'
export function functionToTest() {
let result = ModuleA.getResult();
// do stuff with result which I want to test
}
my test
it("does the right thing", () => {
// I tried using rewire, but this gives me an exception:
ModuleB.__Rewire__('ModuleA', {
getResult: () => {
return fakeResult;
}
});
let xyz = ModuleB.functionToTest(canvas, element);
});
Take a look to this library https://www.npmjs.com/package/mockery I thought It's exactly what do you want. Hope it helps!
EDITED:
Basically you have to use mockery in each test to mock your function, the only problem is that you must use require against import with the modules you want to mock. Look this example:
const moduleUnderTest = './moduleB.js';
const moduleA_fake = { getResult: () => { return "This is a fake result"; } } ;
describe("Mocking functions", () => {
it('Should be Fake Result', (done) => {
mock.registerAllowable(moduleUnderTest);
mock.registerMock('./moduleA.js', moduleA_fake);
mock.enable({ useCleanCache: true });
let ModuleB = require(moduleUnderTest);
let res = ModuleB.functionToTest();
res.should.be.eql("This is a fake result");
mock.disable();
mock.deregisterAll();
done();
});
it('Should be Real Result', (done) => {
let ModuleB = require(moduleUnderTest);
let res = ModuleB.functionToTest();
res.should.be.eql("real foo");
done();
});
});
You could see the complete example Here
For anyone who is interested in how it works with babel-rewire: The trick is to use __RewireAPI__.
import * as ModuleB from '../ModuleB'
import {__RewireAPI__ as ModuleBRewire} from '../ModuleA';
it("does the right thing", () => {
let moduleAMock = {
getResult: () => {
return fakeResult;
}
}
ModuleBRewire.__Rewire__('ModuleA', moduleAMock);
let xyz = ModuleB.functionToTest();
});