A guide to TDD a React/Redux TodoList App — Part 1

Written by sanjsanj | Published 2017/06/24
Tech Story Tags: react | redux | webdriverio | tdd | jest

TLDRvia the TL;DR App

Part 1 — You’re here now.

Part 2 — Link.

Part 3 — Link.

Part 4 — Link.

This tutorial assumes some knowledge of JavaScript, the front end, test-driven-devlopment and the node package manager, with little-to-no experience of React/Redux. It’s my opinion on how it should be done at this very basic level while you’re still learning, and it should at the very least open avenues for you to pursue your own best practice.

The less you know about React the more I recommend you branch off and read supporting links.

High production value

Introduction

The above beauty is what we’re going to be making in this tutorial — a React Todo list app, using Redux as our state management system, TDD’d with end-to-end tests using webdriverIO and unit tests using enzyme.

You can view the finished app online here on heroku. And the final git repository here.

We’re going to be using the create-react-app node package from Facebook to scaffold the essentials of our app. If you’re completely new to React then I’d recommend setting up an app from scratch a couple of times to get familiar with all the packages and configs, but for the purposes of this tutorial we’re going to skip ahead and let create-react-app take care of that for us.

Even for a production app, my feeling at the moment is that it’s better to use create-react-app and then make the few changes you may want, it’s far more efficient.

You can have a look at what create-react-app actually does behind the scenes in this repo, all I’ve done is scaffolded an app and then run _npm eject_ to expose all the packages, configs and scripts. Files of note will be the _package.json_ and contents of the _config/_ and _scripts/_ folder.

Creating the app

Make sure you have the latest node and npm installed.

Then install the create-react-app package globally:

npm install -g create-react-app

In the command line let’s go to the parent directory of where we want to create our app then run the following:

create-react-app my-react-todolist

This will create a react app in the my-react-todolist/ folder relative to wherever we are.

Then let’s change in to that directory and install the node packages we need:

cd my-react-todolistnpm install

If you have a look in the package.json you will notice that the only production dependencies are the react library and react-dom to render it, and the only dev dependency is something called react-scripts. Herein lies part of the magic, it’s simply a package that has all the most essential packages as its dependents and provides scripts to run common tasks. You can view more on the npm page here.

Let’s run the tests as a sanity check to make sure everything has gone according to plan.

npm run test

You should have 1 passing test.

Let’s run our app and see what it does.

npm run start should start the server and open a browser window at http://localhost:3000/

We’ve done it! We’re React devs. Time to get paid.

At this point I would probably make my first commit and push up to git, but that’s your call.

Eslint

This bit is optional, and if it’s your first time using a real-time linter then you’re going to find it to be a nuisance to begin with, but it’s an essential tool and you may as well get used to it if you aren’t already.

Linting is very useful for a number of reasons such as; enforcing style rules and best practice, removing unneeded code and decreasing cognitive load.

My current favourite is eslint with the airbnb rules, it’s not very hipster but it is the most popular and probably the most relevant choice at the moment.

npm install --save-dev eslintnode_modules/.bin/eslint —-init

These are the options I used and would recommend you use:

We’re going to add an exception to the rules at this point, we’re going to tell eslint not to complain when we write JSX in a .js file. The overly-simplified definition of JSX (JavaScript with XML) is that it allows us to write far more concise and easily readable React code, including standard html, in our JavaScript, read more.

"rules": {"react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }]},

Your eslint might look slightly different depending on the version but this is what my .eslintrc.js config looks like:

Refactor

Let’s do a little spring cleaning with our app before we proceed.

We can make good use of our new found eslint-powers. Open up the src/App.js and we should see eslint complain about the App component. It wants us to write it as a stateless pure function as opposed to the stateful React component it is now.

// A stateful React component

class App extends Component {render() {return (<div>My App</div>);}}

export default App

A full-on React component gives you access to all the lifecycle methods and the ability to store and mutate the state but it’s not really the nicest way to write our code if we can avoid it.

Pure functions have the bonus of being really nice to test, they always have predictable returns, are more performant and are written in an immutable functional style.

// A stateless pure function

const App = () => (<div>My App</div>)

export default App

Pure functional patterns were added in React 0.14 in 2015 and I encourage you to embrace them, they are a lot of fun to use. But… you should also know how to write stateful React components for when you need lifecycle methods or to mutate state in the component itself as opposed to in the state management system (Redux in our case).

Read about stateless pure components here and read more about the differences and why you should use them here.

This is how I’ve refactored my App.js, removing all the boilerplate in preparation for creating our Todo list:

Let’s refactor our one and only unit test to use enzyme, a utility that makes it easier for us to test. We’ll also need to install the react-test-renderer dependency.

npm install --save-dev enzyme 

This is what my src/App.test.js looks like now:

Line 2 We import the shallow module from the enzyme testing library. This will make it easier to test in isolation. More info on rendering methods, selectors and assertions can be found in the enzyme docs.

If you’ve not experienced ES6 _import_ syntax before then MDN has some good documentation you should skim. It’s a nicer way to _require_ files and sub-modules but it does require transpiling.

Line 6 — 7 Then refactor our tests to shallow mount the component and assert that it exists.

Eslint might complain that it and expect are not defined. To quash those errors we’ll add an exclusion at the top of the test file.

/* global it, expect */

Make sure your test is still passing and that your App still loads in the browser, just with very little in it.

End-to-end testing

Before we get carried away and start coding let’s write an end-to-end (e2e) test that actually tests that our app performs as we want in the browser. You may call it a feature test, or an acceptance test, or UI test.

Our e2e testing solution is going to involve [selenium](https://www.npmjs.com/package/selenium-standalone) letting us fire up a browser, [webdriverIO](http://webdriver.io/) controlling it and [chai](http://chaijs.com/) granting us assertions.

npm install --save-dev selenium-standalone webdriverio chai

And perform the setup for webdriverIO:

node_modules/.bin/wdio config

These are the settings I used and would recommend:

Make sure you have Chrome installed and then edit the wdio.conf.js to select it as our default browser. Search for:

browserName: ‘firefox’,

And replace it with:

browserName: ‘chrome’,

Before we can start our selenium server we have to perform a one-time setup. We could do this manually but if we want to make this repo easily useable by anyone then we should really set up a task. Later we will document how we setup, start and test our project in our readme.

In the package.json let’s create a few scripts to handle our e2e testing needs:

"scripts": {"selenium-setup": "selenium-standalone install","selenium-start": "selenium-standalone start","e2e-tests": "wdio wdio.conf.js","e2e-tests-watch": "wdio wdio.conf.js --watch",...}

Here’s what my package.json looks like now:

And then setup and start the selenium server:

npm run selenium-setupnpm run selenium-start

Super! Now we can actually write our first e2e test. I manually entered the e2etests/ folder in my wdio config so I’m going to write it in there. If you went with the defaults then your tests should go in the corresponding folder.

To get more acquainted with how to use webdriverIO head over to its API docs.

Now if we run npm run e2e-tests we should get a failing feature test because we haven’t changed the title of the page yet:

We can run _npm run e2e-tests-watch_ if we want to run our e2e tests in ‘watch’ mode, this just means webdriverIO will watch for changes in the files we’ve told it to track via the _wdio.conf.js_ and then rerun tests upon a save. This is more convenient but may sometimes give us unwarranted failures, just beware of that while developing.

Head over to the public/index.html and change the:

<title>React App</title>

to:

<title>Todo List</title>

And behold the green glory of a passing test.

Recap

We’ve learned how to:

  • Set up our development environment.
  • Create our React app.
  • Use Eslint.
  • Refactor to ES6 syntax.
  • Set up e2e testing.
  • Get our first e2e test to pass.

Up next

Now that we’ve set up most of our app and gotten a bit more comfortable with our workflow let’s move on to writing unit tests and adding our first todo.

Part 1 — You’re here now.

Part 2 — Link.

Part 3 — Link.

Part 4 — Link.


Published by HackerNoon on 2017/06/24