Some Thoughts On Testing React/Redux Applications

On Component and Reducer Tests, Asynchronous Actions and Generative Testing

TL;DR:

This is how I currently approach testing React/Redux Applications.
  • Minimal component tests: verify that the component actually renders.
  • Avoid testing implementation details.
  • Verify important callbacks and other props, but keep it minimal.
  • The need to test logic inside a component might signal the need for refactoring.
  • Using Eslint, PropTypes and Flow will add value.
  • Test reducers as they are functions. Same data in. Same data out.
  • Action Creators: only indirectly, when testing reducers.
  • Async Action Creators: yes fully tested.
  • React-Redux: focus on selectors when complex logic is involved.
  • Connected components: might be useful as a type of integration test, not a prime focus.
  • Fully test any services, common and utility functions. Should all be functions. Making testing simple as same data in, same data out.
  • Advanced Testing: Use generative or property-based testing when applicable, catch edge-cases. Reducers are predestined for property-based testing.
  • E2E Tests for the critical parts.

Introduction

The following is not intended as a best practice or a collection of “best-of approaches” for testing a React and/or Redux application. Rather a personal guide and reflection on how to approach testing when building a React App. None the less, it might also prove to be valuable for others. If you’re trying to figure out how to get off the ground with testing a React/Redux App this might be a good starting point.


Tooling

Currently there are multiple tooling options for writing tests in JavaScript with Karma/Jasmine and Mocha/Chai being the more mainstream combinations. Furthermore there is a wide range of alternatives like Tape and other frameworks and assertion libraries as well as testing utilities to choose from.

Regarding components, Enzyme is what appears to be the de-facto standard for testing real-world applications. The other option is Jest, that comes with snapshot-testing and improved auto-mock handling. Finally you can also use the low-level React Test Utilities. There is no general rule or recommendation regarding the one or the other. This is up to the developer or team to decide what suits best for a specific project.

Create-React-App now comes with a Jest test setup out of the box. This might be a good entry point if you still need to figure out how to get things off the ground.

In case you need a quick-start for a Mocha/Chai/Sinon/Enzyme setup, then checkout this gist.

For an example on how to setup your tests with Karma/Jasmine/Enzyme read this.

The following component test examples have been written using Enzyme.


Components

Minimal vs. Full Component Tests

A very minimal approach would be to only test if the component did render. Considering how inconsistent components can be, this makes total sense. Changing the class name will break the test, if we start verifying class names for example. Taking into account how quickly component internals change, it’s clear that taking this route doesn’t make much sense*.

(*Jest seems to tackle the problem with snapshot-testing if I’m not mistaken. If anyone can give insights on this, please leave a comment.)

This is how the most minimal but very effective component test would look like a.k.a ”The only React.js component test you’ll ever need”

The only React.js component test you’ll ever need (Enzyme + Chai) https://gist.github.com/thevangelist/e2002bc6b9834def92d46e4d92f15874

Additionally we might also verify important callbacks being triggered via sinon or similar libraries.

it('toggles the completed status of an item when clicking', () => {
const app = mount(<Root />);
const item = app.find('#item-1');
item.simulate('click');
expect(app.find('.completed').length).toBe(1);
item.simulate('click');
expect(app.find('.completed').length).toBe(0);
});

Also testing a certain number of items being rendered when passing in certain props is a valid approach.

it('renders items', () => {        
const props = {items: { id: 1', title: 'foo'}}
const list = shallow(<Release {...props} />);
expect(list.find('.item').length).toBe(1);
});

What we definitely want to do, is avoid testing React itself. A test like the following adds no value, it just confirms that React knows how to handle jsx.

const Header = ({title}) => <h1>{title}</h1>
it('filters out any out of print items', () => {
const headerTitle = shallow(<Header title="foo" />);
expect(headerTitle.find('h1').length).toBe(1);
});

Neglect testing any implementation details like tags, elements or attributes for the sake of testing if they’re being rendered. These details tend to change very quickly and the tests add no real value.

Bonus: If you’re still wondering about how to test any logic inside the component, the best answer is to not have to test any logic in the first place. Components should simply render a given input, logic should remain outside the component whenever possible.

Summary:

  • Minimal component test to verify that the component actually renders.
  • Avoid verifying tags or class names.
  • Verify important callbacks or props, but keep it minimal.
  • The need to test logic inside a component might signal the need for refactoring.

Additional: Using Eslint, PropTypes and Flow will add more value than simply trying to verify any internal details.

For more in-depth on the topic also read Testing in React: Getting Off The Ground.


Testing Redux

Reducers

Testing reducers should be straight forward. Same data in, same data out. Another thing to consider is to use action creators when creating the action for the reducer under test. By taking this approach we don’t need to explicitly test any action creators, as this might involve some overhead without real benefits.

it('should handle ADD_TODO', () => {
expect(
todos([], addTodo('Test AddTodo') // use the action creator
).toEqual([
{
text: 'Test AddTodo',
completed: false,
id: 0
}
])
})

Bonus: Using generative tests to verify reducers. Read this for or a more detailed writeup.

Action Creator

See reducers. No explicit action creator tests. See the following tests, we’re rewriting the action creator to be able to test it.

it('addTodo should create ADD_TODO action', () => {
expect(addTodo('Test addTodo')).toEqual({
type: types.ADD_TODO,
text: 'Test addTodo'
})
})

Even a better example.

const completeAll = () => ({ type: types.COMPLETE_ALL })
expect(completeAll()).toEqual({
type: types.COMPLETE_ALL
})

By creating the actions via action creators when testing reducers, we’re already verifying that the action creators work as expected.

Async Action Creators with Redux-Thunk

Testing includes mocking the store or mocking a specific call. There are a couple of possible approaches, but best is to consult the redux documentation.

The following example is taken straight from redux “Writing Tests” section.

it('creates FETCH_TODOS_SUCCESS when fetching todos has been done', () => {
nock('http://example.com/')
.get('/todos')
.reply(200, { body: { todos: ['do something'] }})

const expectedActions = [
{ type: types.FETCH_TODOS_REQUEST },
{ type: types.FETCH_TODOS_SUCCESS,
body: { todos: ['do something'] }
}]
const store = mockStore({ todos: [] })

return store.dispatch(actions.fetchTodos())
.then(() => { // return of async actions
expect(store.getActions()).toEqual(expectedActions)
})
})

Yes, asynchronous action creators based on redux-thunk for example should be tested when possible.

Async Action Creators with Redux-Saga

Testing asynchronous actions with redux-saga is as simple as calling next on the generated object. Take a look at the shopping-cart test example:

test('getProducts Saga test', function (t) {
const generator = getAllProducts(getState)
let next = generator.next(actions.getAllProducts())

t.deepEqual(next.value, call(api.getProducts),
"must yield api.getProducts"
)

next = generator.next(products)

t.deepEqual(next.value, put(actions.receiveProducts(products)),
"must yield actions.receiveProducts(products)"
)
t.end()
})

We don’t have to deal with mocking api calls and other asynchronous actions, as we only verify if the returned action is the expected one. This also enables us to verify that the actions are returned in the correct order.

Testing React-Redux

No testing of connected components nor verifying that mapDispatchToProps returns expected results at the moment. Regarding the first, I would like to hear some feedback. It could also be seen as a type of integration test, but I would like to hear about the real benefits, besides testing if react-redux is passing the updated state to a given component under test. Regarding testing mapDispatchToProps, under regular circumstances, we’re only composing action creators with dispatch.

In regards to the connect method, mapStateToProps might be interesting to test, especially when defined as a selector. This depends on the fact if logic is involved when selecting the state and might make a lot of sense in given situations.

Like mentioned above, I would like to hear feedback on how you approach testing the react-redux specific parts.


Services and Utility Functions

Services and utility functions used across a project are usually fully tested, as they generally should always be functions. Same data in, same data out. Writing tests using any assertion library should be straight forward to accomplish with this functions only approach.


Advanced: Property-based Testing

Writing generative or property-based tests has a number of advantages, including finding edge cases that might go unforeseen when only focusing on example based tests. Reducers are a good starting point for writing property-based tests.

If you’re interested in understanding how to approach generative testing with JavaScript in general and redux in specific you might want to read this and this.


E2E Tests

This needs a post of its own, but try to test the most critical parts of the application like login, registration etc.


Outro

This should have been a quasi high level summary of one possible approach to test a React/Redux application. I am really interested in feedback on how you approach testing and where I might be mistaken.

In case you have any questions or feedback leave a comment here or leave feedback via twitter.

Next Story — The Elegance Of React
Currently Reading - The Elegance Of React

The Elegance Of React

Writing Elegant Code With React, Redux and Ramda

This writeup should be a lightweight introduction into writing elegant code with React. We will be combing React with Ramda to write our application in a functional style. All ideas and examples will also work with lodash/fp and other functional util libraries. The choice is yours here.

The examples have been written using eslint-config-cleanjs, to enforce a more functional style, which includes no-this and no-classes rules. They should encourage us to be more strict when we start implementing the following examples. If you’re interested in the configuration take a look at the github repository to see the exact rule sets.

Compose Components

Let’s begin with what might be the most obvious of all approaches. Think about the following:

const comp = (f, g) => x => f(g(x))

In component terms we could implement it like so f.e.:

const TodoList = (List, mapItems) => s => List(mapItems(s))

Now this makes sense, enabling us to to build larger components by composing the smaller ones.

const List = c => <ul>{c}</ul>
const Item = todo => <li key={todo.id}>{todo.name}</li>
const TodoList = compose(List, map(Item))
const result = TodoList([{id: 1, name: 'foo'}])

TodoList is a function that expects an application state and returns a new component according to this state. In itself this is very clean and sensible. We are mapping over a number of todo items and creating list items along the way. The result is then passed onto List via props and rendered inside the component.

Remember:

const App = state => List(map(Item, state)))

This first idea will work nicely for components that render other components, only that most components we write don’t depend on jsx but on a state being passed in. So this will only work for a small subset of components.

Here is a full working example using Ramda map, compose and prop.

import React from 'react'
import
{ render } from 'react-dom'
import
{ compose, map, prop } from 'ramda'
const List = items => <ul>{items}</ul>
const Item = todo => <li key={todo.id}>{todo.text}</li>
const getTodos = prop('todos')
const TodoList = compose(List, map(Item), getTodos)
const props = {todos: [{id: 1, text: 'foo'}, {id: 2, text: 'bar'}]}
render(<TodoList {...props} />, document.getElementById('root'))

Limitations of Compose and Components

We’ve been able to compose components and render a Todo list. Next, let’s see a more common approach, where props are being passed top down. These props can be anything, from callbacks, other components to arrays and objects. Here’s the same Todo List but with an additional Header component.

const Header = title => <h1>A Todo List: {title}</h1>
const List = items => <ul>{items}</ul>
const Item = todo => <li key={todo.id}>{todo.text}</li>

Nothing special here, but taking a look back at our previous implementation clearly shows that we can’t just use compose to create a Todo list containing a header and a list of Todo items. Our previous compose:

const TodoList = compose(List, map(Item), getTodos)

What we’re actually looking for is the ability to compose Header and List. Where would Header fit in? We’re passing application state to the Todo list function, which first of all filters all the todos and then iterates over the filtered todos to create an array of Items that are then passed on to List. How can Header then retrieve title information from state? We need a better approach.

Let’s be more explicit:

const TodoHeader = mapStateToProps => Header(mapStateProps)

Edit: This is a better example (Thanks Thai Pangsakulyanont)

const TodoHeader = todoState =>
Header(getTitleFromTodoState(todoState))

We want the ability to pass in application state and enable all components to retrieve any needed properties. To make it even more obvious I have called the function mapStateToProps.

const mapStateToProps = curry((f, g) => compose(g, f))

Our mapStateProps expects a function as well as a component and then applies state to the provided function first and then passes the result on to the component. Also note worthy is the fact, that we’re currying our function, just in case we want to separate the filter definition from the concrete component. On that note, most Ramda functions are automatically curried.

Here’s how mapStateToProps would be applied with Header.

const TodoHeader = mapStateToProps(s => s.title, Header)
const result = TodoHeader(state)

This is starting to sort of look like the connect() function in react-redux, we us mapStateToProps to transform any state specific information to props. Now Header and our previous composed List components can each retrieve state information individually.

const TodoList = mapStateToProps(getTodos, compose(List, map(Item))
const result = TodoList(state)

Obviously mapStateToProps solves one part of the problem, but we still need the ability to compose the results of TodoList with Header to create our App.

We can’t use compose, so let’s rollout our own handcrafted combine function, that expects two components and returns a new component back.

const combine = curry((c, o) => x => (<div>{c(x)} {o(x)}</div>))

Using combine enables to create a new function composing Header and List.

const TodoHeader = mapStateToProps(s => s.title, Header)
const TodoList = mapStateToProps(getTodos, compose(List, map(Item)))
const App = combine(TodoHeader, TodoList)
render(<App {...state} />, document.getElementById('root'))

We have a way of composing two components, each requiring a specific state to render, now. Let’s take it a step further and see how we can compose multiple components together.

Reducing Components

Imagine we need to add a Footer to our App just to be able to print out the current year. How would we achieve this? This is what might come to mind first:

const App = combine(TodoHeader, combine(TodoList, TodoFooter))

Combine TodoList with TodoFooter and then combine the resulting output with the TodoHeader. This will work, but will become obscure the more components we want to combine.

We could think about it like this:

// array of components 
const comps = [TodoHeader, TodoList, TodoFooter]
const App = comps =>
reduce((acc, x) => combine(acc, x), init, comps)

Now that we have an idea, let’s take a look at a concrete implementation.

const combineComponents = (...args) => {
const [first, ...rest] = args
return reduce((acc, c) => combine(acc, c), first, rest)
}

Referencing combineReducers in redux, we could call our reducer combineComponents. combineComponents takes a number of components and reduces them to a single function expecting the component state.

const App = combineComponents(TodoHeader, TodoList, TodoFooter)
render(<App {...state} />, document.getElementById('root'))

With the help of mapStateToProps, combine and combineComponents we are able to compose components now. Regarding mapStateToProps, we can do one last refinement. Let’s take a look at the original implementation.

const mapStateToProps = curry((f, g) => compose(g, f))

Actually we don’t even need to implement this. Ramda or lodash/fp offer a function called pipe. pipe simply run all functions from left to right. Take the following example:

const add = x => x + 1
const multiplyByFour = x => x * 4
// pipe === flip(compose)
const rCompose = flip(compose)
rCompose(add, multiplyByFour)(1) === compose(multiplyByFour, add)(1)
rCompose(add, multiplyByFour)(1) === pipe(add, multiplyByFour)(1)

So pipe is like compose with reversed arguments. We’re using the Ramda flip function, which will work in this given case, as it only flips the first two arguments. This means we can now refactor mapStateToProps to:

const mapStateToProps = pipe

or simply use pipe straight away, leveraging Ramda to the fullest. This leaves us with two functions: combine and combineReducers. We could even hide away combine, but we will leave it as it is for clarity reasons.

The full example code:

Adding Redux

Reduce everything? What follows is some pseudo code, that should help us with creating a mental model of what we’re setting out to achieve.

const App = (state, action) => TodoList

The above code looks a lot like your typical Redux reducers, only that we’re returning a React component in this specific case not a newly calculated state. What if we could leverage Redux to achieve this? Let’s try this out.

We’re still building yet another Todo list and to keep it obvious, we’ll use a couple of the redux todomvc actions and the todo reducer.

// constants
const ADD_TODO = 'ADD_TODO'
const DELETE_TODO
= 'DELETE_TODO'
// actions
const addTodo = text => ({type: ADD_TODO, text })
const deleteTodo = id => ({ type: DELETE_TODO, id })
// reducers
const todos = createReducer([], {
[ADD_TODO]: (state, action) => [
{ id: getNextId(state), completed: false, text: action.text },
...state
],
[DELETE_TODO]:(state, action) =>
reject(propEq('id', action.id), state),
})

Parts of the original reducer code have been refactored using Ramda’s reject and propEq to filter out the deleted todo item. reject is a complement of filter, in case you’re wondering. We can also write a couple of helper functions.

// redux utils
// alternative is to use
defaultTo instead propOr
const createReducer = (init, handlers) =>
(state = init, action) =>
propOr(identity, prop('type', action), handlers)(state, action)
const addOne = add(1)
const getAllIds = pluck('id')
const getMax = reduce(max, 0)
const getNextId = compose(addOne, getMax, getAllIds)

getNextId is a helper function for retrieving the next id, we will need it when adding new items. createReducer already comes as a top-level export with Redux, but this is an alternative rewritten with Ramda functions.

Now that we have the reducers and actions in place, we will need to adapt our components to handle adding and deleting components. Instead of implementing an input, we’ll keep it simple for now and add a button that handles adding a todo item with a fixed text.

const Add = onSave => (
<div>
<button onClick={() => onSave('foobar')}>Add</button>
</div>
)

Finally, we will also need a way to delete the items. Adding a delete button to Item should suffice for this example.

const Item = ({todo, removeTodo }) => (
<li key={todo.id}>
{todo.text} <button onClick={removeTodo}>Remove</button>
</li>
)

Everything should be in place now. There’s still something to clarify here: removeTodo should dispatch the deleteTodo action. Another aspect to consider is that we need to find a way to define which dispatchers should be provided. Currently we’re only mapping the state to props.

Let’s add getRender, that expects an entry node and returns a function expecting a React element.

const getRender = node => app => ReactDOM.render(app, node)
const render = getRenderer(document.getElementById('root'))
render(<App {...state} />)

Next off let’s also write a bindActionCreator.

// define a bindActionCreator
const bindAction = curry((dispatch, actionCreator) =>
compose(dispatch, actionCreator))
const bindActionCreator = bindAction(store.dispatch)

Let’s hide away the dispatch method, and pass the bindActionCreator to our App along with the state and subscribe to the store to trigger re-renders. Important to note that Redux already comes with a bindActionCreators helper out of the box.

const run = store.subscribe(() =>
render(
<App {...store.getState()} dispatch={bindActionCreator} />
)
)

All we need to do is some final adaptions on our Item and TodoList components. Item expects the todo item as well as an onDelete function.

const Item = ({todo, onDelete}) => (
<li key={todo.id}>
{todo.text}
<button onClick={() => onDelete(todo.id)}>Remove</button>
</li>
)

Due to the fact that Item now also requires the onDelete function we need to adapt our ‘map to props’ function. We have access to dispatch, so instead of returning the todo items array, we will return an object containing the todos as well as onDelete.

// for clearer understanding extracted mapItems
const mapItems = ({todos, onDelete}) =>
map(todo => Item({todo, onDelete}), todos)
const TodoList = pipe(props =>
({todos: props.todos, onDelete: props.dispatch(deleteTodo)}),
compose(List, mapItems)
)

Here’s the final code. Also checkout out the example.

Outro

This should have been an introduction into how to combine Ramda with React and Redux to write more elegant code.

The examples are intended to show how to use Ramda with React and/or Redux. In the real world you can leverage Ramda or lodash/fp to write more elegant code in parts of your application.

For example you might refactor your mapDispatchToProps functions to map the state to your props according to the defined propTypes instead of having to manually define them.

const getPropTypes = prop('propTypes')
const pickKeys = compose(pick, keys)
const mapStateToProps = compose(pickKeys, getPropTypes)
// map state to defined propTypes.
export default connect(mapStateToProps(App))(App)

Alternatively you could replace mapDispatchToProps with Ramda’s pick.

export default connect(pick(['todos']))(App)

If there is interest, I can do a follow-up, where we will refactor the application to integrate with Redux in a more elegant fashion.

If you have any questions or feedback don’t hesitate to leave feedback @ twitter.

This writeup has been inspired by this Brian Lonsdorf talk at React Rally.

Links

The example

Example code with redux

Example code without redux

Ramda.js

Brian Lonsdorf @ React Rally

Recompose

Next Story — Generative Testing Redux: Reducers
Currently Reading - Generative Testing Redux: Reducers

Generative Testing Redux: Reducers

Property-based Testing by Example.

Introduction

This is a followup to Generative Testing in JavaScript which was intended as a high level introduction into the topic. If you haven’t heard of generative testing or need a reminder you might find reading the introductory part to be helpful. but it is not a prerequisite for this write-up.

The following is a short write-up on how to test a redux application with property-based tests. All examples are based on testcheck but can be quickly adapted to any generative testing framework.

Reading through the official redux test documentation and running the tests inside the examples folder can be helpful for a clearer understanding of what we’re testing but are optional. Furthermore there seems to have been a discussion regarding the topic, but I wasn’t able to find any concrete examples or an implementation that would describe how to use property-based tests to run against your reducers.

Thinking about generative testing and redux

Here are a couple of ideas to play around with, before moving on to the first real attempt at writing a property-based test.

The simplest example, and the one that made redux famous, is the Elm-inspired counter example. Here’s the code to remind us what’s happening inside the counter reducer.

export default function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1
case 'DECREMENT':
return state - 1
default:
return state
}
}

The initial state of counter is 0 and we have 2 action types, that either increment or decrement the counter. How would a property look like regarding this reducer?

// first idea
const result = ({state, action, expected})
=> reducer(state, action) === expected

We’re interested in testing the state transformation, meaning comparing the result of an input state and action with an expected new state. The counter example is still comprehensible enough to try to tackle the problem.

Here are a couple of ideas to think about.

// 1.randomize the state
// describe the shape of the state
const initialState = gen.int
// 2. randomize actions
const randomizeAction = (action, gen) => action(gen)

Now that we have an initial high level idea, let’s try a naive implementation.

We need to calculate the expected output state depending on the generated state and action but we don’t want to reimplement the reducer itself. Here is a scrapped idea that came up while trying to understand how to tackle defining an expectation.

const getExpectedState = ({state, action}) => ({
action,
state,
expected: action.type === 'INCREMENT' ? state + 1 : state -1,
})

Create a getExpectedState function that should accept an object containing state and action and extending this given object with a calculated expected result for verifying the state output.

This implementation will not really add any benefit, as we’re actually reimplementing the reducer itself. If you notice we’re either incrementing or decrementing according to the passed in action type. If we had added an undefined action type to our generated actions, then we would already be copying the internal counter reducer implementation, maybe even ending up with using a switch case. So this is obviously not the way to solve this.

Some more Thinking about generative testing and redux

What do we need, to be able to verify that a reducer is always returning the correct output state?

Next try.

We removed the getExpectedState function all together. So what’s going on here? Instead of mapping over a generated object containing the randomized state and action, we defined expected functions for every possible generated action.

inc for example returns the action and a function, that expects state and returns the expected new state. This still looks like we’re reimplementing the counter reducer, but it’s more explicit, as each action has a corresponding expected function, which then returns a new result based on a passed in state. This works for the counter example, let’s see if we can get this to work with the redux todomvc example.

This approach holds true for the addTodo action in the todomvc example. Now let’s extend this example to see if we can randomize the other actions too.

There’s a lot going on here. We’ve generated a random initial state according to a given shape and created random actions and their corresponding expectation depending on that initial state. While this already works and can help to validate reducers against a large number of cases, there are still a couple problems with the current implementation.

First and foremost we’re still duplicating logic inside our expectations and secondly we can’t guarantee every action is a valid one, meaning that you might have certain async actions that can’t be dispatched when a previous action has been dispatched. Just think about async action creators and how certain actions can only be dispatched when the app has a certain state.

If say, you’re application has a state loading and certain actions might not be able to be dispatched as they depend on another action being dispatched before that, chances are high we might be dispatching incorrect actions.

Refinements

The generated tests are valid, but randomizeDeleteTodo looks a lot like the todo equivalent reducer code.

const randomizeDeleteTodo = id => [deleteTodo(id), state 
=> filter(todo => todo.id !== id, state)]

We will keep the idea of creating pairs of actions and expected functions, only that our expected function should actually be a property that verifies a certain state/action combination return the expected new state. Time to redefine those expected functions, to be able to write predicate functions like this:

(state, nextState) => state.length !== nextState.length

This approach needs to gain access to state as well as the nextState and validate that a given action triggers the correct state transition. This looks a lot clearer now, as opposed to the previous implementations.

What we really want to do is pass in a property function, generate random state and actions and verify that the updated state is valid.

check(property(generatedData, propertyFn(reducer)), options)

The propertyFn function can be as trivial as: expect a reducer and return a new function expecting state and the action/property pair, which passes the state and the new state to the property function.

const propertyFn = reducer => ({state, def: {action, property}}) 
=> property(state, reducer(state, action))

Implementing the high level wrapper might look something similair to this.

// helper function
const createAction = (action, property) => ({action, property})
const propertyFn = reducer => ({state, def: {action, property}}) 
=> property(state, reducer(state, action))
const run = (reducer, generatedData, options) 
=> check(property(generatedData, propertyFn(reducer)), options)

Here is the updated todomvc example leveraging the run function.

See the working example.

This needs more work obviously.

More refinements

We still need to deal with ensuring that the correct actions are being dispatched and we need a way to figure out how to write meaningful properties. Finally creating an api for writing your generative reducer test would be highly valuable. In the next post we will need to tackle one and two and three is only a bonus.

For the refinement part, we will focus on the async and shopping-cart examples. We will have a new situation, having to deal with actions being dispatched in the correct order or at least in a non-conflicting way. We will use those examples to write functions that validate if a certain action is applicable.

Don’t hesitate to provide feedback or ask questions in case something is unclear or if there are better alternatives to the solutions presented here. The focus should be on gaining insights on how to test the store with a random set of actions and a generated initial state, guaranteeing a correct order of actions.

Feedback or questions via twitter

Links

todoMvc Example

Generative Testing in JavaScript

Redux

Next Story — Generative Testing in JavaScript
Currently Reading - Generative Testing in JavaScript

Generative Testing in JavaScript

The Case for Property-based Testing

Introduction

Generative testing is a widespread approach in the functional world, mainly due to the popularity of Haskell’s QuickCheck and Clojure’s QuickCheck implementation test.check.

Traditional unit tests enable us to verify assumptions for a given function. This approach, also known as example based testing, is based on manually defined inputs and expected outputs. In other words a developer has to figure out a set of inputs/outputs to assert that the function under test is working as intended. This also means that one would have to write a large number of tests just so to be able to verify a wide range of cases.

So while unit tests might verify a reasonable set of assumptions, you can’t manually catch all the possible cases that need to be tested. Thinking beyond a subset of expectations becomes very inefficient at some point. There’s a limitation here. This is where generative testing comes into play.


The traditional approach

To clarify things, let’s start with an example.

const add = (a, b) => a + b

So we have our own handcrafted add function now, which means we can write some tests to verify everything works as expected. A typical test for add might look something like this (you would probably group expectations like “should work with zero” etc. but this should suffice for now):

describe('add', () => {
it('should work', () => {
assert(add(1, 1), 2)
assert(add(1, 3), 4)
assert(add(0, 0), 0)
assert(add(-1, 1), 0)
assert(add(10, -1), 9)
})
})

These couple of assertions should verify everything is working as expected with the inputs/outputs having been defined manually. Nothing new up until now, but we will build upon this example in the following section.


Property-based Testing Introduction

To highlight the difference between unit and generative tests we can state that generative tests are not defined manually and that they are verified against invariants not against values as this is the case with an example based approach.

In short: with generative testing we specify properties and the testing library generates those tests for us.

The key part is to define a property that can be verified, in other words we need to define a function that should always returns true for any given input.

Writing property functions isn’t as trivial as compared to say traditional unit tests. Take for example the previously defined add function. Writing a property function like

(a, b) => a + b === add(a, b)

is useless, as we’re reimplementing the add function that we’re actually trying to test.

A more suitable approach would to be verify that adding zero works.

const zeroProperty = x => add(x, 0) === x

Addition is also commutative, so we can also base our assumptions on this fact to validate that add actually works.

const commutativeProperty = (x, y) => add(x, y) === add(y, x)

Another good approach for writing a property function is using multiple functions to describe their inverse relationship.

const result = x => decodeSomething(encodeSomething(x)) === x

It is also valid to test a function against another existing function. For example you might test your own min function against Math.min.

Further more you might also write a property function that describes the relationship between the inputs and the outputs. Take the following assumption f.e.:

const result = add(4, 0) + add(0, 4) === add(4, 4)

Defining useful property functions is the hard part here. The rest is handled by a generative testing library that generates tests upon a given data specification.


Property-based Testing in Detail

Now that we have a high level idea about what property-based testing is all about it’s time to see how this works in a JavaScript setup.

The following examples are based on testcheck-js by Lee Byron, but there other alternatives like jsverify.

We have a handlePayment function that expects two arguments balance and payment, balance representing the account total and payment representing a sum that will either be added or deducted from the total balance.

const handlePayment = (balance, payment) =>
(!payment || balance - payment <= 0) ? balance : balance - payment

In a traditional fashion you would start to write a couple of tests that verify certain expectations. For example your test might look something like this. You might test that handlePayment handles zero as well as negative inputs besides checking for the standard case (positive balance and payment).

import { assert } from 'chai'
import
handlePayment from '../src/handlePayment'
describe('handlePayment', () => {
it('should handle zero inputs', () => {
assert.equal(handlePayment(0, 1), 0)
assert.equal(handlePayment(1, 0), 1)
})
it('should handle negative inputs', () => {
assert.equal(handlePayment(-1, 1), -1)
assert.equal(handlePayment(10, -1), 11)
})
it('should handle positive inputs', () => {
assert.equal(handlePayment(200, 1), 199)
assert.equal(handlePayment(10, 11), 10)
})
})

We run the tests and see everything is green.

What property function would we need to define for verifying our assumptions?

Let’s verify that zero payment always returns balance.

import { check, property, gen } from 'testcheck';
import
handlePayment from './handlePayment'
const result = check(property([gen.int], (x) => {
return handlePayment(x, 0) === x
}))
console.log(result)

This is the log output:

{ result: true, ‘num-tests’: 100, seed: 1471133618254 }

testcheck-js ran one hundred tests against handlePayment and all cases passed. Now let’s verify if handlePayment works correctly when working with a balance of 100 and random payment inputs.

const result = check(property([gen.intWithin(0, 100)], x => {
return handlePayment(100, x) === 100 - x;
}))
console.log(result)

The result output shows clearly that a case has failed.

{ result: false,
'failing-size': 22,
'num-tests': 23,
fail: [ 100 ],
shrunk:
{ 'total-nodes-visited': 7,
depth: 0,
result: false,
smallest: [ 100 ] } }

If we take a closer look, we’ll find a shrunk property. This is a powerful feature that most generative testing libraries encompass. Instead of returning a large dataset of cases, where maybe one or a couple of cases failed, testcheck-js will try to find the smallest failing test (see the smallest property inside shrunk) as soon as one case fails. In this specific case the smallest value that failed is 100. This gives us very specific data to find out if the problem is inside the predicate function verifying our handlePayment or in handlePayment itself or if the dataset we generated isn’t explicit enough.

The dataset set should be fine. Let’s check the handlePayment function.

Obviously the case where balance and payment might be equal, in this failing case it’s [100, 100], isn’t handled properly. We verified that balance should return balance when payment is larger than the balance but didn’t cover this specific case. Let’s fix updatePayment.

const handlePayment = (balance, payment) =>
(!payment || balance - payment < 0 // fix. only less than zero
) ? balance : balance - payment

This will the solve the problem.

Let’s verify that paymentTransaction can handle a negative balance as well as a negative payment.

const { strictNegInt : strNegInt } = gen
const result = check(property([strNegInt, strNegInt], (x, y) => {
return handlePayment(x, y) === x - y;
}), {seed: 50})
console.log(result)

If you take a closer look at our new property, we added an options object, where we defined a seed, to be able to reproduce the generated tests. (Thanks Sune Simonsen for highlighting the fact)

Running the newly added test results in a new case failing.

{ result: false,
'failing-size': 1,
'num-tests': 2,
fail: [ -2, -1 ],
shrunk:
{ 'total-nodes-visited': 1,
depth: 0,
result: false,
smallest: [ -2, -1 ] } }

A balance of-2 and a payment of -1 would have a positive outcome on the balance. We need to refactor handlePayment again.

const handlePayment = (balance, payment) =>
(!payment ||
(balance <= 0 && balance - payment < balance) ||
(balance > 0 && balance - payment < 0)) ?
balance : balance - payment

Running the test again finally results in all cases passing.

{ result: true, 'num-tests': 100, seed: 50 }

Let’s refactor the handlePayment one last time, just to make it more readable.

const handlePayment = (balance, payment) =>
payment &&
((balance <= 0 && balance - payment > balance) ||
balance - payment >= 0) ?
balance - payment : balance

The tests still verify the property.

{ result: true, ‘num-tests’: 100, seed: 50 }

The two failed cases can be added to the previously defined unit tests.

We haven’t covered how to integrate testcheck-js with mocha or jasmine. This is out of scope for this writeup but there are specific mocha and jasmine testcheck-js wrappers available, see mocha-check and jasmine-check for more information on the topic. jsverify also has integration for mocha and jasmine.


Summary

Generative or property-based testing isn’t a replacement for traditional unit testing. While the example-based approach should verify common assumptions, the property-based approach should help identify missed cases. Furthermore traditional tests are easier to write, while the generative approach requires more thinking in regards of defining properties. Using them both back to back will yield the greatest benefits. The easiest way to get started with generative testing is to try out a library, for example jsverify or testcheck-js and start writing a couple of tests to fully understand the benefits.

I have added a couple of links that might be beneficial for getting up and running with property-based testing.

Edit

Thank you Sune Simonsen for testing the examples and actually verifying that the final handlePayment test would actually fail. Check this example showing all the examples in this post by using unexpected-check.

Also read Generative Testing Redux: Reducers to see an example for property-based testing your redux reducers.

Feedback or questions via twitter


Next Story — Why The Hipsters Compose Everything
Currently Reading - Why The Hipsters Compose Everything

Why The Hipsters Compose Everything

Functional Composition In JavaScript

Introduction

Lodash and Underscore are everywhere and still there is this one super efficient method that actually only those hipsters use: compose.

We will take a deeper look into compose and along the way learn the ins and outs of why this method will make your code more readable, more maintainable and a lot more elegant.

The Basics

We will be using a lot of lodash functions, just be cause a.) we don’t want to write our own low level implementations as they would distract from what we’re trying to focus on and b.) lodash is widely used and could easily be replaced with underscore or other libraries or your own implementation if you need to.

Before we dive into some basic examples, let us recap what compose actually does and how we could implement our own compose function if needed to.

var compose = function(f, g) {
return function(x) {
return f(g(x));
};
};

This is the most basic implementation. Taking a closer look at the function above, you will notice that the passed in functions are actually applied right to left. Meaning that the result of the right function is passed on to the function left of it.

Now take a closer look at this code:

function reverseAndUpper(str) {
var reversed = reverse(str);
return upperCase(reversed);
}

A function reverseAndUpper that first reverses the given string and then upper cases it. We can rewrite the following code with the help of our basic compose function:

var reverseAndUpper = compose(upperCase, reverse);

Now we can use reverseAndUpper as follows:

reverseAndUpper('test'); // TSET

This is equivalent to writing:

function reverseAndUpper(str) {
return upperCase(reverse(str));
}

only more elegant, maintainable and reusable.

The ability to quickly compose functions and create a data pipeline can be leveraged in a number of ways enabling data transformation along the pipeline. Imagine passing in a collection, mapping over the collection and then returning a max value at the end of the pipeline or turning a given string into a boolean. Compose enables us to easily connect a number of functions together to build more complex functions.

Let us implement a more flexible compose function which can handle any number of functions as well as any number of arguments. The previous compose only works with two functions and only accepts the first passed in argument. We can rewrite the compose function as follows:

var compose = function() {
var funcs = Array.protoype.slice.call(arguments);

return funcs.reduce(function(f,g) {
return function() {
return f(g.apply(this, arguments));
};
});
};

This compose enables to write something like this:

Var doSometing = compose(upperCase, reverse, doSomethingInitial); 
doSomething('foo', 'bar');

A large number of libraries exist that implement compose for us. Our compose function should only help in understanding what’s really happening inside underscores or scoreunders compose function and obviously the concrete implementations vary between the libraries. They still do the same thing mostly, making their respective compose functions interchangeable. Now that we have an idea about what compose is all about, let us use the lodash _.compose function for the next set of examples.

Examples

Let’s get started with a very basic example:

function notEmpty(str) {
return ! _.isEmpty(str);
}

The notEmpty function simply negates the result of _.isEmpty.

We can achieve the same by using lodash’s _.compose and writing a not function:

function not(x) { return !x; }

var notEmpty = _.compose(not, _.isEmpty);

We can call notEmpty on any given argument now;

notEmpty('foo'); // true
notEmpty(''); // false
notEmpty(); // false
notEmpty(null); // false

This first example is super simple, the next one is more advanced:

findMaxForCollection returns the max id for a given collection of objects consisting of id and value properties.

function findMaxForCollection(data) {
var items = _.pluck(data, 'val');
return Math.max.apply(null, items);
}

var data = [{id: 1, val: 5}, {id: 2, val: 6}, {id: 3, val: 2}];

findMaxForCollection(data);

The above example could be rewritten with the compose function:

var findMaxForCollection = _.compose(function(xs) { 
return Math.max.apply(null, xs);
}, _.pluck);

var data = [{id: 1, val: 5}, {id: 2, val: 6}, {id: 3, val: 2}];

findMaxForCollection(data, 'val'); // 6

There is a lot we can refactor here.

_.pluck expects the collection as the first and the property name (thanks @jurstv) as the second argument. What if we could partially apply _.pluck? We can use currying to flip the arguments in this case.

function pluck(key) {
return function(collection) {
return _.pluck(collection, key);
}
}

Our findMaxForCollection still needs more refinement, we could create our own max function:

function max(xs) {
return Math.max.apply(null, xs);
}

This enables us to rewrite the compose function to something more elegant:

var findMaxForCollection = _.compose(max, pluck('val'));

findMaxForCollection(data);

By writing our own pluck function we are able to partially apply pluck with ‘val’. Now you could obviously argue, why even write your own pluck method, when lodash already has that handy _.pluck function. The problem here is that _.pluck expects the collection as the first argument, which is what we don’t want. By flipping the arguments around we were able to partially apply the key first, only needing to call the data on the returned function.

We can still further rewrite the pluck method. Lodash comes with a handy method _.curry, which enables us to write our pluck function as follows:

function plucked(key, collection) {
return _.pluck(collection, key);
}

var pluck = _.curry(plucked);

We simply want to wrap around the original pluck function just so we can flip the arguments. Now pluck simply keeps returning a function until all the arguments have been supplied. Let’s take a look at the final code:

function max(xs) {
return Math.max.apply(null, xs);
}

function plucked(key, collection) {
return _.pluck(collection, key);
}

var pluck = _.curry(plucked);

var findMaxForCollection = _.compose(max, pluck('val'));

var data = [{id: 1, val: 5}, {id: 2, val: 6}, {id: 3, val: 2}];

findMaxForCollection(data); // 6

The findMaxForCollection can easily be read from right to left, meaning for each item in the collection return the val property and then get the max for all given values.

var findMaxForCollection = _.compose(max, pluck('val'));

This leads to more maintainable, reusable and especially elegant code. We will take a look at one last example just to highlight the elegance of functional composing.

Let’s extend the data from the previous example and let’s add another property named active. The data is as follows now:

var data = [{id: 1, val: 5, active: true}, 
{id: 2, val: 6, active: false },
{id: 3, val: 2, active: true }];

We have this function called getMaxIdForActiveItems(data) that expects a collection of objects, filters all active items and returns the max id from those filtered items.

function getMaxIdForActiveItems(data) {
var filtered = _.filter(data, function(item) {
return item.active === true;
});

var items = _.pluck(filtered, 'val');
return Math.max.apply(null, items);
}

What if we could transform the above code into something more elegant? We already have the max and pluck functions in place, so all we need to do now is add the filtering:

var getMaxIdForActiveItems = _.compose(max, pluck('val'), _.filter);

getMaxIdForActiveItems(data, function(item) {
return item.active === true;
}); // 5

_.filter has the same problem that also _.pluck had, meaning we can not partially apply it as the first argument is the collection. We can flip the arguments on filter, by wrapping the native filter implementation:

function filter(fn) {
return function(arr) {
return arr.filter(fn);
};
}

Another refinement is to add an isActive function that simply expects an item and checks if the active flag is set to true.

function isActive(item) {
return item.active === true;
}

We can partially apply filter with isActive, enabling us to call getMaxIdForActiveItems only with the data.

var getMaxIdForActiveItems = 
_.compose(max, pluck('val'), filter(isActive));

Now all we need to pass in is the data:

getMaxIdForActiveItems(data); // 5

This also enables us to easily compose a function that returns the max id for any non active items:

var isNotActive = _.compose(not, isActive);

var getMaxIdForNonActiveItems = _.compose(max, pluck('val'), filter(isNotActive));

Roundup

Functional composing can be a lot of fun as seen in the previous examples plus leads to more elegant and reusable code if applied correctly.

Links

lodash

Hey Underscore, You’re Doing It Wrong!


Originally published at busypeoples.github.io.

Sign up to continue reading what matters most to you

Great stories deserve a great audience

Continue reading