This article will try to teach how to practice test-driven development while at the same time teaching how to test a reusable react component.
Requirements before starting
- You have installed node.js on your computer
- Ensure that
npx create-react-app app-name
works in your terminal since we are going to use this to create the basic setup of the app - Install enzyme
npm i -D enzyme
- Install enzyme adapter
npm i - D enzyme-adapter-react-16
since we are using version 16 of React - Create or edit src/setupTests.js to match the following code
import '@testing-library/jest-dom/extend-expect';
import { configure } from 'enzyme'
import Adapter from 'enzyme-adapter-react-16'
configure({
adapter: new Adapter()
})
Now that you have done this ensure that you can run the single test that comes with the setup when we created the app.
Run npm test in your terminal and press a to run all the tests, you will see a similar screen like the following:
Once you have done this, let’s delete the test case in file src/App.test.js to start creating our tests.
What do we want to achieve? For this exercise we want to create a Button component that we can reuse multiple times inside our app, specifically we want to be able to set a different text every time we call the Button component.
Let’s Go!
Ok, first, following the TDD principles, we need to create a failing test before creating any code for the Button component and App component.
Before creating our tests, we are going to create a describe block and inside we are going to add beforeAll() function to create appWrapper before every test run that will keep our code DRY.
let appWrapper
beforeAll(() => {
appWrapper = shallow(<App />)
})
appWrapper contains our App component.
Now go to src/App.test.js and the first test case will ensure that App component renders a Button component that will be our submit button.
it('renders a button component', () => {
const submitButton = appWrapper.find(SubmitButton)
})
After writing this line we need to run the tests again and we can see that we have a failing test.
Let’s move on to make the test green. Following the other step of TDD, we need to write only enough code to make the test green.
Let’s create Button.js in the src folder with the following code
export default() => {}
And create the following jsx on App.js to return the Button component.
import React from 'react';
import SubmitButton from './Button'
function App() {
return <SubmitButton />
}
export default App;
Also, we need to import the Button component in src/App.test.js
import React from 'react';
import App from './App';
import { shallow } from 'enzyme';
import SubmitButton from './Button'
describe('App', () => {
let appWrapper
beforeAll(() => {
appWrapper = shallow(<App />)
})
it('renders a button component', () => {
const submitButton = appWrapper.find(SubmitButton)
})
})
Now we have our test in green.
Let’s finish this test by doing an assertion. We are going to inspect if the App contains one Button component by adding the following code to the first test:
expect(submitButton).toHaveLength(1)
Full test looks like this:
it('renders a button component', () => {
const submitButton = appWrapper.find(SubmitButton)
expect(submitButton).toHaveLength(1)
})
Ensure that the test is still green.
So far we’ve made a little bit of TDD by creating a Button component and render it in App component.
Let’s keep practicing!
Our second test is going to inspect if we are passing a prop called value to the Button component
Let’s create a failing test
it('pass value as prop', () => {
const submitButton = appWrapper.find(SubmitButton)
expect(submitButton.props().value).toBeDefined()
})
We are expecting a prop called value to be defined in the Button component but we received an undefined value.
Let’s make this test green!
To make the test pass, we need to pass a prop called value in the Button component inside the App component
function App() {
return <SubmitButton value="" />
}
Now our test is green again, congrats!
Let’s do another test to keep practicing TDD, remember practice makes perfect!
For this test, we want to inspect if the value prop is equal to the string “Submit” so we are going to create another failing test.
it("has a value prop equal to 'Submit'", () => {
const submitButton = appWrapper.find(SubmitButton)
expect(submitButton.props().value).toEqual('Submit')
})
As you can imagine this test is going to fail
We need to refactor the Button component inside the App component to make the test green.
import React from 'react';
import SubmitButton from './Button'
function App() {
return <SubmitButton value="Submit" />
}
export default App;
YES!, all the three tests are passing now
If you get here, Well done! you now have a clear idea of what Test-driven development is.
Just to recap:
- write a failing test, just enough code to make the test fails
- refactor only the necessary code to make the test green
- Then again, write a failing test and then refactor to make the test green and so on
And for final touches, as you can see we are repeating const submitButton = appWrapper.find(SubmitButton)
across all our test cases, so let’s refactor this to make our code DRY
let appWrapper, submitButton
beforeAll(() => {
appWrapper = shallow(<App />)
submitButton = appWrapper.find(SubmitButton)
})
Now we can safely delete const submitButton = appWrapper.find(SubmitButton)
in every test and our test will still be green.
Full code:
import React from 'react';
import App from './App';
import { shallow } from 'enzyme';
import SubmitButton from './Button'
describe('App', () => {
let appWrapper, submitButton
beforeAll(() => {
appWrapper = shallow(<App />)
submitButton = appWrapper.find(SubmitButton)
})
it('renders a button component', () => {
expect(submitButton).toHaveLength(1)
})
it('pass value as prop', () => {
expect(submitButton.props().value).toBeDefined()
})
it("has a value prop equal to 'Submit'", () => {
expect(submitButton.props().value).toEqual('Submit')
})
})
Additional Resources
A great resource that explains in a very clear way how to do TDD:
TDD Live Coding - Test Driven Development Tutorial with React, Jest, and Enzyme
Find me at github.com/Viricruz
Top comments (0)