Table of Contents
What’s the idea?
Setting up the testing architecture
The Gulp workflow
Adding a pre-commit hook
Final thoughts
Home Web Front-end JS Tutorial Testing a Sass Library

Testing a Sass Library

Feb 19, 2025 pm 12:38 PM

Testing a Sass Library

Lately, I have spent a decent amount of time working with Eduardo Bouças on include-media. We went through a lot of refactoring so decided to write some tests and run them on every commit to be sure we did not break anything. I’ll go through the details in this article.

If you don’t know include-media yet, it is a very lightweight yet powerful breakpoint manager in Sass.

The provided public API is a single mixin, media(..) (hence the name of the library), but the whole thing is well thought enough so you can actually do wonders with it. A short example before getting started:

1

2

3

4

5

6

7

8

9

10

<span>.my-component {

</span><span>  <span>width: 100%;</span>

</span>

  <span>// On screens larger or equal to *small* breakpoint,

</span>  <span>// make the component floated and half the size

</span><span>  <span>@include media('≥small') {</span>

</span><span>    <span>float: left;</span>

</span><span>    <span>width: 50%;</span>

</span>  <span>}

</span><span>}</span>

Copy after login
Copy after login

Now that’s pretty rad, isn’t it?

Anyway, so we came up with a little testing system that I would like to share with you guys. Of course, if you want to test a full framework, you might want to use True from Eric Suzanne instead, which is a full blown testing framework written in Sass, for Sass and was introduced and explained by David in a recent article on SitePoint.

What’s the idea?

We wanted to run a few tests on the main private functions from the library any time we commit to the repository. If any test fails, the commit is aborted and the code needs to be fixed to allow the commit to pass. This way, we make sure that we can safely work on the library without risking breaking it (which is usually a bad thing).

Achieving something like this ended up being surprisingly easy: we set up a pre-commit Git hook to run tests in both LibSass and Ruby Sass before any commiting. If the test is failing, we kill the process.

Testing a Sass Library

There are different ways to run Sass and LibSass. You can either have binaries, or you could use a wrapper. In our case, we opted for a tiny Gulp workflow, making it easy for us to run both Ruby Sass and LibSass.

We wanted something very simple, so tests are written in Sass using SassyTester, which I introduced recently in the article Testing a Sass function in 5 minutes. SassyTester is about 25 lines long. The testing function only outputs a Sass map with the results from the tests. From there, we can do anything we want with it. In our case, we want to throw an error if a test fails. To do so, we have the @error directive from Sass!

When compiling the Sass tests, if the Gulp task encounters a Sass error, it exits the process while throwing an error itself, which bubbles up to the pre-commit hook and finally aborts the commit.

If we sum this up, it goes like this:

  1. A pre-commit hook runs a test Gulp task on commit
  2. The test Gulp task compiles Sass tests in both LibSass and Ruby Sass
  3. If a test fails, Sass throws an error with @error
  4. The Sass error is caught by Gulp which itself emits an error
  5. The Gulp error is caught by the pre-commit hook which aborts the commit

So far, so good?

Setting up the testing architecture

The architecture word makes it sound so big while it actually is extremely simple. Here is what the project could look like:

1

2

3

4

5

6

7

8

9

10

<span>.my-component {

</span><span>  <span>width: 100%;</span>

</span>

  <span>// On screens larger or equal to *small* breakpoint,

</span>  <span>// make the component floated and half the size

</span><span>  <span>@include media('≥small') {</span>

</span><span>    <span>float: left;</span>

</span><span>    <span>width: 50%;</span>

</span>  <span>}

</span><span>}</span>

Copy after login
Copy after login

Not that impressive after all, heh? The Gulp task will simply run the Sass engines on all files in the tests folder. Here is what function-1.scss could look like:

1

2

3

4

5

6

7

8

9

10

11

12

13

dist/

<span>|

</span><span>|- my-sass-library.scss

</span><span>|

</span>tests/

<span>|

</span><span>|- helpers/

</span><span>|   |- _SassyTester.scss

</span><span>|   |- _custom-formatter.scss

</span><span>|

</span><span>|- function-1.scss

</span><span>|- function-2.scss

</span><span>|- ...</span>

Copy after login

Last but not least, we need to redefine the run(..) because the original one from SassyTester outputs the tests results with @error no matter whether they all pass or not. In our case, we only want to throw if there is an error. Let’s just put it in helpers/_output-formatter.scss.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

<span>// Import the library to test (or only the function if you can)

</span><span><span>@import '../dist/my-sass-library';</span>

</span>

<span>// Import the tester

</span><span><span>@import 'helpers/SassyTester';</span>

</span>

<span>// Import the custom formatter

</span><span><span>@import 'helpers/custom-formatter';</span>

</span>

<span>// Write the tests

</span><span>// See my previous article to know more about this:

</span><span>// http://...

</span><span><span>$tests-function-1: ( ... );</span>

</span>

<span>// Run the tests

</span><span><span>@include run(test('function-1', $tests-function-1));</span></span>

Copy after login

For a more advanced version of an equivalent run(..) mixin, check the one from include-media.

The Gulp workflow

If you want a short introduction to Gulp, please be sure to read my recent article about it: A Simple Gulpy Workflow for Sass. For this section, I’ll assume you’re familiar with Gulp.

We need three tasks:

  • one to run LibSass on tests folder (using gulp-sass)
  • one to run Ruby Sass on tests folder (using gulp-ruby-sass)
  • one to run the two previous tasks

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

<span>// We overwrite the `run(..)` mixin from SassyTester to make it throw

</span><span>// an `@error` only if a test fails. The only argument needed by the

</span><span>// `run(..)` mixin is the return of `test(..)` function from SassyTester.

</span><span>// You can check what `$data` looks like in SassyTester documentation:

</span><span>// http://kittygiraudel.com/SassyTester/#function-test

</span><span><span>@mixin run($data) {</span>

</span><span>  <span>$tests: map-get($data, 'tests');</span>

</span>

<span>  <span>@each $test in $tests {</span>

</span><span>    <span>@if map-get($test, 'fail') {</span>

</span><span>      <span>@error 'Failing test!</span>

</span><span>        <span>Expected : #{map-get($test, 'expected')}</span>

</span><span>        <span>Actual   : #{map-get($test, 'actual')}';</span>

</span>    <span>}

</span>  <span>}

</span><span>}</span>

Copy after login

Ideally, when Sass throws an error (either because of a built-in error or because of @error), Gulp should exit properly. Unfortunately, there is an issue about this on gulp-ruby-sass that is still not fixed so for Ruby Sass, we have to raise a Node Uncaught Fatal Exception with process.exit(1) ourselves.

Adding a pre-commit hook

There are tons of libraries to set up pre-commit hooks. I personally like pre-commit but you can basically choose the one you like as they all do more or less the same thing.

To add a pre-commit hook to our project, we need to create a pre-commit key in our package.json. This key is mapped to an array of npm scripts commands. Thus, we also need a scripts object, with a key named test, mapped to the Gulp command: gulp test.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

<span>var gulp = require('gulp');

</span><span>var sass = require('gulp-sass');

</span><span>var rubySass = require('gulp-ruby-sass');

</span>

<span>// Run LibSass on the tests folder

</span><span>// Gulp automatically exits process in case of Sass error

</span>gulp<span>.task('test:libsass', function () {

</span>  <span>return gulp.src('./tests/*.scss')

</span>    <span>.pipe(plugins.sass());

</span><span>});

</span>

<span>// Run Ruby Sass on the tests folder

</span><span>// Gulp manually exits process in case of Sass error

</span>gulp<span>.task('test:ruby-sass', function () {

</span>  <span>return rubySass('./tests')

</span>    <span>.on('error', function (err) {

</span>      process<span>.exit(1);

</span>    <span>});

</span><span>});

</span>

gulp<span>.task('test', ['test:libsass', 'test:ruby-sass']);</span>

Copy after login

When commiting, the pre-commit hook fires and tries to execute the test npm script. This script runs the following command: gulp test, which intimates Gulp to run the tests.

That’s it, we’re done.

Final thoughts

This example is extremely simplistic as you can see, but it does the job and it does it well. Here is what it might look like:

Testing a Sass Library

So what do you think? Is this something you might consider adding to your library or framework?

The above is the detailed content of Testing a Sass Library. For more information, please follow other related articles on the PHP Chinese website!

Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

What should I do if I encounter garbled code printing for front-end thermal paper receipts? What should I do if I encounter garbled code printing for front-end thermal paper receipts? Apr 04, 2025 pm 02:42 PM

Frequently Asked Questions and Solutions for Front-end Thermal Paper Ticket Printing In Front-end Development, Ticket Printing is a common requirement. However, many developers are implementing...

Demystifying JavaScript: What It Does and Why It Matters Demystifying JavaScript: What It Does and Why It Matters Apr 09, 2025 am 12:07 AM

JavaScript is the cornerstone of modern web development, and its main functions include event-driven programming, dynamic content generation and asynchronous programming. 1) Event-driven programming allows web pages to change dynamically according to user operations. 2) Dynamic content generation allows page content to be adjusted according to conditions. 3) Asynchronous programming ensures that the user interface is not blocked. JavaScript is widely used in web interaction, single-page application and server-side development, greatly improving the flexibility of user experience and cross-platform development.

Who gets paid more Python or JavaScript? Who gets paid more Python or JavaScript? Apr 04, 2025 am 12:09 AM

There is no absolute salary for Python and JavaScript developers, depending on skills and industry needs. 1. Python may be paid more in data science and machine learning. 2. JavaScript has great demand in front-end and full-stack development, and its salary is also considerable. 3. Influencing factors include experience, geographical location, company size and specific skills.

Is JavaScript hard to learn? Is JavaScript hard to learn? Apr 03, 2025 am 12:20 AM

Learning JavaScript is not difficult, but it is challenging. 1) Understand basic concepts such as variables, data types, functions, etc. 2) Master asynchronous programming and implement it through event loops. 3) Use DOM operations and Promise to handle asynchronous requests. 4) Avoid common mistakes and use debugging techniques. 5) Optimize performance and follow best practices.

How to achieve parallax scrolling and element animation effects, like Shiseido's official website?
or:
How can we achieve the animation effect accompanied by page scrolling like Shiseido's official website? How to achieve parallax scrolling and element animation effects, like Shiseido's official website? or: How can we achieve the animation effect accompanied by page scrolling like Shiseido's official website? Apr 04, 2025 pm 05:36 PM

Discussion on the realization of parallax scrolling and element animation effects in this article will explore how to achieve similar to Shiseido official website (https://www.shiseido.co.jp/sb/wonderland/)...

How to merge array elements with the same ID into one object using JavaScript? How to merge array elements with the same ID into one object using JavaScript? Apr 04, 2025 pm 05:09 PM

How to merge array elements with the same ID into one object in JavaScript? When processing data, we often encounter the need to have the same ID...

The Evolution of JavaScript: Current Trends and Future Prospects The Evolution of JavaScript: Current Trends and Future Prospects Apr 10, 2025 am 09:33 AM

The latest trends in JavaScript include the rise of TypeScript, the popularity of modern frameworks and libraries, and the application of WebAssembly. Future prospects cover more powerful type systems, the development of server-side JavaScript, the expansion of artificial intelligence and machine learning, and the potential of IoT and edge computing.

The difference in console.log output result: Why are the two calls different? The difference in console.log output result: Why are the two calls different? Apr 04, 2025 pm 05:12 PM

In-depth discussion of the root causes of the difference in console.log output. This article will analyze the differences in the output results of console.log function in a piece of code and explain the reasons behind it. �...

See all articles