最近,我花了很多时间与EduardoBouças一起在include-media上工作 。 我们经历了很多重构,因此决定编写一些测试并在每次提交时运行它们,以确保我们没有破坏任何东西。 我将在本文中详细介绍。
如果您还不知道include-media,那么它是Sass中非常轻巧但功能强大的断点管理器。
所提供的公共API是单个mixin,即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%;
}
}
现在很漂亮,不是吗?
无论如何,所以我们想出了一个小测试系统,希望与大家分享。 当然,如果您要测试一个完整的框架,则可能要改用Eric Suzanne的True ,它是Sass编写的,功能完整的测试框架,适用于Sass,由David在SitePoint的最新文章中进行介绍和解释。
有什么想法
每当我们提交到存储库时,我们都希望对库中的主要私有函数运行一些测试。 如果任何测试失败,则提交将中止,并且需要修复代码以允许提交通过。 这样,我们确保可以安全地在库上工作而不会冒险破坏它(这通常是一件坏事)。
完成这样的事情最终变得非常容易:我们设置了一个预提交的 Git钩子,以便在提交之前在LibSass和Ruby Sass中运行测试。 如果测试失败,我们将终止该过程。
有多种方法可以运行Sass和LibSass。 您可以具有二进制文件,也可以使用包装器。 在我们的案例中,我们选择了一个很小的Gulp工作流程,这使我们可以轻松地同时运行Ruby Sass和LibSass。
我们想要非常简单的东西,所以测试是使用SassyTester用Sass 编写的 ,我最近在文章5分钟内测试Sass函数中介绍了它。 SassyTester大约25行。 测试功能仅输出Sass映射以及测试结果。 从那里,我们可以用它做任何我们想做的事情。 在我们的例子中,如果测试失败,我们想抛出一个错误。 为此,我们有来自Sass的@error
指令!
编译Sass测试时,如果Gulp任务遇到Sass错误,它将退出该进程,同时抛出错误,该错误会冒泡到预提交钩子,最后中止提交。
如果我们总结一下,它是这样的:
- 预提交钩子在提交时运行
test
Gulp任务 -
test
Gulp任务在LibSass和Ruby Sass中都编译Sass测试 - 如果测试失败,Sass会使用
@error
引发错误 - Sass错误被Gulp捕获,Gulp本身发出错误
- Gulp错误由预提交钩捕获,该钩将中止提交
到目前为止,一切都很好?
设置测试架构
架构字使它听起来非常大,而实际上却非常简单。 以下是该项目的外观:
dist/
|
|- my-sass-library.scss
|
tests/
|
|- helpers/
| |- _SassyTester.scss
| |- _custom-formatter.scss
|
|- function-1.scss
|- function-2.scss
|- ...
毕竟没有那么令人印象深刻吗? Gulp任务将仅在tests
文件夹中的所有文件上运行Sass引擎。 这是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
的测试结果。 在我们的情况下,我们只想在出现错误时抛出。 让我们将其放在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进行简短介绍,请确保阅读我最近关于它的文章: Sass的简单古尔比工作流程 。 在本节中,我假设您熟悉Gulp。
我们需要三个任务:
- 一个在
tests
文件夹上运行LibSass(使用gulp -sass ) - 一个在
tests
文件夹上运行Ruby Sass(使用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应该正确退出。 不幸的是, 关于gulp-ruby-sass的问题仍然没有解决,因此对于Ruby Sass,我们必须自己使用process.exit(1)
引发Node Uncaught Fatal Exception 。
添加一个预提交的钩子
有大量的库可以设置预提交挂钩。 我个人喜欢预先提交,但是您基本上可以选择自己喜欢的一种,因为它们都或多或少地做同一件事。
要将预提交钩子添加到我们的项目中,我们需要在package.json
创建一个pre-commit
密钥。 该密钥映射到npm脚本命令数组。 因此,我们还需要一个scripts
对象,其键名为test
,映射到Gulp命令: gulp test
。
"scripts": {
"test": "gulp test"
},
"pre-commit": ["test"]
提交时,预提交挂钩会触发并尝试执行test
npm脚本。 该脚本运行以下命令: gulp test
,它提示Gulp运行测试。
就是这样,我们完成了。
最后的想法
如您所见,该示例非常简单,但是可以完成工作并且做得很好。 可能是这样的:
所以你怎么看? 您可以考虑将其添加到库或框架中吗?