Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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
like image 742
WeSt Avatar asked Jun 27 '19 06:06

WeSt


People also ask

How do you mock an element in Jest?

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.

How do you mock in HTML?

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.

Does Jest use jsdom?

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.

Is Jest mock global?

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.


2 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.

like image 71
Teneff Avatar answered Oct 17 '22 02:10

Teneff


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');
    });
    
like image 8
paragxviii Avatar answered Oct 17 '22 03:10

paragxviii