Rappelez-vous ce qui est le point de test unitaire est: la garantie d'un module particulier de code réagit à certains stimuli attendu. En JS, une partie importante de votre code, (sauf si vous avez quelques cadre du cycle de vie comme le Sencha ou YUI) sera directement manipulation du DOM ou de faire des appels à distance. Pour tester ces choses, vous appliquez simplement traditionnelle de l'unité de techniques de dépistage de l'injection de dépendance et se moquant/buter. Cela signifie que vous devez écrire chaque fonction ou de la classe, que vous voulez des tests unitaires pour accepter se moque de la structure dépendante.
jQuery prend en charge ce en vous permettant de passer d'un document XML dans toute la traversée de fonctions. Alors que vous pourriez écrire normalement
$(function() { $('.bright').css('color','yellow'); }
vous aurez plutôt envie d'écrire
function processBright(scope) {
// jQuery will do the following line automatically, but for sake of clarity:
scope = scope || window.document;
$('.bright',scope).css('color','yellow');
}
$(processBright);
Avis nous n'avons pas seulement tirer la logique de la fonction anonyme et lui donner un nom, nous faisons aussi que la fonction accepte un paramètre d'étendue. Lorsque cette valeur est null, le jQuery, les appels continuent de fonctionner normalement. Cependant, nous avons maintenant un vecteur pour l'injection d'un simulacre de document que l'on peut inspecter une fois que la fonction est appelée. L'unité de test pourrait ressembler à
function shouldSetColorYellowIfClassBright() {
// arrange
var testDoc =
$('<html><body><span id="a" class="bright">test</span></body></html>');
// act
processBright(testDoc);
// assert
if (testDoc.find('#a').css('color') != 'bright')
throw TestFailed("Color property was not changed correctly.");
}
TestFailed pourrait ressembler à ceci:
function TestFailed(message) {
this.message = message;
this.name = "TestFailed";
}
La situation est similaire avec les appels distants, mais plutôt que l'injection de certains de facilité, vous pourriez vous en sortir avec un masque de stub. Disons que vous disposez de cette fonction:
function makeRemoteCall(data, callback) {
if (data.property == 'ok')
$.getJSON({url:'/someResource.json',callback:callback});
}
Vous pouvez tester en tant que tel:
// test suite setup
var getJSON = $.getJSON;
var stubCalls = [];
$.getJSON = function(args) {
stubCalls[stubCalls.length] = args.url;
}
// unit test 1
function shouldMakeRemoteCallWithOkProperty() {
// arrange
var arg = { property: 'ok' };
// act
makeRemoteCall(arg);
// assert
if (stubCalls.length != 1 || stubCalls[0] != '/someResource.json')
throw TestFailed("someResource.json was not requested once and only once.");
}
// unit test 2
function shouldNotMakeRemoteCallWithoutOkProperty() {
// arrange
var arg = { property: 'foobar' };
// act
makeRemoteCall(arg);
// assert
if (stubCalls.length != 0)
throw TestFailed(stubCalls[0] + " was called unexpectedly.");
}
// test suite teardown
$.getJSON = getJSON;
(Vous pouvez emballer tout ça dans le module de modèle à pas de la litière de l'espace de noms global.)
Pour appliquer tout cela dans un test-driven manière, vous pouvez tout simplement écrire ces premiers essais. C'est simple, sans fioritures, et surtout, de manière efficace de tests unitaires JS.
Des cadres comme qUnit peut être utilisé pour piloter votre appareil-tests, mais c'est seulement une petite partie du problème. Votre code doit être écrit dans un essai de manière conviviale. Aussi, les cadres comme le Sélénium, l'interface htmlunit, jsTestDriver ou Watir/N sont pour les tests d'intégration, pas de tests unitaires en soi. Enfin, en aucun cas votre code orienté objet. Les principes de tests unitaires sont facilement confondus avec ceux de l'application pratique des tests unitaires dans un système orienté objet. Ils sont séparés mais compatible idées.
Les Tests De Styles
Il est à noter que deux de test différents styles sont illustrés ici. La première suppose une ignorance complète de la mise en œuvre de processBright. Il pourrait être l'utilisation de jQuery pour ajouter de la couleur, de style, ou peut-être faire le natif de manipulation du DOM. Je suis contente de tester le comportement externe de la fonction est comme prévu. Dans la deuxième, je suppose la connaissance d'un interne de la dépendance de la fonction (à savoir $.getJSON), et ces tests couvrent la bonne interaction avec cette dépendance.
L'approche que vous prenez dépend de votre test de la philosophie et de l'ensemble des priorités et des coûts-avantages de profil de votre situation. Le premier test est relativement pur. Le deuxième test est simple, mais relativement fragile, si je change la mise en œuvre de makeRemoteCall, le test va se casser. De préférence, l'hypothèse que makeRemoteCall utilise $.getJSON est au moins justifiées par la documentation de makeRemoteCall. Il ya un couple de plus de discipline approches, mais un rapport coût-efficacité de l'approche consiste à envelopper les dépendances dans les fonctions wrapper. Le code de base dépendra uniquement sur ces wrappers, dont les implémentations peuvent être facilement remplacés par des bouts de test au test-temps.