I am trying to write unit tests for my Angular.js application but I cannot manage to inject what I need (it is not able to find a suitable provider).
Does anyone see what I missed?
Firefox 21.0 (Linux) filter staticList should convert static list object into its display value FAILED
Error: Unknown provider: staticListProvider <- staticList in /path/to/my-app/public/third-party/angular/angular.js (line 2734)
createInjector/providerInjector<#/path/to/my-app/public/third-party/angular/angular.js:2734
getService#/path/to/my-app/public/third-party/angular/angular.js:2862
createInjector/instanceCache.$injector<#/path/to/my-app/public/third-party/angular/angular.js:2739
getService#/path/to/my-app/public/third-party/angular/angular.js:2862
invoke#/path/to/my-app/public/third-party/angular/angular.js:2880
workFn#/path/to/my-app/test/lib/angular/angular-mocks.js:1778
angular.mock.inject#/path/to/my-app/test/lib/angular/angular-mocks.js:1764
#/path/to/my-app/test/unit/filtersSpec.js:19
#/path/to/my-app/test/unit/filtersSpec.js:16
#/path/to/my-app/test/unit/filtersSpec.js:3
The application:
angular.module('myApp', ['myAppFilters', 'ui.bootstrap', '$strap.directives']).
// Some other stuff
The filters:
"use strict";
angular.module('myAppFilters', []).
filter('staticList', function () {
return function (listItem) {
if (!listItem) {
return '';
}
return listItem.value;
};
});
The test:
'use strict';
describe('filter', function () {
beforeEach(angular.module('myAppFilters'));
describe('staticList', function () {
it('should convert static list object into its display value',
inject(function (staticList) {
expect(undefined).toBe('');
expect({key: 'A', value: 'B'}).toBe('B');
}));
});
});
The Karma configuration:
basePath = '../';
files = [
JASMINE,
JASMINE_ADAPTER,
'public/third-party/jquery/*.js',
'public/third-party/angular/angular.js',
'public/third-party/angular/i18n/angular-*.js',
'public/third-party/moment/moment.min.js',
'public/third-party/moment/moment-*.js',
'public/js/**/*.js',
'test/lib/**/*.js',
'test/unit/**/*.js'
];
colors = true;
autoWatch = true;
browsers = ['Firefox'];
junitReporter = {
outputFile: 'test_out/unit.xml',
suite: 'unit'
};
If anybody wants to see the full code, the application repository is here: https://github.com/adericbourg/GestionCourrier
Thanks a lot,
Alban
In your inject code
it('should convert static list object into its display value',
inject(function (staticList) {
expect(undefined).toBe('');
expect({key: 'A', value: 'B'}).toBe('B');
}));
replace "inject(function (staticList)" with "inject(function (staticListFilter)". This is some random convention angular is following. You can check comments on this page for more info http://docs.angularjs.org/tutorial/step_09
I faced similar problem, but in my case my filter name contained suffix 'Filter' which caused the same injection problem.
.filter('assetLabelFilter', function(){
return function(assets, selectedLabels){
// Implementation here
};
});
I was finally able to solve the problem by manually injecting the filter to my test
'use strict';
describe('assetLabelFilter', function() {
beforeEach(module('filters.labels'));
var asset1 = {labels: ['A']};
var asset2 = {labels: ['B']};
var asset3 = {labels: []};
var asset4 = {labels: ['A', 'B']};
var assets = [asset1, asset2, asset3, asset4];
var assetLabelFilter;
beforeEach(inject(function($filter) {
assetLabelFilter = $filter('assetLabelFilter');
}));
it('should return only assets with selected label', function() {
var selectedLabels = ['B'];
expect(assetLabelFilter(assets, selectedLabels)).toEqual([asset2, asset4]);
});
});
The nice answer above made me realize that in order to use the angular tutorial way:
it('should ', inject(function(assetLabelFilter) {
var selectedLabels = ['B'];
expect(assetLabelFilter(assets, selectedLabels)).toEqual([asset2, asset4]);
}));
The filter name can't have the suffix 'Filter' because it is magic word as mentioned in the answer above.
To demonstrate the scenario I also created a Plunker
Related
So I'm unit testing a component I made with Angular 1.5. For security purposes, I can't copy and paste that code here, but I can give some mocks.
It's a simple component that has some basic selection action, with an binding event when something is selected.
So the code would look like this:
angular
.module( "myModule" )
.component( "myComponent", {
template: "Some random HTML Template",
bindings: {
onSelect: "&"
},
controller: function () {
var ctrl = this;
ctrl.$onInit = init;
ctrl.selection = selection;
function init() {} function selection() {
ctrl.onSelect(ctrl.selected) }
} } );
That's the basic component. I'm trying to unit test it, with a format such as this:
describe( "Component Test", function() {
beforeEach (module( "myModule") );
var ctrl, onSelectSpy;
beforeEach (inject( function( _$componentController_) {
ctrl = {}
onSelectSpy = jasmine.createSpy("onSelect");
var $componentController = _$componentController_;
ctrl = $componentController("myComponent", null, { onSelect:
onSelectSpy});
} ) );
it ("Controller defined", function() {
ctrl.$onInit();
expect ( ctrl ).toBeDefined();
});
} );
When I try running a test that looks very similar to this, I get this error:
Unknown provider: myComponentDirectiveProvider <- myComponentDirective
And it gives an angular url about the line where it fails.
Not sure why it's not getting defined and why this isn't working. I thought components were directives.
Using Angular 1.5.8 and Angular-mocks 1.5.8
In the application building tutorial on angularjs.org, step-8, testing part, what does the following lines of code mean-
element.all(by.css('.phones li a')).first().click();
expect(browser.getLocationAbsUrl()).toBe('/phones/nexus-s');
Thanks in advance!
PS:
The exact URL is- https://docs.angularjs.org/tutorial/step_08 and the code file (scenarios.js) is-
'use strict';
// Angular E2E Testing Guide:
// https://docs.angularjs.org/guide/e2e-testing
describe('PhoneCat Application', function() {
describe('phoneList', function() {
beforeEach(function() {
browser.get('index.html');
});
it('should filter the phone list as a user types into the search box', function() {
var phoneList = element.all(by.repeater('phone in $ctrl.phones'));
var query = element(by.model('$ctrl.query'));
expect(phoneList.count()).toBe(20);
query.sendKeys('nexus');
expect(phoneList.count()).toBe(1);
query.clear();
query.sendKeys('motorola');
expect(phoneList.count()).toBe(8);
});
it('should be possible to control phone order via the drop-down menu', function() {
var queryField = element(by.model('$ctrl.query'));
var orderSelect = element(by.model('$ctrl.orderProp'));
var nameOption = orderSelect.element(by.css('option[value="name"]'));
var phoneNameColumn = element.all(by.repeater('phone in $ctrl.phones').column('phone.name'));
function getNames() {
return phoneNameColumn.map(function(elem) {
return elem.getText();
});
}
queryField.sendKeys('tablet'); // Let's narrow the dataset to make the assertions shorter
expect(getNames()).toEqual([
'Motorola XOOM\u2122 with Wi-Fi',
'MOTOROLA XOOM\u2122'
]);
nameOption.click();
expect(getNames()).toEqual([
'MOTOROLA XOOM\u2122',
'Motorola XOOM\u2122 with Wi-Fi'
]);
});
it('should render phone specific links', function() {
var query = element(by.model('$ctrl.query'));
query.sendKeys('nexus');
element.all(by.css('.phones li a')).first().click();
expect(browser.getLocationAbsUrl()).toBe('/phones/nexus-s');
});
});
});
It is testing of the routing to /phones/nexus-s.
It is written in Protractor.
The first line reads the DOM and finds all the .phones li a css rules. It then takes only the first one and calls click() on it.
element.all(by.css('.phones li a')).first().click();
The second line expects the output of the function browser.getLocationAbsUrl() to be the string /phone/nexus-s
expect(browser.getLocationAbsUrl()).toBe('/phones/nexus-s');
So all in all the test framework clicks a button and expects it to be routed to a new page.
We use js-data and js-data-angular in our project.
I have the following model:
(function () {
'use strict';
angular.module('dash.models')
.factory('Diagnosis', ['DS', function (DS) {
function transform(resourcename, attrs, cb) {
attrs.icd9codes.forEach(function (el) {
delete el.add;
});
cb(null, attrs);
}
this.transform = transform;
return DS.defineResource({
name: 'diagnosis',
idAttribute: 'id',
endpoint: '/diagnosis',
baseUrl: '/api',
beforeCreate: transform,
beforeUpdate: transform
});
}]);
}());
And the following call to said model:
var startEditing = self.startEditing = function(parentScope, diagnosis) {
Diagnosis.findAll({
deep:true
}, {
endpoint: '/diagnosis/' + diagnosis.id
}).then(function(d) {
$scope.diagnosis = d;
$scope.inScope = true;
});
};
In my unit test, I mock the call like this:
var diagDeferred = _$q_.defer();
diagDeferred.resolve({
'name': 'Breast',
'categories': null,
'id': '026c7cd0-14ef-4312-a8f1-2092107b0e50',
'icd9codes': [{id: '1', code: '001', description: 'ICD9 Code'}]
});
spyOn(Diagnosis, 'findAll').and.returnValue(diagDeferred.promise);
And the actual call is mocked, what doesn't get executed (and I can't find any reliable information on how to get this done) is the function inside the .then of the Diagnosis.findAll
I know the code works, but I need to cover it with unit tests and I'm coming up dry.
Thanks.
I think you forgot to call $scope.digest() in your test. Here is a working fiddle.
After you call startEditing(), you should call $scope.$digest() so that your mock promise is executed and you can get your data in then block. Hope it helps.
First of all I want to say that I am new in Jasmine, so I beg for your kind comprehension if the question is very basic. I am writing a test for this file:
define([
'q',
'backbone',
'marionette',
'education/eet/views/destinationview',
'education/eet/views/editdestinationview',
'education/eet/models/destination',
'common/ajaxerrorhandler',
'common/alertdialog'
], function (Q, Backbone, Marionette, DestinationView, EditDestinationView, Destination, AjaxErrorHandler, AlertDialog) {
'use strict';
var ReferenceDataController = Marionette.Controller.extend({
initialize: function (options) {
this._subjectCompositeId = options.subjectCompositeId;
},
getView: function (destinationTypes, editMode) {
var self = this,
deferred = Q.defer(),
destination = new Destination();
destination.fetch({
data: {subjectCompositeId: self._subjectCompositeId}
}).done(function () {
var view;
if (editMode) {
view = new EditDestinationView({
model: destination,
'destinationTypes': destinationTypes
});
view.on('click:saveDestination', self._handleSaveDestination, view);
} else {
view = new DestinationView({
model: destination
});
}
deferred.resolve(view);
}).fail(function (jqXHR) {
deferred.reject(jqXHR);
});
return deferred.promise;
},
_handleSaveDestination: function () {
if (this.model.isValid(true)) {
this.model.save(null, {
success: function () {
Backbone.Wreqr.radio.vent.trigger('education', 'show:destination');
},
error: function (jqXHR) {
var userFriendlyErrorString = AjaxErrorHandler.buildDefaultErrorMessage(jqXHR);
return new AlertDialog(userFriendlyErrorString);
}
});
}
}
});
return ReferenceDataController;
});
The problem is that I am not very sure about how can I access the variables inside it to test it. I am a Java Tester but never test Javascript even when I wrote, so I am very confused with it.
Any hint or code will be actually appreciated.
Thanks.
Think of Jasmine suite/spec as your application that is dependent on this module.
We do our specs as RequireJS modules that require the appropriate module, instantiate it - sometimes on module level, sometimes on suite (describe) level, sometimes on spec (it) level.
At this point, due to you (in it) having an access to an actual instance of the class, you invoke its various methods and test for the results using various asserts in the form of
expect(something).toBeTruthy();
or similar.
I have simple filter which depends of moment.js:
app.filter('fromNow', function() {
return function(date) {
return moment(date).fromNow();
}
});
Have can i write unit test of this in jasmine ?
EDIT:
now i have
ReferenceError: moment is not defined
when write like that:
describe("fromNow filter", function(){
var moment;
beforeEach(function(){
module('reports');
moment = jasmine.createSpy();
});
it("should output string when input string",
inject(function(fromNowFilter) {
fromNowFilter("string");
}));
})
You need to add moment.js to the testing framework.
I had the same problem and fixed adding the following line to my karma.conf.js
...files: [
....
'app/bower_components/moment/moment.js',
....
],
.....