# AngularJS - testing
# tools
- karma runner
- jasmine
- PhantomJS
- karma-htmlfile-reporter
- karma-junit-reporter
- karma & jenkins
- mockaroo : online mock file generator
# unit testing
# best articles
- angular-tips.com : unit-test
- introduction-aux-tests-unitaires-en-JavaScript
- Les 10 commandements des tests unitaires
- rater-ses-tests-unitaires-en-toutes-circonstances
# best lib (ng-describe)
- ng-describe
- Avec ng-describe, quand on mock une constante utilisée pour stocker une lib externe ( cf Y240 ), mocker la constante ne suffit pas, il faut mocker chaque fonction utilisée en lui réattribuant la fonction initiale. On injecte par IIFE la vrai lib donc on réattribue au mock les fonctions utilisées. Ou pas.
Articles de l'auteur de la lib :
- Unit testing AngularJS code in record time using ng-describe : glebbahmutov.com/
- Slides AngularJS NYC meetup in August 2015
- testing-angularjs-under-node : glebbahmutov.com/
- testing-angular-async-stuff: glebbahmutov.com
# intégration jasmine dans webstorm
stackoverflow - how-can-i-get-webstorm-to-recognize-jasmine-methods
stackoverflow - Selection jasmine definitely typed
# examples
# repos examples
Guidelines and patterns for unit testing AngularJS apps.
Unit and e2e testing recipes for AngularJS
# tests avec $http
# tests de services
# tests de directives
# Dependencies mocking
- mocking-angular-module-dependencies
- injecting-a-mock-into-an-angularjs-service
- divers
- Dummy, Fake, Stub, Mock et Spy, les tests unitaires avec l'aide de Moq
# Testing a throw exception
it('throws exception when args are undefined or null', inject(function (HistoryLRindexService) {
expect(function(){HistoryLRindexService.addIndexElement(undefined, 'toto');})
.toThrowError('HistoryLRindexValueFactory.addIndexElement(key, data) error : key null or undefined');
}));
2
3
4
use anonymous function for the call
use
toThrowError(exceptionMsg)
matcher from Jasmineyour impl should be something like that :
if(!key) {
throw new Error('HistoryLRindexValueFactory.addIndexElement(key, data) error : key null or undefined');
}
2
3
# Testing a directive
(function () {
'use strict';
angular
.module('app')
.directive('exampleDirective', exampleDirective);
function exampleDirective() {
return {
restrict: 'E',
scope: {
stroke: "@",
fill: "@"
},
template: '<svg ng-attr-height="{{values.canvas}}" ng-attr-width="{{values.canvas}}" class="gray">' +
'<circle ng-attr-cx="{{values.center}}" ng-attr-cy="{{values.center}}"' +
'ng-attr-r="{{values.radius}}" stroke="{{stroke}}"' +
'stroke-width="3" fill="{{fill}}" />' +
'</svg>',
link: function(scope, element, attrs) {
var calculateValues = function(size) {
var canvasSize = size * 2.5;
scope.values = {
canvas: canvasSize,
radius: size,
center: canvasSize / 2
};
};
attrs.$observe('size', function(newSize) {
calculateValues(parseInt(newSize, 10));
});
}
};
}
}());
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
(function () {
'use strict';
describe('directive: example', function() {
var element, scope;
beforeEach(module('app'));
beforeEach(inject(function($rootScope, $compile) {
scope = $rootScope.$new();
element =
'<example-directive size="{{size}}" stroke="black" fill="blue"></example-directive>';
scope.size = 100;
element = $compile(element)(scope);
scope.$digest();
}));
describe('with the first given value', function() {
it("should compute the size to create other values", function() {
var isolated = element.isolateScope();
expect(isolated.values.canvas).toBe(250);
expect(isolated.values.center).toBe(125);
expect(isolated.values.radius).toBe(100);
});
it("should contain a svg tag with proper size", function() {
expect(element.find('svg').attr('height')).toBe('250');
expect(element.find('svg').attr('width')).toBe('250');
});
it("should contain a circle with proper attributes", function() {
expect(element.find('circle').attr('cx')).toBe('125');
expect(element.find('circle').attr('cy')).toBe('125');
expect(element.find('circle').attr('r')).toBe('100');
expect(element.find('circle').attr('stroke')).toBe('black');
expect(element.find('circle').attr('fill')).toBe('blue');
});
});
describe('when changing the initial value to a different one', function() {
beforeEach(function() {
scope.size = 160;
scope.$digest();
});
it("should compute the size to create other values", function() {
var isolated = element.isolateScope();
expect(isolated.values.canvas).toBe(400);
expect(isolated.values.center).toBe(200);
expect(isolated.values.radius).toBe(160);
});
it("should contain a svg tag with proper size", function() {
expect(element.find('svg').attr('height')).toBe('400');
expect(element.find('svg').attr('width')).toBe('400');
});
it("should contain a circle with proper attributes", function() {
expect(element.find('circle').attr('cx')).toBe('200');
expect(element.find('circle').attr('cy')).toBe('200');
expect(element.find('circle').attr('r')).toBe('160');
expect(element.find('circle').attr('stroke')).toBe('black');
expect(element.find('circle').attr('fill')).toBe('blue');
});
});
});
}());
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# e2e testing
- angularjs-e2e-testing-using-ngmocke2e
- locators protractors
- page objects
- protractor-styleguide
- phantomjs not good for e2e
# installation des drivers pour webdrivers
ATTENTION CHROME & FIREFOX, need dl update de webdriver.
Derrière un proxy il faut config envvar HTTP_PROXY
& HTTPS_PROXY
sur fiddler (http://localhost:8888
)
et set ignore_ssl
à true si le magasin de certificat du réseau est fucké. Pour le moment on sait le faire que en dur dans le source de webdriver-manager ( .\node_modules\gulp-protractor\node_modules\protractor\bin\webdriver-manager
)
ligne 93 (default('ignore_ssl', false)
.)
ignore_ssl
for protractor issue 1847.- proxy config + ignore_ssl for webdriver-manager, issue 1477.
ATTENTION IE :
pour exécution sur IE need drivers spécifiques.
(dépendent de la plateforme, sur win x64 : IEDriverServer_x64_2.47.0.zip
)
Config à faire ensuite dans protractor.conf.js
pour lancer sur ce server (géré au niveau du generator)
# lancer la suite de test sur chaque browser
multiCapabilities: [{
'browserName': 'firefox'
}, {
'browserName': 'chrome'
}, {
'browserName': 'ie'
}],
2
3
4
5
6
7
8
# tests pour ie fail
UnknownError: The path to the driver executable must be set by the webdriver.ie.driver system property; for more information, see http://code.google.com/p/selenium/wiki/InternetExplorerDriver. The latest version can be downloaded from http://selenium-release.storage.googleapis.com/index.html
IE nécessite un driver spécifique et une config spécifique.
Dans gulp-protractor
les drivers webdriver de IE ne sont pas DL, cf issue 38
Télécharger les drivers IE manuellement ici.
Doc du driver ici.
Installer l'exe dans la partie exécutable du disque dur.
Ajouter de la config à protractor.conf.js :
seleniumArgs: [
'-Dwebdriver.ie.driver=C:\\Produits\\dev\\ws-js\\webdriver-ie\\IEDriverServer.exe'
],
2
3
Ajouter la capability à la config de protractor.conf.js
:
multiCapabilities: [{
'browserName': 'firefox'
}, {
'browserName': 'chrome'
}, {
'browserName': 'internet explorer',
'platform': 'ANY',
'version': '11'
}],
2
3
4
5
6
7
8
9
# exporter les résultats
resultJsonOutputFile: 'e2e.results.json',
Nécessite que jasmineNodeOpts.isVerbose soit à true.
# reporter
- Jasmine reporter
- Discussion dans les issues de protractor
installation :
npm install jasmine-reporters@^2.0.7 --save-dev
Dans protractor.conf.js
, ajouter :
var jasmineReporters = require('jasmine-reporters');
exports.config = {
// ...
framework: 'jasmine2',
// ...
onPrepare: function () {
jasmine.getEnv().addReporter(new jasmineReporters.JUnitXmlReporter({
savePath: paths.e2e + '/reports/',
consolidateAll: true,
filePrefix: 'e2e.chrome.results.' + _currentTimestampToString()
}));
// ...
// Options to be passed to Jasmine-node.
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 60000,
print: function() {}
}
},
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Va créer le rapport dans ../e2e/reports/
Le rapport est un fichier au format xml qui nécessite une mise en forme. Cette xslt peut être utilisée pour mettre en forme le xml d'output mais manque un moyen pour linker automatiquement le xml de sortie avec la xsl. (pas prévu par le module)