Mock dynamic html element in jest

Viewed 43201

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
3 Answers

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:

  1. 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;
     }
    
  2. 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');
    });
    
Related