Depuis que j'ai commencé à utiliser Gulp, mon projet a pris de l'ampleur. Maintenant, j’ai quelques tâches assez sophistiquées et je me demande maintenant si je devrais peut-être créer des tests unitaires pour conserver un peu de santé mentale?
Existe-t-il un moyen simple et efficace de charger Gulpfile et de m'assurer que mes tâches font ce que je veux qu'elles fassent?
Quelqu'un a déjà testé ses scripts, ou est-ce une perte de temps absolue?
Un moyen de réduire la complexité peut être de modulariser les tâches et de les placer dans des fichiers séparés. Dans ce cas, vous devrez peut-être partager des plugins d'instance et de gulp. Je l'ai fait de cette façon:
dans Gulpfile.coffee
:
gulp = require 'gulp'
$ = require('gulp-load-plugins')()
require('./src/build/build-task')(gulp, $)
gulp.task "default", ['build']
dans ./src/build/build-task.coffee
:
module.exports = (gulp, $)->
gulp.task "build",->
$.util.log "running build task"
Certains diront que cette approche compliquerait encore les choses et qu'il serait peut-être préférable de tout conserver dans Gulpfile, mais cela a fonctionné pour moi et j'ai presque l'impression que je peux vivre sans test maintenant.
Mon approche consiste à créer une instance de test et à utiliser exec
et yeoman-assert . Bien que cela ressemble davantage à un test d'intégration, je trouve utile de s'assurer que les tâches fonctionnent correctement ( mon cas d'utilisation étant un véritable générateur ). Quelques exemples (moka):
'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
));
});
});
});
Bien sûr, vous devez vous assurer que vous avez une instance de test configurée. Vous pouvez créer vos fichiers de test via fs.writeFileSync
par exemple. Dans la plupart des cas, vous devez vous assurer que l'instance a la même structure de répertoires et qu'au minimum le fichier gulp est présent.
J'ai trouvé que regarder des tests unitaires à partir de plugins gulp (ex: https://github.com/jonkemp/gulp-useref/tree/master/test ) est un bon point de départ pour écrire le vôtre.
La solution qui a fonctionné pour moi à la fin (l'intérêt principal était le processus de construction) ne consiste pas à tester les tâches gulp individuellement, mais à disposer de répertoires avec les fichiers source, de copier les fichiers source à la place, d'exécuter gulp ('gulp build' en tant que commande Shell) et comparez les répertoires et les fichiers de sortie avec un autre répertoire en conservant la sortie correcte. -
Je crée des tests pour ma tâche, il y a 2 types de tests que j'ai créés pour les tâches gulp:
Pour être sûr de pouvoir le tester facilement, je modularise tout, de la tâche au plugin gulp.
Test unitaire
Test d'intégration
Assez parlé, voici un test unitaire mélangé à un exemple de test d'intégration utilisant 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();
});
});
});
});
Voici l'exemple de module pour la tâche:
/* 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;
Voici les adaptateurs:
/* 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;
Voici l'environnement pour démontrer les tests de condition:
/* lib/env.js */
var args = require("yargs").argv;
var env = {
production: function(){
return (args.environment === "production");
}
}
Et enfin, voici la tâche qui utilise l'exemple du module de tâche:
/* tasks/build.js */
var gulp = require("gulp");
var build = require("./lib/tasks/build");
gulp.task("build", function(){
build.run();
});
Je sais que ce n'est pas parfait et qu'il y a du code dupliqué. Mais j'espère que cela vous montrera comment j'ai testé ma tâche.