WeSt
WeSt

Reputation: 929

Mock dynamic html element in jest

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

Upvotes: 15

Views: 78526

Answers (4)

Teneff
Teneff

Reputation: 32158

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:

import { 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.

Upvotes: 25

Akaisteph7
Akaisteph7

Reputation: 6436

A combination of the top two answers worked well for me:

...

beforeAll(() => {
  const modalDiv = document.createElement("div");
  modalDiv.setAttribute("id", "react-modal-container");
  document.body.appendChild(modalDiv);
});

...

Upvotes: 0

Patryk
Patryk

Reputation: 338

This line have problem:

 let listItem = document.getElementById(props.id);

Create element in the first place for mocking in jest. Be sure to wait for document and inject it.

What you doing is getting element when isn't ready to test / non exist in this context.

--- EDITED TO ADD EXAMPLE ---

What need to be added: https://jestjs.io/docs/en/configuration#setupfiles-array

Others response to similar problem with example solution: https://stackoverflow.com/a/41186342/5768332

Upvotes: 1

paragxviii
paragxviii

Reputation: 220

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');
    });
    

Upvotes: 8

Related Questions