React & Pancakes: A Practical Overview Of React For Software Testers

By James Espie

In a lot of organizations, software testing is a much more collaborative function than it used to be. The “DevOps” approach, or “shifting left”, requires more time spent paired up with developers. This is a good thing. It means testers and developers are working together to build better software, faster.

To collaborate effectively, it helps if testers know the basics of and keep up-to-date with any changes in relevant technologies their developers are using.

Some of the things testers should try to learn are:

  • Much of the jargon and how it relates to various technologies.
  • The benefits of using a particular technology over another.
  • How a particular technology works, even if it’s only a high-level understanding.

These learning suggestions, and other ideas, can all lead to a better understanding of what our development teams are doing.

One technology that’s surged in popularity over the last few years is React. This article will cover some of the terms unique to React, what makes it a great library to use, and how it works. This should allow any tester to collaborate and converse with developers who are using the React framework.

An Introduction To React

React is one of the most popular front-end technologies in use today. If you’re testing in the web space, there are pretty good odds that you’ll come across React at some point.

React is a javascript library created by Facebook. There are many advantages to using React. One advantage is that React is modular, meaning the code is designed in a way to be very reusable and each functional piece is self-contained. Another advantage is that it can render web pages and apps much faster than other front-end technologies. Its modular nature and its speed are two reasons it’s gaining such incredible traction in the world of front-end development.

There’s been some debate between developers over whether React is technically a ‘library’ or a ‘framework’. There is a technical difference between the two. A library is a set of functions and other code that an application can reach out and use. A framework, though, actually defines the functions that an application contains. Geeks for geeks has a good breakdown of libraries vs frameworks.

Lots of other popular front-end technologies (like Angular.js) clearly fit the definition of a framework. But the creators of React define it as a library. The conversation and debate around this can be interesting and it’s worth reading into. Develoger.com and Andrew Ray both have some thought-provoking points around this discussion.

Learning By Example: Making The Pancake App

A great way to learn a new technology is to do something practical with it. So, I gave myself the challenge of making an app. I worked through some tutorials and got some friendly advice from some React developers that I work with. The end result was a finished, functional React application. Introducing the pancake app! For me, it was an excellent way to learn how React worked. It will be the example used to explain the different parts of the React library and how those parts function.

How The Pancake App Works

The pancake app is simple. It renders a new pancake on top of a stack of pancakes, every time you click a button. The CSS is adapted from this CSS pancake stack by Dario Corsi.

The pancake app is not the most glamorous app (there are likely to be bugs), but it should work for the purposes of learning a bit about React.

Feel free to play with it, and view the source code on GitHub.

There are plenty of other demo React apps out there, however, the pancake app is simple and easy enough to pick up the basics. Appendto.com has a nice list of React apps for learning more about different kinds of React apps and the things you could do with the React library along with other libraries.

The Virtual DOM Makes React Fast

You might be familiar with the term Document Object Model or DOM. The DOM is, essentially, the raw HTML that makes up a webpage. React uses a virtual DOM, which is an invisible copy of the DOM that React tracks behind the scenes. Any time there is an action, React updates the virtual DOM. It then compares it to the actual DOM and takes note of the differences. This is where the magic happens. React will only render those differences or the parts of the DOM which have changed. It won’t re-render the whole page, as other JavaScript technologies might. This saves a good deal of response time for an application and gives the application the ability to render changed components quickly.

In the pancake app, when the user clicks the ‘add a pancake’ button, it renders a new pancake on top of the stack. When React uses the virtual DOM, it doesn’t need to redraw the whole stack, only the new pancake. On a small app like this, you might not notice the speed increase. On a page with lots of content, like Facebook, it makes it much faster than it would be otherwise!

React Components Are Built To Be Reused

As mentioned, React is modular. It is made up of “components” that can be reused and rearranged to suit different needs. A component can be a single HTML element, like a button, or a collection of HTML elements.

In the sample app, each pancake is a component. Each component is designed to be reused. The plate is a component too, and so is the butter.

This is what a basic component called ‘Pancake’ looks like:

Pancake Component

class Pancake extends Component {
 render() {
  return (
	 <div className=pancake>
	 </div>
  );
 }
}

In this case, it renders a single <div> element.

Components can contain other components too. We can combine components into one larger component. This is a combined component called ‘StackOfPancakes’:

StackOf Pancakes Component

class StackOfPancakes extends Component {
render() {
return (
        <div>
            <Butter />
            <Pancake />
            <Pancake />
            <Pancake />
            <Plate />
        </div>
    )
}
}

It renders a <div>, and that div contains other components: a ‘Butter’ component, three ‘Pancake’ components, and a ‘Plate’ component.

Components are good because once they are built they can be used over and over again. This kind of reuse is preferable to having to build multiple instances of the same thing. Components are an important part of how React.js works.

How React.js Works

The fact that React is modular and uses reusable components allows for fast rendering by comparing the DOM to the virtual DOM of an application. Doing so allows an app to only render the parts which have changed at any given point in time. But, how does React actually work? To find out, let’s work through the pancake example. (If you’re feeling hungry, now might be a good time to make yourself some pancakes. Why? Because they’re delicious!)

Firstly, here are a couple of hypothetical pancake-related questions to think about while working through the pancake example:

  1. If you make a blueberry pancake, once it’s cooked, can you ever change it to a plain pancake?
  2. If you’ve made some pancakes, and they’ve gone cold, is there any way to make them hot again?

Don’t worry, these questions are relevant and, if you don’t know the answers, all will be revealed! Now, let’s start the pancake example by taking a look at JSX.

JSX: A Syntax Extension To JavaScript

The most common way to write React components is to use a special syntax called “JSX”.

It’s a variation on JavaScript. The JSX block of code below might seem weird for those familiar with JavaScript.

Code Example

render() {
 return (
        <div class="stack">
            <Butter />
            {this.state.listOfPancakes.map(x => x)}
            <Plate />
            <button onClick={this.addAPancake}>Add a pancake!</button>
        </div>
    )
}

Note that there’s a <div> inside the return statement which is HTML directly inside JavaScript! This might seem unusual to you. That’s because it’s not normal JavaScript, it’s an example of JSX and React knows how to interpret this. This syntax allows for JavaScript inside HTML inside JavaScript (that’s the stuff inside the curly braces {}). It’s like JavaScript inception!

Two Libraries

When talking about web apps, there are two JavaScript libraries that React uses. One is called ‘react’, the other ‘react-dom’.

  1. The react library is the one that does all the heavy lifting with React by creating components and rendering and managing the virtual DOM.
  2. The react-dom library manages interaction between React and the DOM itself. Mostly, it’s used for determining where on your page a component renders.

Components & Render Function

React is made up of components and every component contains a render() function. This function defines what is rendered on screen.

A basic Pancake component written in JSX might look like this:

A Basic Pancake Component

class Pancake extends Component {
render() {
 return (
     <div className=pancake>
     </div>
 );
}
}

In this case, the pancake is just an empty div, its appearance is controlled by CSS. There could be any HTML element represented here such as an image, a button, or a block of text.

Rendering The Component On Screen

The render() function controls what our React component displays, but how does it actually end up on the page? This is where the react-dom library plays its part in the rendering process. It also contains a render() function which controls rendering the components on the DOM itself.

An example is something like:

ReactDOM.render(<Pancake />, document.getElementById('root'));

The example takes two arguments. The name of the component we want to render, and then a location on the page to render it. In this case, assume the DOM starts out looking like this (a basic HTML page):

A DOM Example

<html lang="en">
<head>
 <title>React App</title>
</head>
<body>
    <div>
        <h1>The React components will appear after this!</h1>
        <div id="root"></div>
    </div>
</body>
</html>

In our Pancake app, the <Pancake /> component will appear inside the div with the id=”root”.

Controlling The Appearance Of Components

There are two factors that control the appearance of any component. One is props (short for properties) and the other is state. One of the keys to learning about React is to learn how props and state work.

Props

Remember the question from earlier:

If you make a blueberry pancake, once it’s cooked, can you ever change it to a plain pancake?

Once a blueberry pancake is made, it’s blueberry forever. Even if the blueberries are taken out, it’s still going to taste like a blueberry pancake. The pancake has no control over its own flavour.

Props are like the flavour of the pancake. They are immutable, in other words, a component cannot change its props. Props are added to the HTML tag as options when creating the component. In the pancake example from earlier, there is a drop-down field to choose the flavour of the pancake. Once the user clicks the ‘Add a pancake!’ button, a pancake is added to the stack with the chosen flavour.

Let's create different flavoured pancakes by using a prop called flavour. If we were to create chocolate chip pancakes, the component might look like this:

Example Of A Flavour Component

<Pancake flavor=”chocchip” />

So now, while rendering a stack of pancakes, it can add pancakes of different flavours! This is achieved by giving each pancake a different value for the flavour prop.

Rendering Flavour Components

class StackOfPancakes extends Component {
render() {
 return (
        <div>
            <Butter />
            <Pancake flavor=”blueberry” />
            <Pancake flavor=”chocchip”/>
            <Pancake flavor=””/>
            <Plate />
        </div>
    )
}
}

State

The other question asked was this:

If you’ve made some pancakes, and they’ve gone cold - is there any way to make them hot again?

Of course, the answer is yes! They can be reheated in the microwave, or in a frying pan. The main difference between state and props is that a component can control its own state. It’s a bit of a stretch, but imagine the pancakes can put themselves in the microwave.

It’s common to see the state set when the component is first created and then changed when an action happens. To demonstrate this, let’s imagine the pancake is in a cold state, then changes to hot because heat (the action) was applied. Shown below is a more substantial version of a pancake component.

A Complex Pancake Component Example

class Pancake extends Component {
    constructor(props){
        super(props);
        this.state={temperature: 'hot'}
     }

handleClick = () => {
     if(this.state.temperature==='hot')
         this.setState({temperature: 'cold'})
     else
          this.setState({temperature: 'hot'})
}
render() {
 return (
        <div className={"pancake "+this.props.flavor+" "+this.state.temperature} onClick={this.handleClick}>
     </div>
 );
}
}

There’s a bit to it, so let’s go over it in a few sections.

Section 1: Constructor & Super Props, And Setting The Initial State

class Pancake extends Component {
    constructor(props){
        super(props)
        this.state={temperature: 'hot'}
     }

This first section is called the constructor. The constructor performs any action it’s told to when the component is created. The first line, super(props), calls the constructor from the parent class. This is a little beyond the scope of this article, but React recommends always including super(props) in a constructor. (You can read more about it in the React documentation.)

This second line in the first section sets the initial state of our component. In this case, the constructor sets the temperature to ‘hot’. Since the constructor is setting this state, the pancake initially starts in the ‘hot’ state, and won’t change until an action is taken.

Section 2: A handleClick Example

handleClick = () =>{
     if(this.state.temperature==='hot')
         this.setState({temperature: 'cold'})
     else
          this.setState({temperature: 'hot'})
}

The next section is a function called handleClick. The handleClick determines what happens when a user clicks on a pancake. A component doesn’t have to have a handleClick function, it could have other functions which work to change the state. In this function, when a pancake is clicked, the component toggles its state. If the current value of temperature is ‘hot’, it switches to ‘cold’, and vice versa.

Section 3: Rendering The Component

render() {
 return (
        <div className={"pancake "+this.props.flavor+" "+this.state.temperature} onClick={this.handleClick}>
     </div>
 );
}
}

Finally, the render function. Remember, the render function controls what is displayed on the screen. It renders a div with two attributes: className, and onClick. ClassName is the class attribute of the div. The div is set to include the flavor (prop) and temperature (state) of the pancake. The onClick attribute ties the div back to the handleClick function above.

The important things to know are:

  • The constructor sets the initial state for the component. In this case, it sets the temperature to ‘hot’.
  • The component can then make changes to the state. In this case, the handleClick() function toggles the state from ‘hot’ to ‘cold’ or vice versa.
  • The render function, as always, determines what gets displayed on screen. It can use the props and state that have been set, to help determine this.

Components are controlled by props (properties) and state. The difference between the two is that props are immutable. A component can’t alter its own props. However, components can control their own state.

For further reading, check out the React documentation. It has great sections on props and state, and the section on thinking on React has a good breakdown of when a developer might choose to use props or state.

Components Within Components & Reusing Components

One of the great things about React is that components can be reused across an app. The combined component called “StackOfPancakes”, shown below is interesting for a couple of reasons:

  • It is a component made up of several other components
  • The same component is re-used multiple times

StackOfPancakes Component

class StackOfPancakes extends Component {
render() {
 return (
        <div>
            <Butter />
            <Pancake flavor=”blueberry” />
            <Pancake flavor=”chocchip/>
            <Pancake flavor=””/>
            <Plate />
        </div>
    )
}
}

An important thing to note is that the React render() function can only return one HTML element. If we want to return more, like shown in the StackOfPancakes component above, they would have to be enclosed in a parent element. That’s why the stack of pancakes is wrapped in a parent <div> element. The declared class is called StackOfPancakes. It returns a <div>, which contains:

  • a <Butter /> component
  • three <Pancake /> components
  • a <Plate /> component.

If there were any changes to the Pancake component now, this will apply to any occurrence here or anywhere else the component might be used. This is really useful when you want to maintain consistent styles across an entire site. As an example, imagine the pancake app only made blueberry pancakes.

It would no longer need to include the ‘flavor’ prop, the blueberry flavour could be included directly within the code that makes up the component.

This would only need to be changed once and would apply to every pancake in the app.

React and its modular nature is an important consideration when testing React apps. If a modification is made to a component to suit a particular usage, what happens to other places that component is used? Should every pancake in the pancake app be blueberry flavoured? If so, then great - modifying the component makes sense. If not, then flavour should remain a prop on the pancake component, with blueberry as one of the flavour options.

Being able to consider whether changes to components should be props instead, is an important part of testing React components.

Testing Props And State Using React Dev Tools

Chrome has a React developer tools extension. It’s a powerful tool for testing React applications. Installing the plugin adds a new ‘React’ tab to the developer console.

The React tab shows any props and state applied to a component. The powerful part is being able to edit the props and state within the console. The console can be used to tease out different props, or state, combinations. This can lead to finding unexpected behaviours which may otherwise be missed. Try this using the sample pancake app in the console. Try changing the state, or props, of a pancake component using the React DevTools and see what happens.

React DevTools has a neat context menu available. Right-click on a component to reveal the menu. It can be used to find all instances of a particular component, or easily locate a component’s render function. This menu is really useful when trying to find things on pages with lots of components.

Automation With React

There are lots of different ways to test React applications. Exploring the application to learn and evaluate its features is one way, and teasing out this exploration using React DevTools is another.

There are lots of ways we can automate testing React applications too. Some great tools are emerging for unit and integration testing specific to React. We can do end-to-end automation too (e.g. with Selenium), but that brings some new challenges.

End-To-End Tests With React Components

Let’s think about how automated UI checks are going to work when implementing React. When writing UI automation, it’s pretty common to hook on to an HTML element’s attributes to interact with it.

When you use a component multiple times on the same page, it’s very possible that all the attributes of the components might be the same. The very nature of how React works can cause trouble with automated tests. The tests won’t be able to find a unique match to use and the test scripts will fail, or at minimum be flaky when it can’t find the right attributes.

One way to resolve this is to add a unique identifier as a prop when a component is added to the page. For example, there is a page with a number of <Pancake /> components. To automate a click on one of these pancakes would be difficult if there was no way to uniquely identify one pancake compared to another. A solution to this is to programmatically add a unique prop to each Pancake as they are added to the stack.

The DOM could end up looking like:

<div class="pancake hot" id="pancake-2"></div>
<div class="pancake hot" id="pancake-1"></div>
<div class="pancake hot" id="pancake-0"></div>

Now it’s much easier to pick out which pancake to click on - because each pancake has a unique id.

Testability is an important conversation to have with development teams when using React. It’s important to consider how automated checks are going to work early in a project so that development teams know to build in functionality for it.

Automation Tools For React

There are other ways to automate testing when using React. React comes with a built-in set of unit testing utilities called TestUtils. React also recommends a test runner called Jest which has some interesting applications as a testing framework. One way Jest can be used is for snapshot testing. Snapshot testing is comparing a screenshot of a component to a baseline image of the component which gives developers a way to find unanticipated changes in a component’s appearance. Airbnb has released a testing utility called Enzyme, for asserting and manipulating components alongside Jest. Follow any of the links above to find out more about implementing these tools, and when you’re done, why not share your findings in The Club.

Front-End Development With React.js

React is a really powerful tool for front-end development. Its fast, modular nature, means we’re going to see it in action more and more. This has only been a brief introduction. By knowing a bit about how it works and some of its intricacies, testers can work with developers to make React apps more robust. In doing so, development teams can give the users a much faster, more consistent, and high-quality app experience.

Select References:

Other MoT Resources You May Be Interested In:

Author Bio:

James is a software tester, currently helping to build world class giving and engagement solutions at Pushpay. He's a big believer in making life better for everyone - by building helpful, easy-to-use software; by helping other testers get better at their craft; or by taking his friends to the pub. When he's not doing the above, you can find him drawing pictures, running around in the forest, or playing video games with his wife and son. You can find him as @jamesespie on Twitter or read his daily comics at @daily.pie on Instagram. He also hosts the SuperTestingBros podcast with his buddy Dan, which is definitely something you should listen to. Find them on Twitter as @SuperTestingBro.