Codementor Events

Writing Easily Testable Code with Stateless Components in React.js

Published Sep 10, 2017Last updated Mar 09, 2018
Writing Easily Testable Code with Stateless Components in React.js

Understanding Outcomes with Pure Components

The use of Stateless Functional Components (a.k.a. “pure” or “dumb” components) in React.js continues to grow since first it was released in React 0.14, and for good reasons. By relying on pure components, developers end up writing reusable code that's extremely easy to test, which leads to better application architecture. Why? A functional stateless component is merely a factory function used to create a React component. Personally, I like the term “pure component” because it's indicative of what the code really is — a pure function sprinkled with some JSX.

"A pure function is a function which:
Given the same input, will always return the same output.
Produces no side effects.
Relies on no external mutable state."

— Eric Elliott, Master the JavaScript interview: What is a Pure Function

Equipped with the right number of tests, the use of pure functions can help bring peace of mind to developers by ensuring that any scenario that has been tested will not have an unintended result in the application. As you can imagine, this can go a long way when building large scale applications. It should also come as no surprise that pure functions are the heart of Redux, which relies on JavaScript’s reduce function.

In an upcoming post, I’ll go into further detail on how reduce works. For now, I want to continue on with the idea of writing “bullet-proof” React components using pure functions. To best illustrate this, I’m going to show some code that shows a React child component.

// Post.js
const Post = ({ postStyles, content }) =>
<div className={postStyles}>
<p>{content}</p>
</div>;

The clean nature of these components make it extremely easy to reason about the code. In this case, we see that the Post is just a function that takes postStyles and content as props and then uses them to produce an element. You may see props being passed in as a parameter but I’ve utilized ES6 destructuring. By doing so, we’re able to pull specific values out of the props object and access props.postStyles and props.content without using dot notation.

Personally, I think this is awesome because it removes any mystery about the props being passed into a component. In programming, mystery is never a good thing, so this allows us to read code faster than others, and allows us to reason with them better, especially as components become more complex!

Why this is important

Although this component may not look like much, it could very well play an integral part of an application. For instance, let's imagine <Post/> rendering articles for a website that has over a million daily users. If that's the case, then we will most likely want these smaller, cohesive blocks of code, as they allow us to easily hammer out every last detail!

You’ll also find that pure components make modifications a breeze and also result in writing code that can be easily ported into other projects. In addition to these benefits, pure components also make it extremely easy to test code with any crazy props that you wish. Like a pure function, pure components will always map the same input to the same output, therefore, if we can think of the scenario, we can test how our component will react.

Diving into Unit Tests

As we just saw, the code for functional components provides us awesome intel for everything we need to know in order to write effective tests. To best illustrate this, I’m going to take you through the code block below, which is a unit test of the <Post /> component.

// Post.test.js
import test from 'tape';
import dom from 'cheerio';
import Post from './Post';
import { renderToStaticMarkup } from 'react-dom';

test('<Post /> ', assert => {
const msg = 'should render a post with a title and content';
// define pre-exisiting conditions
const contentText = 'I am the content of the blog post. Lorem ipsum I need sum now!';
const props = {
  postStyles: 'post',
  content: contentText
};
const checkProps = new RegExp(contentText, 'g');

const el = <Post {...props} />
const $ = dom.load(render(el));
const output = $('.post').html();

const actual = checkProps.test(output);
const expected = true;

assert.same(actual, expected, same);
assert.end()
})

Don’t worry if the code above seems a bit confusing to you — it will all make sense soon.

Step 1 - Set up pre-existing conditions

In this instance, we know that <Post /> takes a title and a content prop so we can test this by simply passing in these values to a props variable. Once we’ve assigned the props we want to test, we can use JavaScript's Regex function to create another variable, checkProps.

Originally, the article was incorrectly using the RegExp function which was resulting in the tests giving false postivies but it has been updated since!

// post.test.js
const contentText = 'I am the content of the blog post. Lorem ipsum I need sum now!';
const props = {
  postStyles: 'post',
  content: contentText
};
const checkProps = new RegExp(contentText, 'g');

Our unit tests should test one piece of the component per each test assertion, which in this case is the content prop. Specifically, we see checkProps being created to search through a string to ensure that it contains the string assigned within contentText.

Step 2 - Render the HTML using Cheerio

Following this, we then see the use of cheerio to render our component. Cheerio allows us to traverse and manipulate the DOM using its API. Additionally, Cheerio is lightning fast and as a result of that, it’s at the heart of popular libraries like enzyme.

Taking a look into our code, we see cheerio's .load method to render our React element. From there, we can simply pass in the selector of the contents we want to check. In our case, it's the string post therefore we use the .class selector being used to grab this portofion of the code. Lastly, we then see cheerio’s .html() method, which will render the content from our selector and allow us to check for our component’s props.

// Set up cheerio
const el = <Post {...props} />
const $ = dom.load(render(el));
const output = $(‘.post').html();

Step 3 - Test the props

The last thing we need to do is check the props of our output variable against our check props method. To do this, we create an actual value by testing output against an expected value, which in this case is true.

const actual = checkProps.test(output);
const expected = true;
// asserts that actual and expected are equal
assert.same(actual, expected, msg);
// ends assertion test
assert.end();

If it does, that means the actual value will return true and give us a passing test!

Step 4 - Celebrate (Final Words)

Awesome! You’ve made it to the end. Hopefully by now you’ve got a good understanding of how functional stateless components work in React. Personally, I can’t get enough of them and I hardly ever try to use classes in my code. Why use this if you don’t need to? 😃

If you have any questions and comments, leave a comment below!

Discover and read more posts from Thomas Greco
get started
post comments2Replies
Iris Schaffer
7 years ago

Not sure what RegExp(props) does, since you pass in an object and I can’t find any documentation about creating a regex from an object, but this almost definitely doesn’t work the way you would like it to work. Tested it real quick in my chrome console, with the same “pattern” that you provide, these are some of the things that return true:

checkProps.test('<div class="post"><h1>I am the title</h1><p>I am the content!!!!!! Woooooooo!</p></div>') // true
checkProps.test('o') // true
checkProps.test(false) // true
checkProps.test(undefined) // true

Is there something I am missing here?

Iris Schaffer
7 years ago

Thanks for getting back to me – unfortunately still not entirely sure I understand…
The problem I see isn’t the output, that is of course a string, but the way you check if it worked. As per my examples above, even checkProps(undefined) would return true, which means the test condition is well… flawed :)