How to unit test angularjs form?
-
21-12-2019 - |
Frage
I have been learning AngularJS and things have been going pretty smoothly regarding unit testing, but I've reached a bit of a tricky spot.
Suppose I have a simple form, for example:
<form name="form">
<input type="text" name="number" ng-pattern="/^d+$/">
</form>
If I was testing something like a controller, I know that I would write it something like this (using Jasmine + Karma):
beforeEach(module('some.module'));
beforeEach(inject(/* services */) {
/* inject necessary services */
});
it('should be invalid when given bad input', function () {
form.number = 'Not a number';
expect(form.number.$valid).toBeFalsy();
expect(form.$valid).toBeFalsy();
});
But I don't know which services I need to inject, and I haven't had any luck finding documentation on unit testing in either the forms
guide or the ng-form
documentation.
How does one unit test a form in Angular?
Lösung
I'm not convinced this is the best way to unit test something like this but with some help from this answer on testing custom angular directives and some experimentation, I figured out a way to unit test the form.
After installing karma-ng-html2js-preprocessor
and configuring it, I managed to get a working unit test like this:
var scope, form;
beforeEach(function() {
module('my-module');
module('templates');
});
beforeEach(inject($rootScope, $controller, $templateCache, $compile) {
scope = $rootScope.$new()
ctrl = $controller('MyController'), {
"$scope": scope
}
templateHtml = $templateCache.get('path/to/my/template.html')
formElem = angular.element("<div>" + templateHtml + "</div>")
$compile(formElem)(scope)
form = scope.form
scope.$apply()
}
it('should not allow an invalid `width`', function() {
expect(form.$valid).toBeTruthy();
form.number.$setViewValue('BANANA');
expect(form.number.$valid).toBeFalsy()
});
Andere Tipps
I guess i can add some details to the accepted answer: karma-ng-html2js-preprocessor should be configured in the karma.conf.js file in a similar way:
//karma.conf.js
ngHtml2JsPreprocessor: {
moduleName: 'templates'
},
files: [
//... other files
//my templates
'app/**/*.html'
],
preprocessors: {
'app/**/*.html': ['ng-html2js']
},
plugins: [
//... other plugins
"karma-ng-html2js-preprocessor"
]
Here's a way to unit test with an angular form without having to compile a controller's template. Works well for me in my limited usage.
describe('Test', function() {
var $scope, fooController;
beforeEach(function($rootScope, $controller, formDirective) {
$scope = $rootScope.$new();
fooController = $controller('fooController', {$scope: $scope});
// we manually create the form controller
fooController.form = $controller(formDirective[0].controller, {
$scope: $scope,
$element: angular.element("<form></form>"),
$attrs: {}
});
});
it('should test something', function() {
expect(fooController.form.$valid).toBeFalsy();
});
});
Alternatively, if you are using WebPack with karma-webpack
- you can include the template with require
, without the need of karma-ng-html2js-preprocessor
package:
describe("MyCtrl's form", function () {
var $scope,
MyCtrl;
beforeEach(angular.mock.module("my.module"));
beforeEach(inject(function (_$rootScope_, _$controller_, _$compile_) {
$scope = _$rootScope_.$new();
// Execute the controller's logic
// Omit the ' as vm' suffix if you are not using controllerAs
MyCtrl = _$controller_("MyCtrl as vm", { $scope: $scope });
// Compile the template against our scope to populate form variables
var html = require("./my.template.html"),
template = angular.element(html);
_$compile_(template)($scope);
}));
it('should be invalid when given bad input', function () {
MyCtrl.form.number.$setViewValue('Not a number');
expect(MyCtrl.form.number.$valid).toBeFalsy();
expect(MyCtrl.form.$valid).toBeFalsy();
});
});
HTML:
<form name="vm.form">
<input type="text" name="number" ng-pattern="/^d+$/">
</form>