如何测试Gulp任务

时间:2014-07-04 05:43:04

标签: unit-testing gulp

自从我开始使用Gulp以来,我的项目变得越来越大。现在我有一些相当奇特的任务,现在我想知道我是否应该建立一些单元测试以保持一定的理智?

是否有一种简单的方法来加载Gulpfile并确保我的任务正在执行我希望他们做的事情?

有人曾测试过他们的剧本,还是绝对浪费时间?

4 个答案:

答案 0 :(得分:13)

我的方法是创建一个测试实例并使用execyeoman-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创建测试文件。在大多数情况下,您需要确保实例具有相同的目录结构,并且至少存在gulp文件。

答案 1 :(得分:7)

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

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

答案 2 :(得分:4)

TL; DR

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

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

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

单元测试

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

    1. 构建输入
    2. 运行gulp任务
    3. 输出到临时位置
    4. 真的很长的答案

      足够的闲聊,这里是一个单元测试与使用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();
      });
      

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

答案 3 :(得分:0)

降低复杂性的方法可能是模块化任务并将它们放在单独的文件中。在这种情况下,您可能需要共享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中可能会更好,但它对我有用,而且几乎感觉我现在可以忍受没有测试。