CSS in JS and Test-ability

David Cai
3 min readDec 20, 2017

--

My company is in the middle of technology migration to move closer to the React ecosystem. We seek a new styling solution to replace SASS or PostCSS in order to bring more direct interplay between React components and CSS styling. We chose Styled Components — one of the main CSS-in-JS libraries. Part of the reasons is test-ability. Many modern styling solutions try to solve the following common problems:

  1. Isolation — Encapsulate styles in scopes. Prevent styles inside and outside of the scope from polluting each other.
  2. Utilities — Helpers to ease the calculation (e.g., darkening color), and reduce boilerplate code (e.g., adding vendor prefixes).
  3. Reusablity — Reuse and extend defined styles.

Traditional CSS pre-processing technologies (e.g., PostCSS with CSS Module) are able to solve the above problems. Even SASS with BEM convention can provide a workaround solution to these problems. However, CSS-in-JS solution excels at solving another common issue — style testing.

Test Mixins and Functions

Here is a SASS function that maps weight names to numeric values:

This is all good. But how do we test this getWeight function? I am sure you can come up with some creative solutions that might leverage the SASS compiler and then inspect the produced CSS. This solution has quite some loops to jump in order to get the test going.

Now with Styled Components, getWeight is simply a JavaScript function like this:

And to test it is just like unit tests for any JavaScript function:

Mixins or functions are probably the lowest-level dynamic parts in style definitions. And they tend to be used a lot. It is crucial to get this base-level functionality done right. Having unit test cover these functions gave us high confidence for the styling code.

Test Components

We can also test a component’s styles in unit test. To continue our example, we call the getWeight function in a Styled Component – StyledDiv in which we define the font-weight style; then a React component FontWeight wraps its children with StyledDiv:

Styled component translates interpolated strings to CSS styles, and create CSS classes. Tools such as jest-styled-components can be used to inspect the translated CSS styles. This gives us an easy way to test component styles in unit test.

Here is an example. We test if a FontWeight component has font-weight: 300 when its weight prop is set to ‘light’.

toHaveStyleRule gives us fine granular assertions over a component’s internal styles. This makes it much easier for unit test to verify styles. Unit tests are way faster than visual acceptance tests which often rely on browsers or engines to render components in DOMs. With the capability of having style verification in unit test, we can conduct more extensive style tests without relying on slow acceptance tests. That is not to say acceptance test should be replaced by style unit test, but having unit test cover all corner cases make it possible for acceptance test to focus on critical-path scenarios, which results in less acceptance test code and shorter pipeline build time.

I have to mention another API from jest-styled-components toMatchSnapshot because it is amazing. toMatchSnapshot takes a snapshot of a component’s structure and its styles, and compares the snapshot with previously saved one. If there is any difference, the assertion will fail and a diff will be output.

For instance, the toMatchSnapshot call in the following test spec will capture FontWeight component’s DOM structure and styles in a snapshot file:

Running the above test spec for the first time will produce a snapshot file like this:

If anything goes wrong (e.g., font-weight becomes 200), running the same test will produce a diff in terminal:

The diff provides us a straightforward way to spot what changes and what was the original. Quite handy when you just want to make sure that no style will accidentally get changed.

Conclusion

We are quite happy about the adoption Styled Components. CSS in JS not only solves common styling problems, but more importantly for us, it makes unit test for components pleasant again. We are able to write more useful style tests with much less effort.

--

--