Introducing the Nielsen Testing Style Guide

Liad Shiran
5 min readFeb 11, 2020

--

In the world of web development, testing our application is very important. Yet, there is a need for clear rules and standards for testing each part of our application correctly.

👉 Intro

Here in the Nielsen Tel-Aviv engineering team, we have multiple micro FrontEnd web applications. We utilize many tools (e.g., React, Jest, Cypress, Enzyme, React-Testing-Library, etc…) but we have always felt like writing frontend Javascript tests is just not standardized enough.

A few of us grouped and decided that we need a standard that defines how our tests should be written and organized.

We started to compare different testing patterns and best practices from the various development teams. We also searched the web for best practices. Eventually, we came up with the idea to write an open-source testing style guide! 💡

GLaDOS focused on testing before it was cool

📜 The Style Guide

The Nielsen Testing Style Guide is the result of numerous sessions where we discussed many testing examples and ideas. We then put the polished conclusions of these discussions together in a document, with a matching explanation for each part and some code examples (DOs ✅ and DON’Ts ❌).

We focused on FrontEnd testing, specifically using Jest and React-Testing-Library to test React/Redux applications but tried to come up with general guidelines as well.

In the style guide, we suggest using the AAA testing pattern (Arrange Act Assert), which we used in all of the code examples. We demonstrated our approaches for testing things like UI-Components, Asynchronous side-effects, react-hooks, redux, dates, and more.
Furthermore, we also addressed End-to-end tests and how we organize them into separate decoupled parts.

Check the style guide out on Github:
https://github.com/nielsen-oss/docs/tree/master/javascript/testing

🔍 Dive Into Details

In this section, I will explore some of the main points you can take from the style guide.

AAA — Arrange, Act, Assert

We recommend structuring a test in a way that has visual separation between each part of the test:
1. Arrange- Preparations, mocks, rendering of components, etc.…
2. Act- Execution of the action under test.
3. Assert- Ensuring the expectation is satisfied. This part contains one test concept (while it may include more than one assertion).

Implementation details testing vs. Behavioral testing

While doing our research, we bumped into the concepts of implementation details testing and behavioral testing. These are two opposite approaches for UI-components unit tests.

Simply put, implementation details testing means making assertions about the inner implementation mechanisms of the component.
Common examples of this can be assertions about the inner component state or its internal functions. Also, calling internal functions during the test to imitate a particular behavior of the component, is considered implementation details testing (for example, calling the onClick handler function from the test).

On the other hand, behavioral testing means making assertions only about the result of an action, while imitating how the user interacts with the component.

After clarifying these two concepts, we all agreed that moving forward, behavioral testing is the correct way to go. That’s why we heavily recommend this testing approach in the style guide. We also recommend using react-testing-library rather than enzyme.
The main reason is that react-testing-library enforces writing behavioral tests, while enzyme allows wrong implementation details tests.

Tweet by Kent C. Dodds, the author of react-testing-library

Let’s take a look at some code examples:

❌ Implementation details test example (using enzyme):

test(‘shows the correct amount of clicks’, () => {
const wrapper = shallow(<Counter />)
wrapper.find(‘button’).simulate(‘click’)expect(wrapper.instance().state.count).to.equal(1)
})

The above example is a bad test that relies on the implementation of the component. The button element may change to a div in the future. Also, accessing wrapper.instance().state is bad because it relies on the component being a react class component and that its state property has an attribute called “count”. These are all subjects to changes by developers that will unnecessarily break the test.

✅ Behavioral test example (using react-testing-library):

test('shows the correct amount of clicks', () => {
const { getByText, getByRole } = render(<Counter />)
fireEvent.click(getByText('increment'))expect(getByRole('counter-value').textContent).toBe('1')
})

The second example shows a good behavioral test. Clicking on an element that contains the text ‘increment’ and expecting the value of an element with the role ‘counter-value’ to be ‘1’, is exactly how the user interacts with the component. This test will not break after a change in the internal component implementation. It only tests the behavior of the component.

End-to-end testing

For End-to-end testing, we make use of the Page Object pattern, in which the test uses a designated object that represents a page of the application.

The page object and exposes methods to access and manipulate parts of the page. It encapsulates technical details of the page (like selectors).

We also introduce the concept of flows. A flow is a common reusable logic that you can use in multiple e2e tests. Flows may use one or more page objects to interact with the application. A good and frequent example of a flow can be login-flow.

Page objects and flows are utilities that help us write better and organized tests. It’s important to notice that both page objects and flows don’t make assertions. There should be a clear and definitive responsibility to each of them.

The diagram above shows the relationship between each part of our E2E tests. In the actual style guide, we include some code snippets for each of the concepts.

🎯 Summary

In case you missed the link before, here it is again:
https://github.com/nielsen-oss/docs/tree/master/javascript/testing

The style guide includes more exciting topics I didn’t write about in this post and goes even more into details and code examples.

We hope you find our style guide useful for your purposes!
Please feel free to use the style guide and even contribute to it (check the open issues).

We don’t expect everyone to agree with our approaches completely, so we encourage you to open issues, start discussions, or comment on one of the existing discussions.

Nevertheless, we believe that caring about the standards of our tests is a significant step in the process of writing better tests 💪

Happy testing!

--

--

Liad Shiran

Frontend Software Engineer, writer, open-source contributor