【问题标题】:How to test Gulp tasks如何测试 Gulp 任务
【发布时间】:2014-07-04 05:43:04
【问题描述】:

自从我开始使用 Gulp 后,我的项目变得更大了。现在我有一些非常奇特的任务,现在我想知道也许我应该构建一些单元测试来保持理智?

有没有一种简单又好用的方法来加载 Gulpfile 并确保我的任务正在做我希望他们做的事情?

有人测试过他们的脚本,还是完全浪费时间?

【问题讨论】:

  • 我从没想过构建脚本适合进行单元测试,因为没有真正可以断言的输入和输出。不过,我很想看看你的进展如何!
  • 我也不是,但正如我所说 - 脚本越来越重,一些任务变得繁琐。我想正确的做法是将一些任务扩展到单独的 gulp 插件中,并且可以并且应该对这些插件进行测试。

标签: unit-testing gulp


【解决方案1】:

我的方法是创建一个测试实例并使用exec 和yeoman-assert。虽然感觉更像是集成测试,但我发现确保任务正常运行很有帮助 (my use case being a yeoman-generator)。一些(摩卡)示例:

'use strict';
var path = require('path');
var helpers = require('yeoman-generator').test;
var assert = require('yeoman-generator').assert;
var exec = require('child_process').exec;
var fs = require('fs');
var injectStyles = require('../.test-instance/tasks/dev');


describe('gulp inject', function ()
{
    var instancePath = path.join(__dirname, '../.test-instance');
    var mainScss = path.join(instancePath, 'app/styles/main.scss');
    var gulpfile = path.join(instancePath, 'gulpfile.js');
    var gulp = '$(which gulp)';
    var injectStylesCmd = gulp + ' injectStyles';

    describe('scss partials in styles folder', function ()
    {
        var expectedContent = [
            [mainScss, /_variables/],
            [mainScss, /base\/_buttons\.scss/],
            [mainScss, /base\/_fonts\.scss/],
            [mainScss, /base\/_forms\.scss/],
            [mainScss, /base\/_icons\.scss/],
            [mainScss, /base\/_lists\.scss/],
            [mainScss, /base\/_page\.scss/],
            [mainScss, /base\/_tables\.scss/],
            [mainScss, /base\/_typography\.scss/],
            [mainScss, /functions\/_some-function\.scss/],
            [mainScss, /mixins\/_some-mixin\.scss/],
            [mainScss, /placeholders\/_some-placeholder\.scss/]
        ];
        var expected = [
            mainScss
        ];


        beforeEach(function (done)
        {
            this.timeout(10000);
            fs.truncateSync(mainScss);
            fs.writeFileSync(mainScss, '// inject:sass\n\n// endinject');
            exec(injectStylesCmd + ' injectStyles', {
                cwd: instancePath
            }, function (err, stdout)
            {
                done();
            });
        });

        it('creates expected files', function ()
        {
            assert.file([].concat(
                expected
            ));
            assert.fileContent([].concat(
                expectedContent
            ));
        });
    });
});

当然,您需要确保已设置测试实例。例如,您可以通过fs.writeFileSync 创建您的测试文件。在大多数情况下,您需要确保实例具有相同的目录结构,并且至少存在 gulpfile。

【讨论】:

【解决方案2】:

我发现从 gulp 插件(例如:https://github.com/jonkemp/gulp-useref/tree/master/test)查看单元测试是编写自己的一个很好的起点。

最终对我有用的解决方案(主要兴趣是构建过程)不是单独测试 gulp 任务,而是拥有包含源文件的目录,复制源文件,运行 gulp('gulp build' as shell 命令)并将输出目录和文件与另一个保存正确输出的目录进行比较。 -

【讨论】:

    【解决方案3】:

    TL;DR

    我确实为我的任务创建了测试,我为 gulp 任务创建了两种测试:

    • 单元测试,例如用于条件、选项等。
    • 集成测试,例如流输入、流输出等

    为了确保它可以轻松测试,我对从任务到 gulp 插件的所有内容进行了模块化。

    单元测试

    1. 为每个 gulp 插件创建适配器。
    2. 在模块化任务中使用适配器。
    3. 窥探适配器并在流上添加调试属性。
    4. 断言任何条件和流序列。

    集成测试

    1. 构建输入
    2. 运行 gulp 任务
    3. 输出到临时位置

    真的很长的答案

    闲聊已经够了,这里有一个单元测试和使用 jasmine 的集成测试示例:

    /* spec/lib/tasks/build_spec.js */
    var gulp     = require("gulp");
    var through2 = require("through2");
    var exec     = require("child_process").exec;
    var env      = require("../../../lib/env");
    var build    = require("../../../lib/tasks/build");
    var gulpif   = require("../../../lib/adapters/gulpif");
    var gzip     = require("../../../lib/adapters/gzip");
    var uglify   = require("../../../lib/adapters/uglify");
    
    describe("build", function(){
      it("stream to uglify then gzip if environment is production", function(testDone){
        var streamSequence = [];
    
        // 1. Stub condition and output path
        spyOn(env, "production").and.returnValue(true);
        spyOn(build, "dest").and.returnValue("./tmp");
    
        // 2. Stub debug message for stream sequence 
        spyOn(gulpif, "stream").and.callFake(function(env, stream){
          if (env.production()) streamSequence.push(stream.debug["stream-name"]);
          return through2.obj();
        });
        spyOn(uglify, "stream").and.callFake(function(){
          var stream = through2.obj();
          stream.debug = { "stream-name": "stream-uglify" };
          return stream;
        });
        spyOn(gzip, "stream").and.callFake(function(){
          var stream = through2.obj();
          stream.debug = { "stream-name": "stream-gzip" };
          return stream;
        });
    
        var stream = build.run();
        var url = "file://" + process.cwd() + "/tmp/resource.js";
        stream.on("end", function(){
    
          // 3. Assert stream sequence (unit test)
          expect(streamSequence).toEqual(["stream-uglify", "stream-gzip"]);
          exec("curl " + url, function(error, stdout, stderr){
    
            // 4. Assert stream output (integration)
            expect(eval.bind(Object.create(null), stdout)).not.toThrow();
            testDone();
          });
        });
      });
    });
    

    这是任务的模块示例:

    /* lib/tasks/build.js */
    var gulp   = require("gulp");
    var env    = require("../env");
    var gulpif = require("../adapters/gulpif");
    var gzip   = require("../adapters/gzip");
    var uglify = require("../adapters/uglify");
    
    var build = {
      dest: function(){
        return "./path/to/output";
      },
      run: function(){
        return gulp.src("./path/to/resource.js")
          .pipe(gulpif.stream(env.production(), uglify.stream()))
          .pipe(gulpif.stream(env.production(), gzip.stream()))
          .pipe(gulp.dest(this.dest()));
      }
    };
    
    module.exports = build;
    

    这里是适配器:

    /* lib/adapters/gulpif.js */
    var gulpif = require("gulp-if");
    var adapter = {
      stream: function(){
        return gulpif.apply(Object.create(null), arguments);
      }
    };
    module.exports = adapter;
    
    /* lib/adapters/gzip.js */
    var gzip = require("gulp-gzip");
    var adapter = {
      stream: function(){
        return gzip.apply(Object.create(null), arguments);
      }
    };
    module.exports = adapter;
    
    /* lib/adapters/uglify.js */
    var gzip = require("gulp-uglify");
    var adapter = {
      stream: function(){
        return uglify.apply(Object.create(null), arguments);
      }
    };
    module.exports = adapter;
    

    这是演示条件测试的环境:

    /* lib/env.js */
    var args = require("yargs").argv;
    var env = {
      production: function(){
        return (args.environment === "production");
      }
    }
    

    最后,这里是使用任务模块示例的任务:

    /* tasks/build.js */
    var gulp = require("gulp");
    var build = require("./lib/tasks/build");
    gulp.task("build", function(){
      build.run();
    });
    

    我知道它并不完美,并且有一些重复的代码。但我希望这可以让您演示我是如何测试我的任务的。

    【讨论】:

      【解决方案4】:

      降低复杂性的一种方法可能是将任务模块化并将它们放在单独的文件中。在这种情况下,您可能需要共享 gulp 实例和 gulp 插件。我是这样做的:

      在Gulpfile.coffee:

      gulp = require 'gulp'
      $ = require('gulp-load-plugins')()
      
      require('./src/build/build-task')(gulp, $)
      
      gulp.task "default", ['build']
      

      在./src/build/build-task.coffee:

      module.exports = (gulp, $)->    
         gulp.task "build",->
             $.util.log "running build task"
      

      虽然有些人可能会争辩说这种方法会使它变得更加复杂,最好将所有内容都保存在 Gulpfile 中,但它对我很有用,而且几乎感觉我现在可以不用测试了。

      【讨论】:

      • 这是一个很好的做法,但不涉及测试 gulp 任务。
      • 为什么这是公认的答案?它没有回答如何实际测试 gulp 的问题。 Hendra Uzia 的答案看起来是一个完整的答案。
      猜你喜欢
      • 1970-01-01
      • 2016-09-25
      • 1970-01-01
      • 1970-01-01
      • 2017-09-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-14
      相关资源
      最近更新 更多