How do I deal with DOM requests when testing React code with Jest and Enzyme?











up vote
1
down vote

favorite












I have a React 16 application created from create-react-app (which uses react-scripts 1.1.4) with the following component we created:



import React, {Component} from 'react';
import './ListNav.css';

const tabs = {
previousIndex: 0
};

function styleStringGenerator(index) {
let styleString = {
leftBase: 'left: ',
widthBase: 'width: '
}

if (index === 0) {
styleString.aggregate = `${styleString.leftBase} 0; ${styleString.widthBase}${tabs.widths[0]}px;`;
} else {
styleString.aggregate = `${styleString.leftBase}${tabs.distanceFromOrigin[index]}px; ${styleString.widthBase}${tabs.widths[index]}px;`;
}

return styleString.aggregate;
}

class ListNav extends Component{
constructor(props){
super(props);
this.handleDataTypeSelection = this.handleDataTypeSelection.bind(this);

this.tabScrollWidth = null;

this.setInputRef = element => {
this.tabScrollWidth = element;
};
}

render(){
const dataTypeSelection = (s) => () => this.handleDataTypeSelection(s);

return(
<div className="tab" ref={this.setInputRef}>
<div className="tab__header" onClick={dataTypeSelection("Addresses")}>
<span className="tab__title">Addresses</span>
</div>
<div className="tab__header" onClick={dataTypeSelection("Hotspots")}>
<span className="tab__title">Hotspot Data</span>
</div>
<div className="tab__header" onClick={dataTypeSelection("PSRs")}>
<span className="tab__title">PSRs</span>
</div>
<div className="tab__underline"></div>
</div>
);
}

componentDidMount(){
tabs.elements = document.querySelectorAll('.tab__header');
tabs.length = tabs.elements.length;
tabs.finalIndex = tabs.length - 1;
tabs.totalWidth = document.querySelector('.tab').scrollWidth;
console.log(document);

tabs.widths =
tabs.elements.forEach((v, index, array) => {
tabs.widths.push(v.scrollWidth);
});

tabs.distanceFromOrigin = [0];
tabs.widths.forEach((v, index, array) => {
if (index > 0) {
tabs.distanceFromOrigin.push(array[index-1] + tabs.distanceFromOrigin[index-1]);
}
});

let styleString = styleStringGenerator(0);
document.querySelector('.tab__underline').setAttribute('style', styleString);
document.querySelector('.tab__title').setAttribute('class', 'tab__title tab__title--active');

document.querySelectorAll('.tab__header').forEach((v, index, array) => v.addEventListener('click', function(){
const currentIndex = index;

if (tabs.previousIndex !== currentIndex) {

const styleString = styleStringGenerator(index);

document.querySelector('.tab__underline').setAttribute('style', styleString);
document.querySelector('.tab__title--active').setAttribute('class', 'tab__title');
this.querySelector('.tab__title').setAttribute('class', 'tab__title tab__title--active');

tabs.previousIndex = (function(){return currentIndex})();
}

}, index));
}

handleDataTypeSelection(s){
this.props.getData(s);
}
}

export default ListNav;


I am using Jest 20.0.4, Enzyme 3.3.0 and enzyme-adapter-react-16 1.1.1 and created the following test:



import React from 'react';
import Enzyme from 'enzyme';
import {shallow, mount} from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';

import ListNav from '../components/map-list/list-nav/ListNav';

Enzyme.configure({
adapter: new Adapter()
});

const listNav = shallow(<ListNav/>);

describe('ListNav', () => {
it('ListNav renders without crashing', () => {
expect(listNav).toMatchSnapshot();
});
});


When I run my test, I get the following error:




TypeError: Cannot read property 'scrollWidth' of null




The line in question is in the component, in the componentDidMount() call. The code fails on the line:



tabs.totalWidth = document.querySelector('.tab').scrollWidth;


because tabs.totalWidth = document.querySelector('.tab') evaluates to null so scrollWidth can't be read. I am using shallow(<ListNav/>) and can see "classname": "tab" in my snapshot, but the test cannot seem to find it. Any ideas as to how to either better implement my test or better construct my code?










share|improve this question
























  • Have you tried using mount instead of shallow? Generally I feel like enzyme works better when you use the methods they've provided you, like .find()
    – tenor528
    Jul 23 at 17:55










  • I did try mount() but got the same result.
    – TortillaCurtain
    Jul 23 at 18:00















up vote
1
down vote

favorite












I have a React 16 application created from create-react-app (which uses react-scripts 1.1.4) with the following component we created:



import React, {Component} from 'react';
import './ListNav.css';

const tabs = {
previousIndex: 0
};

function styleStringGenerator(index) {
let styleString = {
leftBase: 'left: ',
widthBase: 'width: '
}

if (index === 0) {
styleString.aggregate = `${styleString.leftBase} 0; ${styleString.widthBase}${tabs.widths[0]}px;`;
} else {
styleString.aggregate = `${styleString.leftBase}${tabs.distanceFromOrigin[index]}px; ${styleString.widthBase}${tabs.widths[index]}px;`;
}

return styleString.aggregate;
}

class ListNav extends Component{
constructor(props){
super(props);
this.handleDataTypeSelection = this.handleDataTypeSelection.bind(this);

this.tabScrollWidth = null;

this.setInputRef = element => {
this.tabScrollWidth = element;
};
}

render(){
const dataTypeSelection = (s) => () => this.handleDataTypeSelection(s);

return(
<div className="tab" ref={this.setInputRef}>
<div className="tab__header" onClick={dataTypeSelection("Addresses")}>
<span className="tab__title">Addresses</span>
</div>
<div className="tab__header" onClick={dataTypeSelection("Hotspots")}>
<span className="tab__title">Hotspot Data</span>
</div>
<div className="tab__header" onClick={dataTypeSelection("PSRs")}>
<span className="tab__title">PSRs</span>
</div>
<div className="tab__underline"></div>
</div>
);
}

componentDidMount(){
tabs.elements = document.querySelectorAll('.tab__header');
tabs.length = tabs.elements.length;
tabs.finalIndex = tabs.length - 1;
tabs.totalWidth = document.querySelector('.tab').scrollWidth;
console.log(document);

tabs.widths =
tabs.elements.forEach((v, index, array) => {
tabs.widths.push(v.scrollWidth);
});

tabs.distanceFromOrigin = [0];
tabs.widths.forEach((v, index, array) => {
if (index > 0) {
tabs.distanceFromOrigin.push(array[index-1] + tabs.distanceFromOrigin[index-1]);
}
});

let styleString = styleStringGenerator(0);
document.querySelector('.tab__underline').setAttribute('style', styleString);
document.querySelector('.tab__title').setAttribute('class', 'tab__title tab__title--active');

document.querySelectorAll('.tab__header').forEach((v, index, array) => v.addEventListener('click', function(){
const currentIndex = index;

if (tabs.previousIndex !== currentIndex) {

const styleString = styleStringGenerator(index);

document.querySelector('.tab__underline').setAttribute('style', styleString);
document.querySelector('.tab__title--active').setAttribute('class', 'tab__title');
this.querySelector('.tab__title').setAttribute('class', 'tab__title tab__title--active');

tabs.previousIndex = (function(){return currentIndex})();
}

}, index));
}

handleDataTypeSelection(s){
this.props.getData(s);
}
}

export default ListNav;


I am using Jest 20.0.4, Enzyme 3.3.0 and enzyme-adapter-react-16 1.1.1 and created the following test:



import React from 'react';
import Enzyme from 'enzyme';
import {shallow, mount} from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';

import ListNav from '../components/map-list/list-nav/ListNav';

Enzyme.configure({
adapter: new Adapter()
});

const listNav = shallow(<ListNav/>);

describe('ListNav', () => {
it('ListNav renders without crashing', () => {
expect(listNav).toMatchSnapshot();
});
});


When I run my test, I get the following error:




TypeError: Cannot read property 'scrollWidth' of null




The line in question is in the component, in the componentDidMount() call. The code fails on the line:



tabs.totalWidth = document.querySelector('.tab').scrollWidth;


because tabs.totalWidth = document.querySelector('.tab') evaluates to null so scrollWidth can't be read. I am using shallow(<ListNav/>) and can see "classname": "tab" in my snapshot, but the test cannot seem to find it. Any ideas as to how to either better implement my test or better construct my code?










share|improve this question
























  • Have you tried using mount instead of shallow? Generally I feel like enzyme works better when you use the methods they've provided you, like .find()
    – tenor528
    Jul 23 at 17:55










  • I did try mount() but got the same result.
    – TortillaCurtain
    Jul 23 at 18:00













up vote
1
down vote

favorite









up vote
1
down vote

favorite











I have a React 16 application created from create-react-app (which uses react-scripts 1.1.4) with the following component we created:



import React, {Component} from 'react';
import './ListNav.css';

const tabs = {
previousIndex: 0
};

function styleStringGenerator(index) {
let styleString = {
leftBase: 'left: ',
widthBase: 'width: '
}

if (index === 0) {
styleString.aggregate = `${styleString.leftBase} 0; ${styleString.widthBase}${tabs.widths[0]}px;`;
} else {
styleString.aggregate = `${styleString.leftBase}${tabs.distanceFromOrigin[index]}px; ${styleString.widthBase}${tabs.widths[index]}px;`;
}

return styleString.aggregate;
}

class ListNav extends Component{
constructor(props){
super(props);
this.handleDataTypeSelection = this.handleDataTypeSelection.bind(this);

this.tabScrollWidth = null;

this.setInputRef = element => {
this.tabScrollWidth = element;
};
}

render(){
const dataTypeSelection = (s) => () => this.handleDataTypeSelection(s);

return(
<div className="tab" ref={this.setInputRef}>
<div className="tab__header" onClick={dataTypeSelection("Addresses")}>
<span className="tab__title">Addresses</span>
</div>
<div className="tab__header" onClick={dataTypeSelection("Hotspots")}>
<span className="tab__title">Hotspot Data</span>
</div>
<div className="tab__header" onClick={dataTypeSelection("PSRs")}>
<span className="tab__title">PSRs</span>
</div>
<div className="tab__underline"></div>
</div>
);
}

componentDidMount(){
tabs.elements = document.querySelectorAll('.tab__header');
tabs.length = tabs.elements.length;
tabs.finalIndex = tabs.length - 1;
tabs.totalWidth = document.querySelector('.tab').scrollWidth;
console.log(document);

tabs.widths =
tabs.elements.forEach((v, index, array) => {
tabs.widths.push(v.scrollWidth);
});

tabs.distanceFromOrigin = [0];
tabs.widths.forEach((v, index, array) => {
if (index > 0) {
tabs.distanceFromOrigin.push(array[index-1] + tabs.distanceFromOrigin[index-1]);
}
});

let styleString = styleStringGenerator(0);
document.querySelector('.tab__underline').setAttribute('style', styleString);
document.querySelector('.tab__title').setAttribute('class', 'tab__title tab__title--active');

document.querySelectorAll('.tab__header').forEach((v, index, array) => v.addEventListener('click', function(){
const currentIndex = index;

if (tabs.previousIndex !== currentIndex) {

const styleString = styleStringGenerator(index);

document.querySelector('.tab__underline').setAttribute('style', styleString);
document.querySelector('.tab__title--active').setAttribute('class', 'tab__title');
this.querySelector('.tab__title').setAttribute('class', 'tab__title tab__title--active');

tabs.previousIndex = (function(){return currentIndex})();
}

}, index));
}

handleDataTypeSelection(s){
this.props.getData(s);
}
}

export default ListNav;


I am using Jest 20.0.4, Enzyme 3.3.0 and enzyme-adapter-react-16 1.1.1 and created the following test:



import React from 'react';
import Enzyme from 'enzyme';
import {shallow, mount} from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';

import ListNav from '../components/map-list/list-nav/ListNav';

Enzyme.configure({
adapter: new Adapter()
});

const listNav = shallow(<ListNav/>);

describe('ListNav', () => {
it('ListNav renders without crashing', () => {
expect(listNav).toMatchSnapshot();
});
});


When I run my test, I get the following error:




TypeError: Cannot read property 'scrollWidth' of null




The line in question is in the component, in the componentDidMount() call. The code fails on the line:



tabs.totalWidth = document.querySelector('.tab').scrollWidth;


because tabs.totalWidth = document.querySelector('.tab') evaluates to null so scrollWidth can't be read. I am using shallow(<ListNav/>) and can see "classname": "tab" in my snapshot, but the test cannot seem to find it. Any ideas as to how to either better implement my test or better construct my code?










share|improve this question















I have a React 16 application created from create-react-app (which uses react-scripts 1.1.4) with the following component we created:



import React, {Component} from 'react';
import './ListNav.css';

const tabs = {
previousIndex: 0
};

function styleStringGenerator(index) {
let styleString = {
leftBase: 'left: ',
widthBase: 'width: '
}

if (index === 0) {
styleString.aggregate = `${styleString.leftBase} 0; ${styleString.widthBase}${tabs.widths[0]}px;`;
} else {
styleString.aggregate = `${styleString.leftBase}${tabs.distanceFromOrigin[index]}px; ${styleString.widthBase}${tabs.widths[index]}px;`;
}

return styleString.aggregate;
}

class ListNav extends Component{
constructor(props){
super(props);
this.handleDataTypeSelection = this.handleDataTypeSelection.bind(this);

this.tabScrollWidth = null;

this.setInputRef = element => {
this.tabScrollWidth = element;
};
}

render(){
const dataTypeSelection = (s) => () => this.handleDataTypeSelection(s);

return(
<div className="tab" ref={this.setInputRef}>
<div className="tab__header" onClick={dataTypeSelection("Addresses")}>
<span className="tab__title">Addresses</span>
</div>
<div className="tab__header" onClick={dataTypeSelection("Hotspots")}>
<span className="tab__title">Hotspot Data</span>
</div>
<div className="tab__header" onClick={dataTypeSelection("PSRs")}>
<span className="tab__title">PSRs</span>
</div>
<div className="tab__underline"></div>
</div>
);
}

componentDidMount(){
tabs.elements = document.querySelectorAll('.tab__header');
tabs.length = tabs.elements.length;
tabs.finalIndex = tabs.length - 1;
tabs.totalWidth = document.querySelector('.tab').scrollWidth;
console.log(document);

tabs.widths =
tabs.elements.forEach((v, index, array) => {
tabs.widths.push(v.scrollWidth);
});

tabs.distanceFromOrigin = [0];
tabs.widths.forEach((v, index, array) => {
if (index > 0) {
tabs.distanceFromOrigin.push(array[index-1] + tabs.distanceFromOrigin[index-1]);
}
});

let styleString = styleStringGenerator(0);
document.querySelector('.tab__underline').setAttribute('style', styleString);
document.querySelector('.tab__title').setAttribute('class', 'tab__title tab__title--active');

document.querySelectorAll('.tab__header').forEach((v, index, array) => v.addEventListener('click', function(){
const currentIndex = index;

if (tabs.previousIndex !== currentIndex) {

const styleString = styleStringGenerator(index);

document.querySelector('.tab__underline').setAttribute('style', styleString);
document.querySelector('.tab__title--active').setAttribute('class', 'tab__title');
this.querySelector('.tab__title').setAttribute('class', 'tab__title tab__title--active');

tabs.previousIndex = (function(){return currentIndex})();
}

}, index));
}

handleDataTypeSelection(s){
this.props.getData(s);
}
}

export default ListNav;


I am using Jest 20.0.4, Enzyme 3.3.0 and enzyme-adapter-react-16 1.1.1 and created the following test:



import React from 'react';
import Enzyme from 'enzyme';
import {shallow, mount} from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';

import ListNav from '../components/map-list/list-nav/ListNav';

Enzyme.configure({
adapter: new Adapter()
});

const listNav = shallow(<ListNav/>);

describe('ListNav', () => {
it('ListNav renders without crashing', () => {
expect(listNav).toMatchSnapshot();
});
});


When I run my test, I get the following error:




TypeError: Cannot read property 'scrollWidth' of null




The line in question is in the component, in the componentDidMount() call. The code fails on the line:



tabs.totalWidth = document.querySelector('.tab').scrollWidth;


because tabs.totalWidth = document.querySelector('.tab') evaluates to null so scrollWidth can't be read. I am using shallow(<ListNav/>) and can see "classname": "tab" in my snapshot, but the test cannot seem to find it. Any ideas as to how to either better implement my test or better construct my code?







reactjs jestjs enzyme






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 7 at 7:52









skyboyer

2,69511027




2,69511027










asked Jul 23 at 17:27









TortillaCurtain

1559




1559












  • Have you tried using mount instead of shallow? Generally I feel like enzyme works better when you use the methods they've provided you, like .find()
    – tenor528
    Jul 23 at 17:55










  • I did try mount() but got the same result.
    – TortillaCurtain
    Jul 23 at 18:00


















  • Have you tried using mount instead of shallow? Generally I feel like enzyme works better when you use the methods they've provided you, like .find()
    – tenor528
    Jul 23 at 17:55










  • I did try mount() but got the same result.
    – TortillaCurtain
    Jul 23 at 18:00
















Have you tried using mount instead of shallow? Generally I feel like enzyme works better when you use the methods they've provided you, like .find()
– tenor528
Jul 23 at 17:55




Have you tried using mount instead of shallow? Generally I feel like enzyme works better when you use the methods they've provided you, like .find()
– tenor528
Jul 23 at 17:55












I did try mount() but got the same result.
– TortillaCurtain
Jul 23 at 18:00




I did try mount() but got the same result.
– TortillaCurtain
Jul 23 at 18:00












1 Answer
1






active

oldest

votes

















up vote
0
down vote













Solution 1:



Make your document dependency swappable using a closure. This way, in your unit tests you can provide a mock.



Usage in your real code would be:



import ListNav from "./ListNav";
...
render(){
return <ListNav/>;
}


Usage in your tests:



import { create } from "./ListNav";

it('should...', ()=>{
const documentMock = { title: "mock title" };
const ListNavWithMock = create(documentMock);
const component = shallow(<ListNavWithMock />);
});


In order to support that your module will have to be modified like this:



import React from "react";
export const create = documentInstance => {
return class ListNav extends React.Component {
render() {
return <div>{documentInstance.title}</div>;
}
};
};

export default create(document);


See an exemple here where both ListNav and ListNavWithMock are loaded.



Solution 2 (if you use webpack)




  1. Abstract away the code that relies on the document api by creating a new module called documentHelper.js

  2. In your component, import documentHelper

  3. In your unit test, swap the documentHelper module with a mock using https://github.com/plasticine/inject-loader.


Example:



describe('ListNav', () => {
let ListNav ;
let documentHelperMock;

beforeEach(() => {
documentHelperMock= { title: "mock title" };
ListNav= require('inject-loader!./ListNav')({
'.documentHelperMock': {documentHelperMock},
});
});

it('should ...', () => {
const wrapper = shallow(<ListNav/>)
});
});


Note: make sure you don't import the module under test (ListNav) at the top of your file. The require call does that part.



This approach is less intrusive because component code does not have to be modified in a way that makes it obvious that it's for testing purpose. It just makes the code cleaner by moving document specific code out of your component.



This approach is also easier because the APIs you will have to mock will be your own (documentHelper.UpdateTabs). In the first solution your mock might have to be complex (querySelector and what it returns).






share|improve this answer























  • Thank you for your very complete example. I am using Webpack, but via create-react-app. I tried your second approach but could not get inject-loader to work. I always received a Cannot find module error despite repeated attempts. I found that if I used refs to pass values, I could change my code to use them rather than document calls. I'm not sure if this is the best approach, but it solved my problem. I will pursue your Solution 1 as well to see if it works for me.
    – TortillaCurtain
    Jul 26 at 14:59











Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














 

draft saved


draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f51484281%2fhow-do-i-deal-with-dom-requests-when-testing-react-code-with-jest-and-enzyme%23new-answer', 'question_page');
}
);

Post as a guest
































1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes








up vote
0
down vote













Solution 1:



Make your document dependency swappable using a closure. This way, in your unit tests you can provide a mock.



Usage in your real code would be:



import ListNav from "./ListNav";
...
render(){
return <ListNav/>;
}


Usage in your tests:



import { create } from "./ListNav";

it('should...', ()=>{
const documentMock = { title: "mock title" };
const ListNavWithMock = create(documentMock);
const component = shallow(<ListNavWithMock />);
});


In order to support that your module will have to be modified like this:



import React from "react";
export const create = documentInstance => {
return class ListNav extends React.Component {
render() {
return <div>{documentInstance.title}</div>;
}
};
};

export default create(document);


See an exemple here where both ListNav and ListNavWithMock are loaded.



Solution 2 (if you use webpack)




  1. Abstract away the code that relies on the document api by creating a new module called documentHelper.js

  2. In your component, import documentHelper

  3. In your unit test, swap the documentHelper module with a mock using https://github.com/plasticine/inject-loader.


Example:



describe('ListNav', () => {
let ListNav ;
let documentHelperMock;

beforeEach(() => {
documentHelperMock= { title: "mock title" };
ListNav= require('inject-loader!./ListNav')({
'.documentHelperMock': {documentHelperMock},
});
});

it('should ...', () => {
const wrapper = shallow(<ListNav/>)
});
});


Note: make sure you don't import the module under test (ListNav) at the top of your file. The require call does that part.



This approach is less intrusive because component code does not have to be modified in a way that makes it obvious that it's for testing purpose. It just makes the code cleaner by moving document specific code out of your component.



This approach is also easier because the APIs you will have to mock will be your own (documentHelper.UpdateTabs). In the first solution your mock might have to be complex (querySelector and what it returns).






share|improve this answer























  • Thank you for your very complete example. I am using Webpack, but via create-react-app. I tried your second approach but could not get inject-loader to work. I always received a Cannot find module error despite repeated attempts. I found that if I used refs to pass values, I could change my code to use them rather than document calls. I'm not sure if this is the best approach, but it solved my problem. I will pursue your Solution 1 as well to see if it works for me.
    – TortillaCurtain
    Jul 26 at 14:59















up vote
0
down vote













Solution 1:



Make your document dependency swappable using a closure. This way, in your unit tests you can provide a mock.



Usage in your real code would be:



import ListNav from "./ListNav";
...
render(){
return <ListNav/>;
}


Usage in your tests:



import { create } from "./ListNav";

it('should...', ()=>{
const documentMock = { title: "mock title" };
const ListNavWithMock = create(documentMock);
const component = shallow(<ListNavWithMock />);
});


In order to support that your module will have to be modified like this:



import React from "react";
export const create = documentInstance => {
return class ListNav extends React.Component {
render() {
return <div>{documentInstance.title}</div>;
}
};
};

export default create(document);


See an exemple here where both ListNav and ListNavWithMock are loaded.



Solution 2 (if you use webpack)




  1. Abstract away the code that relies on the document api by creating a new module called documentHelper.js

  2. In your component, import documentHelper

  3. In your unit test, swap the documentHelper module with a mock using https://github.com/plasticine/inject-loader.


Example:



describe('ListNav', () => {
let ListNav ;
let documentHelperMock;

beforeEach(() => {
documentHelperMock= { title: "mock title" };
ListNav= require('inject-loader!./ListNav')({
'.documentHelperMock': {documentHelperMock},
});
});

it('should ...', () => {
const wrapper = shallow(<ListNav/>)
});
});


Note: make sure you don't import the module under test (ListNav) at the top of your file. The require call does that part.



This approach is less intrusive because component code does not have to be modified in a way that makes it obvious that it's for testing purpose. It just makes the code cleaner by moving document specific code out of your component.



This approach is also easier because the APIs you will have to mock will be your own (documentHelper.UpdateTabs). In the first solution your mock might have to be complex (querySelector and what it returns).






share|improve this answer























  • Thank you for your very complete example. I am using Webpack, but via create-react-app. I tried your second approach but could not get inject-loader to work. I always received a Cannot find module error despite repeated attempts. I found that if I used refs to pass values, I could change my code to use them rather than document calls. I'm not sure if this is the best approach, but it solved my problem. I will pursue your Solution 1 as well to see if it works for me.
    – TortillaCurtain
    Jul 26 at 14:59













up vote
0
down vote










up vote
0
down vote









Solution 1:



Make your document dependency swappable using a closure. This way, in your unit tests you can provide a mock.



Usage in your real code would be:



import ListNav from "./ListNav";
...
render(){
return <ListNav/>;
}


Usage in your tests:



import { create } from "./ListNav";

it('should...', ()=>{
const documentMock = { title: "mock title" };
const ListNavWithMock = create(documentMock);
const component = shallow(<ListNavWithMock />);
});


In order to support that your module will have to be modified like this:



import React from "react";
export const create = documentInstance => {
return class ListNav extends React.Component {
render() {
return <div>{documentInstance.title}</div>;
}
};
};

export default create(document);


See an exemple here where both ListNav and ListNavWithMock are loaded.



Solution 2 (if you use webpack)




  1. Abstract away the code that relies on the document api by creating a new module called documentHelper.js

  2. In your component, import documentHelper

  3. In your unit test, swap the documentHelper module with a mock using https://github.com/plasticine/inject-loader.


Example:



describe('ListNav', () => {
let ListNav ;
let documentHelperMock;

beforeEach(() => {
documentHelperMock= { title: "mock title" };
ListNav= require('inject-loader!./ListNav')({
'.documentHelperMock': {documentHelperMock},
});
});

it('should ...', () => {
const wrapper = shallow(<ListNav/>)
});
});


Note: make sure you don't import the module under test (ListNav) at the top of your file. The require call does that part.



This approach is less intrusive because component code does not have to be modified in a way that makes it obvious that it's for testing purpose. It just makes the code cleaner by moving document specific code out of your component.



This approach is also easier because the APIs you will have to mock will be your own (documentHelper.UpdateTabs). In the first solution your mock might have to be complex (querySelector and what it returns).






share|improve this answer














Solution 1:



Make your document dependency swappable using a closure. This way, in your unit tests you can provide a mock.



Usage in your real code would be:



import ListNav from "./ListNav";
...
render(){
return <ListNav/>;
}


Usage in your tests:



import { create } from "./ListNav";

it('should...', ()=>{
const documentMock = { title: "mock title" };
const ListNavWithMock = create(documentMock);
const component = shallow(<ListNavWithMock />);
});


In order to support that your module will have to be modified like this:



import React from "react";
export const create = documentInstance => {
return class ListNav extends React.Component {
render() {
return <div>{documentInstance.title}</div>;
}
};
};

export default create(document);


See an exemple here where both ListNav and ListNavWithMock are loaded.



Solution 2 (if you use webpack)




  1. Abstract away the code that relies on the document api by creating a new module called documentHelper.js

  2. In your component, import documentHelper

  3. In your unit test, swap the documentHelper module with a mock using https://github.com/plasticine/inject-loader.


Example:



describe('ListNav', () => {
let ListNav ;
let documentHelperMock;

beforeEach(() => {
documentHelperMock= { title: "mock title" };
ListNav= require('inject-loader!./ListNav')({
'.documentHelperMock': {documentHelperMock},
});
});

it('should ...', () => {
const wrapper = shallow(<ListNav/>)
});
});


Note: make sure you don't import the module under test (ListNav) at the top of your file. The require call does that part.



This approach is less intrusive because component code does not have to be modified in a way that makes it obvious that it's for testing purpose. It just makes the code cleaner by moving document specific code out of your component.



This approach is also easier because the APIs you will have to mock will be your own (documentHelper.UpdateTabs). In the first solution your mock might have to be complex (querySelector and what it returns).







share|improve this answer














share|improve this answer



share|improve this answer








edited Jul 26 at 17:13

























answered Jul 25 at 2:44









Sylvain

11.3k2285129




11.3k2285129












  • Thank you for your very complete example. I am using Webpack, but via create-react-app. I tried your second approach but could not get inject-loader to work. I always received a Cannot find module error despite repeated attempts. I found that if I used refs to pass values, I could change my code to use them rather than document calls. I'm not sure if this is the best approach, but it solved my problem. I will pursue your Solution 1 as well to see if it works for me.
    – TortillaCurtain
    Jul 26 at 14:59


















  • Thank you for your very complete example. I am using Webpack, but via create-react-app. I tried your second approach but could not get inject-loader to work. I always received a Cannot find module error despite repeated attempts. I found that if I used refs to pass values, I could change my code to use them rather than document calls. I'm not sure if this is the best approach, but it solved my problem. I will pursue your Solution 1 as well to see if it works for me.
    – TortillaCurtain
    Jul 26 at 14:59
















Thank you for your very complete example. I am using Webpack, but via create-react-app. I tried your second approach but could not get inject-loader to work. I always received a Cannot find module error despite repeated attempts. I found that if I used refs to pass values, I could change my code to use them rather than document calls. I'm not sure if this is the best approach, but it solved my problem. I will pursue your Solution 1 as well to see if it works for me.
– TortillaCurtain
Jul 26 at 14:59




Thank you for your very complete example. I am using Webpack, but via create-react-app. I tried your second approach but could not get inject-loader to work. I always received a Cannot find module error despite repeated attempts. I found that if I used refs to pass values, I could change my code to use them rather than document calls. I'm not sure if this is the best approach, but it solved my problem. I will pursue your Solution 1 as well to see if it works for me.
– TortillaCurtain
Jul 26 at 14:59


















 

draft saved


draft discarded



















































 


draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f51484281%2fhow-do-i-deal-with-dom-requests-when-testing-react-code-with-jest-and-enzyme%23new-answer', 'question_page');
}
);

Post as a guest




















































































這個網誌中的熱門文章

Tangent Lines Diagram Along Smooth Curve

Yusuf al-Mu'taman ibn Hud

Zucchini