Table of contents
1.
Introduction
2.
React Wrapper API
3.
Mount
3.1.
Arguments
3.2.
Returns
4.
Example
4.1.
Program:
4.2.
Program:
4.3.
Output:
5.
FAQs
6.
Key Takeaways
Last Updated: Mar 27, 2024
Easy

Full DOM Rendering

Career growth poll
Do you think IIT Guwahati certified course can help you in your career?

Introduction

While testing react components, a developer may come across some components that interact with DOM APIs, need to test components wrapped in higher-order components, or require the entire lifecycle to test the component thoroughly. Full DOM Rendering is perfectly ideal for such use cases.

Full DOM rendering requires that an entire DOM API is available at the global scope. That means that you must run it in an environment similar to a browser environment. If you don't want to run your tests inside a browser, depending on a library called jsdom is the recommended approach for using mountjsdom is essentially a headless browser implemented entirely in JavaScript.

While writing a test, it is worth noting that, unlike shallow or static rendering, full rendering mounts the component in the DOM, meaning the tests can affect each other if they are all using the same DOM. So if necessary, use .unmount() or something similar for cleanup.

React Wrapper API

Wrapper components surround unknown components and provide a default structure to display the child components. React provides Wrapper API, which is helpful to test user interface (UI) elements that users can repeatedly use throughout a design, like modals, template pages, and information tiles.

The React Wrapper API contains these multiple useful methods, which return the given object:

  • .find(selector) => ReactWrapper: Finds every node in the render tree that matches the selector you provide.
  • .findWhere(predicate) => ReactWrapper: Finds every node in the render tree that returns true for the predicate function you provide.
  • .filter(selector) => ReactWrapper: Removes nodes in the current wrapper that do not match the selector you provide.
  • .filterWhere(predicate) => ReactWrapper: Removes nodes in the current wrapper that do not return true for the predicate function you provide.
  • .contains(node) => Boolean: Returns whether or not a node you give is somewhere in the render tree.
  • .hasClass(className) => Boolean: Returns boolean for whether or not the current root node has the class name or not gave.
  • .is(selector) => Boolean: Returns boolean for whether or not the current node matches a selector you provide.
  • .not(selector) => ReactWrapper: Remove boolean for nodes in the current wrapper that match the selector you provide. It is inverse of .filter().
  • .children() => ReactWrapper: Returns a wrapper with all the children nodes of the current wrapper.
  • .parents() => ReactWrapper: Returns a react wrapper with all the parents of the current node.
  • .parent() => ReactWrapper: Returns a react wrapper with the direct parent of the current node.
  • .closest(selector) => ReactWrapper: Returns a react wrapper with the first ancestor of the current node to match the selector you provide.
  • .text() => String: Returns a string for the current render tree's text nodes.
  • .get(index) => ReactWrapper: â€‹â€‹Returns the node of the current wrapper at the index you provide.
  • .at(index) => ReactWrapper: Returns a react wrapper of the node of the current wrapper at the index you provide.
  • .first() => ReactWrapper: Returns a wrapper of the current wrapper’s first node.
  • .last() => ReactWrapper: Returns a wrapper of the current wrapper’s last node.
  • .state([key]) => Any: Returns the root component’s state.
  • .props() => Object: Return’s the root component’s props.
  • .prop(key) => Any: Return’s the root component’s named prop.
  • .simulate(event[, data]) => ReactWrapper: Simulates an event on the current node and returns a wrapper.
  • .setState(nextState) => ReactWrapper: Sets the state of the root component manually.
  • .setProps(nextProps) => ReactWrapper: Sets the props of the root component manually.
  • .instance() => ReactComponent: Returns the root component’s instance.
  • .update() => ReactWrapper: Calls .forceUpdate() on the instance of the root component and returns a wrapper.
  • .type() => String|Function: Returns the wrapper’s type of the current node.
  • .forEach(fn) => ReactWrapper: Iterates through every node of the current react wrapper and executes the function you provide.
  • .map(fn) => Array: Maps nodes’ current array to another array.
  • reduce(fn[, initialValue]) => Any: Reduces the nodes’ current array to a value.
  • reduceRight(fn[, initialValue]) => Any:  Reduces the nodes’ current array to a value, from right to left.
  • some(selector) => Boolean: Returns boolean for whether or not any of the nodes in the wrapper match the selector you provide.
  • someWhere(predicate) => Boolean: Returns boolean for whether or not any of the nodes in the wrapper pass the predicate function you provide.
  • every (selector) => Boolean: Returns boolean for whether or not all of the nodes in the wrapper match the selector you provide.
  • everyWhere(selector) => Boolean: Returns boolean for whether or not any of the nodes in the wrapper pass the predicate function you provide.

Mount

The mount method renders the full DOM and includes the child components of the parent component that we are running the tests. This method is more suitable when there are components that directly interfere with DOM API or the lifecycle methods of React. The syntax for the mount method is:

mount(node[, options]) => ReactWrapper

The mount method includes multiple arguments that have different usage and implementations.

Arguments

  • node (ReactElement): The node you are going to render
  • options (Object [optional]): The basic options.
  • options.context: (Object [optional]): The context that will pass into the component
  • options.attachTo: (DOMElement [optional]): DOM Element to which the component will attach.
  • options.childContextTypes: (Object [optional]): Merged contextTypes for all of the wrapper’s children.
  • options.wrappingComponent: (ComponentType [optional]): A component that renders as a parent of the node. you can use it to provide context to the node, among other things. It is worth noting that wrappingComponent must render its children.
  • options.wrappingComponentProps: (Object [optional]): Initial props to pass to the wrappingComponent if specified.

Returns

  • ReactWrapper: The wrapper instance around the output that renders.

Example

So far, we have explored a large number of methods and arguments available in the Wrapper API and the Mount method. Now we will see the implementation of some methods and arguments with the help of an example.

This example will test an increment and decrement button application and its components using the Mocha testing framework, Enzyme, and React. The code for the application looks something like this:

Program:

//App.js
import React, { Component } from 'react';
import axios from 'axios';

export const doIncrement = prevState => ({
 counter: prevState.counter + 1,
});

export const doDecrement = prevState => ({
 counter: prevState.counter - 1,
});

class App extends Component {
 constructor() {
   super();

   this.state = {
     counter: 0,
     asyncCounters: null,
   };

   this.onIncrement = this.onIncrement.bind(this);
   this.onDecrement = this.onDecrement.bind(this);
 }

 componentDidMount() {
   axios
     .get('http://mydomain/counter')
     .then(counter => this.setState({ asyncCounters: counter }))
     .catch(error => console.log(error));
 }

 onIncrement() {
   this.setState(doIncrement);
 }

 onDecrement() {
   this.setState(doDecrement);
 }

 render() {
   const { counter } = this.state;

   return (
     <div>
       <h1>My Counter</h1>
       <Counter counter={counter} />

       <button type="button" onClick={this.onIncrement}>
         Increment
       </button>

       <button type="button" onClick={this.onDecrement}>
         Decrement
       </button>
     </div>
   );
 }
}

export const Counter = ({ counter }) => <p>{counter}</p>;

export default App;
You can also try this code with Online Javascript Compiler
Run Code

Now we can write our test code to test the components of this application. This test suite will perform four tests, checking the increment/decrement component, props, and component mount.

Program:

//App.spec.js
import React from 'react';
import axios from 'axios';
import sinon from 'sinon';
import { mount } from 'enzyme';
import App, { doIncrement, doDecrement, Counter } from './App';

describe('App Component', () => {
 const result = [3, 5, 9];
 const promise = Promise.resolve(result);

 before(() => {
   sinon
     .stub(axios, 'get')
     .withArgs('http://mydomain/counter')
     .returns(promise);
 });

 after(() => {
   axios.get.restore();
 });
  it('passes all props to Counter wrapper', () => {
   const wrapper = shallow(<App />);
  
   // We use .find to find node that matches counter.
   let counterWrapper = wrapper.find(Counter);

   // We use props to match the components props.
   expect(counterWrapper.props().counter).to.equal(0);

   // we set state of our counter component.
   wrapper.setState({ counter: -1 });

   counterWrapper = wrapper.find(Counter);
   expect(counterWrapper.props().counter).to.equal(-1);
 });

 // we test the increpement and decrement components of the app.
 it('increments the counter', () => {
   const wrapper = shallow(<App />);

   wrapper.setState({ counter: 0 });
   wrapper.find('button').at(0).simulate('click');

   expect(wrapper.state().counter).to.equal(1);
 });

 it('decrements the counter', () => {
   const wrapper = shallow(<App />);

   wrapper.setState({ counter: 0 });
   wrapper.find('button').at(1).simulate('click');

   expect(wrapper.state().counter).to.equal(-1);
 });

 // we use mount to check if the component mounts.
 it('calls componentDidMount', () => {
   sinon.spy(App.prototype, 'componentDidMount');

   const wrapper = mount(<App />);
   expect(App.prototype.componentDidMount.calledOnce).to.equal(true);
 });
});
You can also try this code with Online Javascript Compiler
Run Code

Output:

FAQs

1. What is shallow rendering?

Ans: Shallow rendering lets you render a component a level deeper and assert facts about the render method of shallow returns without worrying about the child components' behavior, which did not instantiate or render.

 

2. What is the difference between jest and enzyme?

Ans:

 

 

3. What is mounting and unmounting in React?

Ans: React JS's primary purpose is to find methods to modify the DOM to match what the components want to be rendered on the screen.

React does so by mounting, i.e., adding nodes to the DOM, unmounting, which is removing nodes from the DOM, and making changes to nodes already in the DOM, which is called updating.

 

4. When is render called in React?

Ans: The render() method is not user callable. It is part of the React component lifecycle's part. Generally, React calls it at various app stages when the React component instantiates for the first time or a new update to the component state. Render doesn't take any arguments and returns a JSX.

Key Takeaways

This article extensively discussed Full DOM Rendering API Reference and Mounting and their implementation in React JS using Enzyme and Mocha. We learned about all the methods of the React Wrapper API and the concepts, arguments, and returns of the Mount methods in Enzyme. Through our example, we also implemented many of these methods and arguments.

There is a lot more to explore here. If you are interested in testing with Mocha or Jest and React JS, check out our introductory articles like  Introduction to MochaMocha Assertions, and Basics of React. We hope that this blog has helped you enhance your knowledge regarding Mocha and Chai. If you would like to learn more, head over to our practice platform Coding Ninjas Studio to practice top problems, attempt mock tests, read interview experiences, and much more. Do upvote our blog to help other ninjas grow. Happy Coding!

 

Live masterclass