Comment tester les tâches Gulp
Depuis que J'ai commencé à utiliser Gulp, mon projet a pris de l'ampleur. Maintenant, j'ai quelques tâches assez fantaisistes et maintenant je me demande peut-être que je devrais construire des tests unitaires pour garder un peu de santé mentale?
Existe-t-il un moyen simple et efficace de charger Gulpfile et de s'assurer que mes tâches font ce que je veux qu'elles fassent?
Quelqu'un a déjà testé ses scripts, ou c'est une perte de temps absolue?
4 réponses
Mon approche est de créer une instance de test et d'utiliser exec
et yeoman-affirmer. Bien que cela ressemble plus à un test d'intégration, je trouve utile de m'assurer que les tâches fonctionnent correctement ( mon cas d'utilisation étant un générateur yeoman). Certains (moka) exemple:
'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épertoire et qu'à tout le moins le fichier gulpfile est présent.
J'ai trouvé que regarder les tests unitaires de gulp plugins (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) n'est pas de tester les tâches gulp individuellement mais d'avoir des répertoires avec des fichiers source, de copier les fichiers source en place, d'exécuter gulp ('gulp build' comme commande shell) et de comparer les répertoires et les fichiers de sortie avec avec la tenue de la sortie correcte. –
TL; DR
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:
- tests unitaires, par exemple pour les conditions, les options, etc.
- tests D'intégration, p.ex. entrée de flux, sortie de Flux, etc.
Pour être sûr qu'il peut être testé facilement, je modularise tout de la tâche au plugin gulp.
Essai Unitaire
- créez des adaptateurs pour chaque plugin gulp.
- utilisez l'adaptateur dans le tâche modularisée.
- espionnez l'adaptateur et ajoutez la propriété debug sur le flux.
- affirmer toute condition et séquences de flux.
Test D'Intégration
- Construire l'entrée
- exécuter la tâche gulp
- sortie à l'emplacement temporaire
Réponse Très Longue
Assez le chit-chat, voici un test unitaire mélangé avec un exemple de test d'intégration en 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 le 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 task:
/* 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 il y a du code dupliqué. Mais j'espère que cela peut vous donner une démonstration de la façon dont j'ai testé ma tâche.
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 l'instance gulp et les plugins 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"
Bien que certains puissent prétendre que cette approche la rendrait encore plus complexe et qu'il est peut-être préférable de tout garder dans Gulpfile, mais cela a fonctionné pour moi, et il me semble presque que je peux vivre sans tests maintenant.