В последнее время я потратил приличное количество времени, работая с Эдуардо Боучасом на include-media . Мы провели много рефакторинга, поэтому решили написать несколько тестов и запускать их при каждом коммите, чтобы убедиться, что мы ничего не сломали. Я пройдусь по деталям в этой статье.
Если вы еще не знаете include-media, это очень легкий, но мощный менеджер точек останова в Sass.
Предоставленный публичный API представляет собой единый миксин media(..)
(отсюда и название библиотеки), но все это достаточно хорошо продумано, так что вы действительно можете творить чудеса с ним. Краткий пример перед началом работы:
.my-component { width: 100%; // On screens larger or equal to *small* breakpoint, // make the component floated and half the size @include media('≥small') { float: left; width: 50%; } }
Теперь это довольно рад, не так ли?
В любом случае, мы создали небольшую систему тестирования, которой я хотел бы поделиться с вами, ребята. Конечно, если вы хотите протестировать полную платформу, вы можете вместо этого использовать True от Eric Suzanne, которая представляет собой полнофункциональную платформу тестирования, написанную на Sass, для Sass, которая была представлена и объяснена Дэвидом в недавней статье на SitePoint.
В чем идея?
Мы хотели запустить несколько тестов основных закрытых функций из библиотеки в любое время, когда мы фиксируем репозиторий. Если какой-либо тест завершается неудачей, фиксация прерывается, и код должен быть исправлен, чтобы позволить фиксации пройти. Таким образом, мы гарантируем, что сможем безопасно работать с библиотекой, не рискуя ее сломать (что обычно плохо ).
Достигнуть чего-то подобного оказалось на удивление легко: мы установили ловушку Git перед фиксацией для запуска тестов в LibSass и Ruby Sass перед любой фиксацией. Если тест не пройден, мы убиваем процесс.
Существуют разные способы запуска Sass и LibSass. Вы можете иметь двоичные файлы или использовать оболочку. В нашем случае мы выбрали крошечный рабочий процесс Gulp, что облегчает нам запускать как Ruby Sass, так и LibSass.
Мы хотели что-то очень простое, поэтому тесты пишутся на Sass с использованием SassyTester , который я недавно представил в статье « Тестирование функции Sass за 5 минут» . SassyTester составляет около 25 строк. Функция тестирования выводит только карту Sass с результатами испытаний. Оттуда мы можем делать с ней все, что захотим. В нашем случае мы хотим выдать ошибку, если тест не пройден. Для этого у нас есть директива @error
от Sass!
При компиляции тестов Sass, если задача Gulp обнаруживает ошибку Sass, она выходит из процесса и выдает саму ошибку, которая пузырится до ловушки перед фиксацией и, наконец, прерывает фиксацию.
Если подвести итог, это будет выглядеть так:
- Хук pre-commit запускает
test
задание Gulp при коммите -
test
задание Gulp компилирует тесты Sass как в LibSass, так и в Ruby Sass. - Если тест не
@error
, Sass выдает ошибку с@error
- Ошибка Sass отлавливается Gulp, который сам выдает ошибку
- Ошибка Gulp перехватывается ловушкой предварительной фиксации, которая прерывает фиксацию
Все идет нормально?
Настройка архитектуры тестирования
Архитектурное слово заставляет его звучать так громко, хотя на самом деле это очень просто. Вот как может выглядеть проект:
dist/ | |- my-sass-library.scss | tests/ | |- helpers/ | |- _SassyTester.scss | |- _custom-formatter.scss | |- function-1.scss |- function-2.scss |- ...
Не так уж и впечатляет, а? Задача Gulp просто запустит движки Sass для всех файлов в папке tests
. Вот как может выглядеть function-1.scss
:
// Import the library to test (or only the function if you can) @import '../dist/my-sass-library'; // Import the tester @import 'helpers/SassyTester'; // Import the custom formatter @import 'helpers/custom-formatter'; // Write the tests // See my previous article to know more about this: // http://... $tests-function-1: ( ... ); // Run the tests @include run(test('function-1', $tests-function-1));
И последнее, но не менее важное: нам нужно переопределить run(..)
потому что оригинальный из SassyTester выводит результаты тестов с помощью @error
независимо от того, все они @error
или нет. В нашем случае мы хотим бросить только в случае ошибки. Давайте просто поместим это в helpers/_output-formatter.scss
.
// We overwrite the `run(..)` mixin from SassyTester to make it throw // an `@error` only if a test fails. The only argument needed by the // `run(..)` mixin is the return of `test(..)` function from SassyTester. // You can check what `$data` looks like in SassyTester documentation: // http://hugogiraudel.com/SassyTester/#function-test @mixin run($data) { $tests: map-get($data, 'tests'); @each $test in $tests { @if map-get($test, 'fail') { @error 'Failing test! Expected : #{map-get($test, 'expected')} Actual : #{map-get($test, 'actual')}'; } } }
Для более продвинутой версии эквивалентного run(..)
mixin, проверьте тот из include-media .
Рабочий процесс Gulp
Если вы хотите краткое введение в Gulp, пожалуйста, прочитайте мою недавнюю статью об этом: Простой рабочий процесс Gulpy для Sass . В этом разделе я предполагаю, что вы знакомы с Gulp.
Нам нужно три задачи:
- один для запуска LibSass в папке с
tests
(используя gulp-sass ) - один для запуска Ruby Sass в папке с
tests
(используя gulp-ruby-sass ) - один для запуска двух предыдущих задач
var gulp = require('gulp'); var sass = require('gulp-sass'); var rubySass = require('gulp-ruby-sass'); // Run LibSass on the tests folder // Gulp automatically exits process in case of Sass error gulp.task('test:libsass', function () { return gulp.src('./tests/*.scss') .pipe(plugins.sass()); }); // Run Ruby Sass on the tests folder // Gulp manually exits process in case of Sass error gulp.task('test:ruby-sass', function () { return rubySass('./tests') .on('error', function (err) { process.exit(1); }); }); gulp.task('test', ['test:libsass', 'test:ruby-sass']);
В идеале, когда Sass выдает ошибку (либо из-за встроенной ошибки, либо из-за @error
), Gulp должен правильно @error
. К сожалению, есть проблема по этому поводу в gulp-ruby-sass, которая до сих пор не исправлена, поэтому для Ruby Sass мы должны вызвать исключение Node Uncaught Fatal Exception с process.exit(1)
самостоятельно.
Добавление хука до фиксации
Существует множество библиотек для настройки хуков предварительной фиксации. Лично мне нравится предварительная фиксация, но вы можете выбрать ту, которая вам нравится, поскольку все они делают более или менее одно и то же.
Чтобы добавить хук предварительной фиксации к нашему проекту, нам нужно создать ключ pre-commit
в нашем package.json
. Этот ключ сопоставлен с массивом команд сценариев npm . Таким образом, нам также нужен объект scripts
с ключом с именем test
, сопоставленный с командой Gulp: gulp test
.
"scripts": { "test": "gulp test" }, "pre-commit": ["test"]
При фиксации запускается ловушка pre-commit и пытается выполнить test
скрипт npm. Этот скрипт запускает следующую команду: gulp test
, которая указывает Gulp для запуска тестов.
Вот и все, мы сделали.
Последние мысли
Этот пример очень прост, как вы можете видеть, но он выполняет свою работу и делает это хорошо. Вот как это может выглядеть:
Так что ты думаешь? Вы можете добавить это в свою библиотеку или фреймворк?