Can you test-drive the UI of a web application? And why would you even want to?
With React and some extra tools, testing at least some aspects of the UI is easy. Let’s take a look…
Intro (TL;DR)
Because I teach TDD trainings and also React trainings, I sometimes write code to practice what I teach. This time, I implemented a simple timer app to practice doing TDD with some newer React features.
You are watching the first video of a series where I want to show you some aspects of doing TDD in React applications - Things I have learned and also things I already knew that you might find interesting.
Do you want to practice writing React-Apps in a test-driven way yourself now? Then have a look at our training pages at devteams.at/servies/training. Have any questions? Send them to hello@devteams.at.
Question / Problem
When you write a web-application using test-driven development from scratch, you will start with almost nothing being there. In my case, I had a React application that was created by the create-react-app
command line tool. So, there was some infrastructure, but nothing else.
How do you get from nothing to some components being displayed? That’s the simplest thing I could do now. Adding some components that still do nothing - To iterate on the structure of the UI itself.
But: How do you test-drive the UI itself?
Solution
Let’s take a step back and look at how React works. The developer writes some components. Those components can have an internal state and they can get data from the outside via their “props”.
React will “render” those components to the screen. It has one-way data binding: Whenever the state or props of a component change, React will update what is shown on a screen. You could imagine it as a pure function that gets components with their internal state and creates what our user sees on their screen.
OK this is an extremely over-simplified explaination, but it will do for the purpose of this video.
How a component is displayed only depends on the state and props of this component. And whenever a component changes, React will update what our user sees on their screen. So, when we test how our components work, we test the UI.
This works as long as the programmer does nothing fancy and sticks to some rules. But programmers doing something fancy is a completely different problem and a warning sign most of the time.
To test my React components, I use “enzyme” - a library that allows me to test the components without actually rendering them. I also use “mocha” as my test runner and the assertion library “chai”. I use “sinon” as my mocking / stubbing library. I tried using “jest” instead of the last few, but it was much slower, which is a no-go for TDD.
Enzyme gives me two functions to create React components in a test: mount
and shallow
. shallow
provides some isolation and does not render the contents of the children components. mount
simulates a complete component lifecycle.
One of the easiest assertions I can do with enzyme is test whether one component renders another. This is also often a good test to get started with the UI when I have nothing else. One component renders another. There is no business logic yet, no view logic, just the structure of the components. But I just got one tiny step closer to building the application.
it('renders a timer slice', () => {
const timer = shallow(<TestTimer/>);
expect(timer.find(Slice)).to.have.length(1);
});
I can also test whether a component passes the correct props to another component or some HTML element. By doing that, I can test not only the structure of the UI, but also indirctly test the view logic: Did the component gather the correct data, maybe convert it and pass it on to the right places?
it('passes percentLeft to slice when timer is not running', () => {
const timeToPercentage = () => 17.34;
const TimerComp = createTimer(timeToPercentage);
const timer = shallow(<TimerComp/>);
expect(timer.find(Slice).prop('percentLeft')).to.equal(17.34);
});
In this test, I stub the function timeToPercentage
and create a timer that uses my stubbed function. Then I can check whether the timer uses the value from this stubbed function to render the Slice
.
I can even simulate DOM events and test whether the component behaves correctly by calling the simulate
function on an element:
it('calls startTimer when start was pressed', () => {
const startTimer = sinon.stub();
const config = shallow(<Config startTimer={startTimer} />);
config.find('.start-timer').simulate('click');
sinon.assert.called(startTimer);
});
Conclusion
With React and some extra libraries to use in your tests, testing the UI itself is not that hard. At least the UI logic - Which components and HTML elements are shown and what data is passed around. With the technique I just showed you it is not possible to test how the UI really looks in a browser.
But this is not my main concern while I am doing TDD.
I must still decide what to test and what to leave untested, and this decision has some consequences. But I want to talk more about that in the next video of this series.
CTA
Do you want to work on an example like that, together with me, to learn TDD with React? Then check out our training offers at devteams.at/services/training.
And follow me on Twitter - I am @dtanzer there - so you’ll not miss the next video.
Read / watch all parts of “React TDD” here: