How to test a Component’s CSS styles with React-Testing-Library (RTL) and Styled Components.
In my current project I use
- RTL: https://github.com/kentcdodds/react-testing-library
- Styled Components: https://www.styled-components.com/
If you check RTL documentation and examples you see very simple getBy* selectors for testing a code.
https://testing-library.com/docs/dom-testing-library/api-queries
The most ugly thing here is ByTestId
The author suppose that you could add some specific IDs in your code specially for tests. What is just unacceptable. Your code has to know just nothing about your tests and you MUST avoid pollution of your code with any tests-specific identifiers.
<input data-testid=”username-input” />
Time to time you have to select and check components which do not have any specific identifiers. For example, it may be a wrapper for a main part of a content. However, usually these wrappers have a class name and you can use it to select an element from DOM tree. Like that:
<ContentWrapper className="wrapperClass">
<ContentComponent />
</ContentWrapper>
But what if you use Styled Components? In this case you will not know a name of the wrapper component, it will be a hashed class name.
Otherwise it’s possible to find this element and check its styles. There is the solution.
import React from 'react'import 'jest-dom/extend-expect'
import { render, cleanup } from 'react-testing-library'import MyHeader from './MyHeader'afterEach(cleanup)test('It has to contain My Header', () => {
render(
<div>
<MyHeader />
</div>
) const headerClass = MyHeader().type.styledComponentId
const MyHeaderRoots = document.getElementsByClassName(headerClass)
const style = window.getComputedStyle(MyHeaderRoots[0]) expect(style.position).toBe('fixed')
expect(style.top).toBe('0px')
})
Here we see how to get a className of a Styled Component.
MyHeader().type.styledComponentId
=> "MyHeader__MyHeaderRoot-a8c9o2-0"
After that we just use a typical DOM selector method
document.getElementsByClassName(headerClass)
and finally we define the style object for the rendered element
const style = window.getComputedStyle(MyHeaderRoots[0])
Actually that is it. Now you can check CSS styles like that:
expect(style.position).toBe('fixed')
expect(style.top).toBe('0px')
Enjoy with testing!
UPD 1:
Why didn’t I use a way like that?
expect(container).toHaveStyle(‘padding-top: 10px;’)
This way was described in the Article https://hackernoon.com/testing-react-with-jest-axe-and-react-testing-library-accessibility-34b952240f53
In my case I have a div
wrapper for my component in the test
render(
<ExternalWrapper>
<MyHeader />
</ExternalWrapper>
)
In my case I could not remove the wrapper, because sometimes you have to use a specific wrappers with some properties from external packages. It makes impossible to use toHaveStyle directly in the code.
UPD 2:
The code
const headerClass = MyHeader().type.styledComponentId
const MyHeaderRoots = document.getElementsByClassName(headerClass)
const style = window.getComputedStyle(MyHeaderRoots[0])
May be moved to a helper-function
const getStyledComponentStyles = (StyledComponent, index = 0) => {
const componentClass = StyledComponent().type.styledComponentId
const components = document.getElementsByClassName(componentClass)
const style = window.getComputedStyle(components[index])
}
UPD 3:
Testing of CSS is not a best practice. Usually you have to avoid it, because it gives not too much. But sometimes you should test something specific what is simpler to test via this approach. For example, sometimes you want to be sure that an element has a required position
value. That is exactly I showed to you in my case.
For better testing you should try to use testing via snapshots and visual testing. Maybe once I’ll write an article about these approaches also.