I want to write a test for a utils method. In that method I get a html element by id and then change the color of the element. The problem is that element is only available after a button click. How can I mock the element?
UtilListItem.js
import variables from '../stylesheets/Variables.scss';
export function activeListItem(props){
let listItem = document.getElementById(props.id);
listItem.style.backgroundColor = variables.whiteGray;
return listItem;
}
UtilListeItem.test.js
it('check if the correct color is set for the acitve list item', () => {
let props = {id:'123'}
const listItem = activeListItem(props);
expect(listItem.style.backgroundColor).toBe('#ededed');
});
error
TypeError: Cannot read property 'style' of null
To mock a React component, the most straightforward approach is to use the jest. mock function. You mock the file that exports the component and replace it with a custom implementation. Since a component is basically a function, the mock should also return a function.
You can use it like this: imoprt { activeListItem } from './utils'; let spy; beforeAll(() => { spy = jest. spyOn(document, 'getElementById'); }); describe('activeListItem', () => { describe('with found element', () => { let mockElement; beforeAll(() => { // here you create the element that the document.
Jest actually ships with jsdom and the environment already configured. You can override it with the testEnvironment setting. If you need to set up more aspects of the environment though, you can use the setupTestFrameworkScriptFile setting to point to a file that executes before all of your tests run.
If you need to mock a global variable for all of your tests, you can use the setupFiles in your Jest config and point it to a file that mocks the necessary variables. This way, you will have the global variable mocked globally for all test suites.
I'd suggest you to you jest.spyOn. It's a really handy way to spy on a function and/or attach some mock behaviour.
You can use it like this:
imoprt { activeListItem } from './utils';
let spy;
beforeAll(() => {
spy = jest.spyOn(document, 'getElementById');
});
describe('activeListItem', () => {
describe('with found element', () => {
let mockElement;
beforeAll(() => {
// here you create the element that the document.createElement will return
// it might be even without an id
mockElement = document.createElement(....);
spy.mockReturnValue(mockElement);
});
// and then you could expect it to have the background
it('should have the background applied', () => {
expect(mockElement.style.backgroundColor).toBe('#ededed');
});
});
describe('without found element', () => {
// and here you can create a scenario
// when document.createElement returns null
beforeAll(() => {
spy.mockReturnValue(null);
});
// and expect you function not to throw an error
it('should not throw an error', () => {
expect(() => activeListItem({id:'123'})).not.toThrow();
});
});
});
It's also a good idea to mock the .scss
file, since it's a dependency of your utility file, so that when it's change it won't affect your unit test.
There are two options I can think of, you can opt either of them:
Put a check on listItem of function activeListItem
export function activeListItem(props) {
let listItem = document.getElementById(props.id);
if (listItem === null) {
return;
}
listItem.style.backgroundColor = variables.whiteGray;
return listItem;
}
Add dummy element in your test case
it('check if the correct color is set for the acitve list item', () => {
/** Create and add dummy div **/
let testId = "dummy-testId";
let newDiv = document.createElement("div");
newDiv.setAttribute("id", testId);
document.body.appendChild(newDiv);
let props = {id: testId}
const listItem = activeListItem(props);
expect(listItem.style.backgroundColor).toBe('#ededed');
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With