Статьи

Создание тестов для вашей Sass Framework

Итак, вы написали (или хотите написать) потрясающий инструментарий Sass, фреймворк или даже небольшую функцию или миксин, и вы хотите поделиться им со всем миром. Фантастический! Вот что хорошо в разработке программного обеспечения с открытым исходным кодом – вы можете совместно работать над кодом с другими поддерживающими разработчиками в сообществе, чтобы создавать полезное программное обеспечение, от которого многие могут извлечь выгоду.

Но не так быстро! Очень важно протестировать ваше программное обеспечение перед его выпуском. Один из наиболее эффективных способов сделать это – использовать модульные тесты , которые представляют собой автоматизированные сценарии, которые проверяют, ведет себя ли одна функция одного блока кода в соответствии с определенной спецификацией.

Sass дает вам возможность манипулировать данными, используя функции , операторы , переменные и управляющие директивы и выражения . Возвращаемое значение @function также может быть напрямую проверено на соответствие ожидаемому значению. Для вывода CSS и визуального регрессионного тестирования лучше подходит такой инструмент, как PhantomCSS , хотя @mixins можно протестировать в последней версии True.

Мы будем использовать среду истинного тестирования Эрика Сюзанна для примерных функций модульного тестирования в Sass, но представленные рекомендации применимы для любой среды тестирования Sass. Для простых тестов вы можете использовать минималистичную (и потрясающую) среду тестирования Sass, например SassyTester от Hugo Giraudel .

Настройка

True может быть установлен как Ruby gem ( gem install true ) или как пакет Bower ( bower install true ). Параметры конфигурации можно найти в документации True . Оттуда путь загрузки может быть настроен так, чтобы указывать на истинную установку.

Храните тестовые файлы в отдельном каталоге от ваших файлов Sass. Это .gitignore того, чтобы тесты можно было легко .gitignore -d и освободить от случайного просмотра и компиляции Sass. Если проект в основном является проектом Sass, каталог может быть назван /tests , а основной тестовый файл находится по адресу /tests/tests.scss .

Внутри каталога /tests он помогает организовать ваши тесты по модулю , который обычно соответствует @function или @mixin он представляет. Иногда похожие функции и миксины могут быть сгруппированы в одном модуле.

 // Sample Sass project structure /css /scss /tests /api _foo.scss _bar.scss _all.scss // imports foo and bar tests /helpers _baz.scss _qux.scss _all.scss // imports baz and qux tests tests.scss // imports api/_all and helpers/_all index.html package.json // ... etc. 

Основной тестовый файл tests.scss будет отвечать за импорт и составление отчетов обо всех тестах и фактическом проекте SCSS, который будет тестироваться:

 @import 'true'; // Import the project @import '../scss/project'; // Import the test files @import 'api/all'; @import 'helpers/all'; // Run the tests @include report(); 

Функции тестирования

Чтобы эффективно выполнять функции модульного тестирования, лучше всего собрать спецификации тестируемой функции. В модульной архитектуре каждая функция несет одну ответственность – у нее одно задание, и обычно она возвращает значение одного и того же типа.

Простой способ создать спецификации для функции – это сказать, что « она должна (что-то делать) » с учетом определенного ввода. Это принцип управляемой поведением разработки , который можно увидеть в других средах тестирования, таких как Jasmine (JavaScript) .

Давайте посмотрим на пример функции (и соответствующие переменные):

 // inside 'scss/project.scss' $type-base-font-size: 16px !default; $type-base-ratio: 1.4 !default; @function type-scale( $exponent: 1, $base: $type-base-font-size, $ratio: $type-base-ratio, $factor: 1 ) { @return ceil(($factor * $base) * pow($ratio, $exponent)); } 

Это очень простая функция, которая должна возвращать соответствующий размер шрифта в зависимости от модульной шкалы (см. Type-scale.com для визуального представления этой концепции). С этим у нас есть наша первая спецификация :

 // inside 'tests/api/type-scale.scss' @include test-module('type-scale') { @include test('should return the appropriate font size based on the modular scale') { $actual: type-scale(1); $expected: 23px; // based on base 16px and ratio 1.4 @include assert-equal($actual, $expected); } } 

Здесь есть три основные части:

  • test-module : модуль (или блок ) кода, который вы тестируете; в этом случае функция type-scale
  • тест : спецификация или предположение о тестируемом устройстве
  • утверждение : утверждение, что фактическое значение соответствует ожидаемому значению.

Чтобы упростить управление тестами, я предпочитаю сохранять значения $actual и $expected в переменных перед проверкой утверждения. В True есть четыре типа утверждений:

  • assert-true($value) утверждает, что значение истинно
  • assert-false($value) утверждает, что значение неверно , например, null или false
  • assert-equal($assert, $expected) утверждает, что фактические (утвержденные) и ожидаемые значения равны
  • assert-unequal($assert, $expected) утверждает, что фактические (заявленные) и (не) ожидаемые значения не равны

Давайте рассмотрим несколько стратегий для функций модульного тестирования.

Используйте @mixin для подготовки теста, если это необходимо.

В нашей вышеупомянутой функции мы предполагаем, что глобальные переменные $type-base-font-size == 16px и $type-base-ratio == 1.4 . Предположения опасны при тестировании, как и изменчивые данные. Установите @mixin который может применять эти значения перед каждым тестом:

 @mixin test-type-scale-before() { $type-base-font-size: 16px !global; $type-base-ratio: 1.4 !global; } 

Теперь эти переменные будут защищены от внешних изменений в каждом тесте, который включает этот @mixin :

 @include test-module('type-scale function') { @include test('...') { @include test-type-scale-before(); // ... test code } } 

Проверьте каждый аргумент.

Функция Sass @function может включать в себя множество аргументов (хотя лучше их ограничить), и рекомендуется проверить каждый из них по отдельности, чтобы убедиться, что они возвращают ожидаемый результат:

 @include test('should work with an integer $exponent') { @include test-type-scale-before(); $actual: type-scale($exponent: 2); $expected: 32px; @include assert-equal($actual, $expected); } @include test('should work with a negative integer $exponent') { @include test-type-scale-before(); $actual: type-scale($exponent: -2); $expected: 9px; @include assert-equal($actual, $expected); } @include test('should work with a numerical font $base') { @include test-type-scale-before(); $actual: type-scale($exponent: 2, $base: 10px); $expected: 20px; @include assert-equal($actual, $expected); } // ... etc. for $ratio, $factor 

Проверьте аргументы по умолчанию.

Конечно, если ваша функция имеет аргументы по умолчанию, следует сделать утверждение, что функция, когда вызывается без аргументов, возвращает ожидаемый результат.

 @include test('should return the base font size with default arguments') { @include test-type-scale-before(); $actual: type-scale(); $expected: $type-base-font-size; @include assert-equal($actual, $expected); } 

Перебирайте разные потенциальные значения.

Иногда аргумент может принимать либо разные типы значений, либо значения с разными единицами измерения. Используйте @each , @while или @for для перебора всех потенциальных значений, которые могут принимать аргументы:

 @include test('should work with different units for $base') { @include test-type-scale-before(); $values: (10px, 100%, 10pt); // etc. $units: ('px', 'percent', 'pt'); // etc. $expected-values: (14px, 140%, 14pt); // etc. @for $index from 1 through length($values) { $value: nth($values, $index); $unit: nth($units, $index); $actual-value: type-scale(1, $value); $expected-value: nth($expected-values, $index); @include assert-equal($actual-value, $expected-value, 'Works with #{$unit}'); } } 

Тестирование миксинов

С True, @mixin можно проверить на корректность, сравнив фактический результат с ожидаемым. Синтаксис аналогичен утверждениям функций, с добавлением @include input @include expect и @include expect mixin @include expect .

Вот пример mixin масштаба масштаба, который использует функцию type-scale() :

 @mixin type-scale($exponent: 0) { font-size: type-scale($exponent); } 

И вот его сопровождающий тест:

 @include test('@mixin type-scale()') { @include assert('should output the appropriate font size') { @include input { @include type-scale(1); } @include expect { font-size: 23px; } } } 

Теперь, когда тесты запущены, вы можете сравнить .input { ... } блоком .expect { ... } для этого теста:

 /* @mixin type-scale() */ [data-module="type-scale function"] [data-test="@mixin type-scale()"] [data-assert="should output the appropriate font size"] .input { font-size: 23px; } [data-module="type-scale function"] [data-test="@mixin type-scale()"] [data-assert="should output the appropriate font size"] .expect { font-size: 23px; } 

Рекомендации по тестированию миксинов должны быть такими же, как и для тестирования функций. Обязательно проверяйте каждый аргумент, значения по умолчанию и просматривайте различные потенциальные значения.

Чтобы сделать миксины легко тестируемыми, разделите логику на миксины и на функции. Функции являются непосредственно тестируемыми, и это обеспечит большую модульность и разделение проблем внутри вашего проекта.

Запуск ваших тестов

В True есть несколько способов запустить тесты – через true-cli в терминале, или с помощью node-sass и выбранного вами тестировщика, такого как MochaJS . Обратитесь к документации True для получения дополнительной информации о настройке и запуске тестов. Если вы используете терминал, это так же просто, как запустить true-cli path/to/your/tests.scss .

Все вышеперечисленные тесты внутри @include test() должны быть внутри @include test-module() , с именем конкретного @include test-module() в качестве имени модуля, например, @include test-module('type-scale') Вы можете проверить эту суть Sassmeister, чтобы увидеть все вышеупомянутые тесты в действии.

Финальные заметки

Модульные тесты могут сэкономить часы разработки, автоматизировав процесс тестирования каждой части вашего проекта. Таким образом, вы можете выполнить рефакторинг с уверенностью в том, что, если все модульные тесты пройдут, код можно будет безопасно использовать и правильно реализовать. Модульное тестирование, естественно, также подчеркивает модульную структуру проекта.

Хорошее практическое правило: чем меньше кода, тем лучше, и чем больше модульных тестов, тем лучше. Сохраняйте объем и обязанности модулей вашего проекта (миксины и функции) небольшими, и они будут легко проверяемыми. Проверьте все возможные входные данные и убедитесь, что ваши функции как можно более идемпотентны .

Если вы хотите увидеть реальное использование модульных тестов в библиотеке Sass, посмотрите модульные тесты, которые я написал для Sassdash . Удачного тестирования!