At Sparkbox, we’re quite familiar with design systems and are huge fans when they’re done well and are continuously nurtured. Well-established design systems that are adopted and cared for have tremendous value potential.
I am currently part of a project team that works on concepting, designing, building, and continuously improving a design system for one of our enterprise clients. This particular design system is spread across multiple brands and is one we’ve been iterating on for two-and-a-half years. It has been adopted by a handful of teams we refer to as “subscribers.” Since our design system is the “source of truth” for styles across all the company’s many brands and online channels, accuracy of the styles we put into the system is very important. For example, if we were to make an update that broke a subscriber’s styles, it could impact production code. This would lead to distrust in our design system and potentially impact our ability to serve subscribers well.
Knowing that our enterprise client’s design system needs to be dependable at all times, we take any updates to it very seriously. Recently, we were asked by subscribers to provide the Sass mixins and functions that we use for things like colors, typography, and spacing. They already existed but weren’t easily accessible to subscribers without them going through our codebase. Since we were making our code more available, changes that we make to this underlying code could have major repercussions if done incorrectly. We never want our subscribers to worry that changes we make will break their code, so we started looking into ways to unit test our Sass.
What Is Unit Testing?
In software development, unit testing includes writing tests that ensure a piece of functionality, or “unit,” is working as expected. Some benefits of unit testing include faster debugging when there is an issue, and finding issues before code is sent to production. Ryan mentions in a previous post that when unit testing, you want to test your code, not the code of a dependency (i.e. jQuery). We refer to this as “Mowing Your Own Yard.”
Why Unit Test Sass?
“Should you be unit testing your Sass?” is a great question to ask. But, “Why would you want to do that?” is an even better question.
Ensure Code Is Compiling as Expected
Projects that make use of mixins or functions in Sass can really benefit from unit testing to ensure your code is compiling as expected. If you are not using a lot of mixins, functions, or Sass in general, then you may not benefit as much from unit testing. Mixins and functions are powerful features of Sass but checking the accuracy of their output is not a native feature. Your CSS might still compile, but it could not be the intended output. In the case of our design system, let’s say we were to rearrange some of our Sass files. This could alter the cascade and affect compiled styles regarding colors or fonts, as a few examples.
Catch Output Errors Quickly
Another reason to unit test Sass is because Sass as a library could change. This could impact our CSS output, which we might not notice right away. Having the unit tests in place will help catch output errors before our code is sent to production and will allow us to debug those errors faster if the library changes.
For our current project, we are using an open-source tool called True, by Miriam Suzanne for unit testing. The tool is currently well-maintained and there are multiple ways to integrate it into your project.
How to Use True
There are a few ways you can implement True, but the setup instructions below are taken from the True documentation website—see the “Using Mocha (or other JS Test Runners)” section. These instructions assume you have npm and node-sass set up in your project.
First, install True via npm:
npm install sass-true
The way to set up your testing file structure is completely up to you. However, if you’re already using Sass partials to organize your project’s styles, I suggest setting up your tests in a similar way. Your unit tests are written in Sass, so you can use partials to organize your files. This is an example of how we have our unit tests organized alongside our Jasmine testing and setup files:
Then, import True into your project (your file path may differ) and specify the Sass file to test against:
sass-tests.scss
@import "node_modules/sass-true/sass/true";
@import "../../../my-styles.scss";
Next, you will write the JavaScript file used to set up your Sass tests. I will be following the Describe/It testing pattern. The Describe/It testing pattern gives your tests structure, and makes the output of a test easy to understand.
Write a JS test file:
true-sass-spec.js
var path = require('path');
var sassTrue = require('sass-true');
var sassFile = path.join(__dirname, 'true-sass-tests/sass_tests.scss');
sassTrue.runSass({file: sassFile}, {describe, it});
Now we’re at the fun part—writing unit tests for our Sass!
There are two main ways to write tests, depending on if you’re testing a function or a mixin. I’ll start with an example of testing a mixin.
This is an example of what the mixin would look like in your Sass:
@mixin primary-header {
font-family: "Helvetica", Arial, sans-serif;
font-size: 2rem;
line-height: 2.5rem;
color: #333;
text-decoration: underline;
}
And here is the test for it:
// Describe what you're testing
@include describe('The primary-header mixin') {
// Explain what it should do
@include it('outputs the properties of our primary header.') {
// Assert the output of the mixin matches the expected result
@include assert {
@include output {
@include primary-header();
}
@include expect {
font-family: "Helvetica", Arial, sans-serif;
font-size: 2rem;
line-height: 2.5rem;
color: #333;
text-decoration: underline;
}
}
}
}
When this test passes, here is how it looks in the console:
Notice that the output reads like a sentence of exactly what is happening. This is intentional and is accomplished by using the Describe/It pattern.
You can also use contains
instead of expect
in the code above to test if a subset of properties exist. This is useful if you have a lot of properties in the output and will reduce code by not being required to test all of them. This is documented on the the True website.
Here is an example of a function in our Sass that we’d like to test:
@function divide-height ($height) {
$startingHeight: 100%;
@return $startingHeight / $height;
}
Here is the test we would write:
// Describe the function
@include describe('The `divide-height` function') {
// Describe what it should do
@include it('returns a valid height when given valid input.') {
// Test your function with an input
$test: divide-height('2');
// What the expected value should be
$expect: 50%;
// Assert they are equal, otherwise show the error message
@include assert-equal($test, $expect, "The height is either incorrect or does not exist.");
}
}
The console will output a failure because the input that we passed to our function (‘2′) is a string, not a number, therefore causing a failure.
The test that has the correct input passed:
// Describe the function
@include describe('The `divide-height` function') {
// Describe what it should do
@include it('returns a valid height when given valid input.') {
// Test your function with an input
$test: divide-height(2);
// What the expected value should be
$expect: 50%;
// Assert they are equal, otherwise show the error message
@include assert-equal($test, $expect, "The height is either incorrect or does not exist.");
}
}
Will pass the test:
Write Your Tests to Fail
One tip I’ve recently learned on writing unit tests, thanks to Sparkboxer Catherine, is that you want to write your tests to fail first, and then adjust them to make them pass. If your tests pass as soon as you write them, they may not actually be working. We had this happen to us recently when we were testing a mixin that wasn’t working correctly. The result was that the mixin and test weren’t returning any output, but the test didn’t fail because they were equal. Had we written the test to fail first, we would have caught this sooner.
Worry Less, Test More
Design systems, web apps, or any project that makes use of mixins or functions in Sass can really benefit from unit testing to ensure code is compiling as expected. You will be able to catch unintended output that may otherwise go unnoticed. You will also worry less when it comes to refactoring if you’re confident that your tests will catch potential errors. Sass testing is one thing you can add to your arsenal for ensuring a web built right.
Sparkbox’s Development Capabilities Assessment
Struggle to deliver quality software sustainably for the business? Give your development organization research-backed direction on improving practices. Simply answer a few questions to generate a customized, confidential report addressing your challenges.