unable to inject module/service for unit test - angularjs

I am new to TDD and am trying to wire up a test, and have been stuck on it for hours. I keep getting the following error:
[$injector:modulerr] Failed to instantiate module AuthInterceptor due to:
Error: [$injector:nomod] Module 'AuthInterceptor' is not available! You either misspelled the module name or forgot to load it. If registering a module ensure that you specify the dependencies as the second argument.
http://errors.angularjs.org/1.5.8/$injector/nomod?p0=AuthInterceptor
at client/test/index.js:8237:13
at client/test/index.js:10251:18
at ensure (client/test/index.js:10175:39)
at module (client/test/index.js:10249:15)
at client/test/index.js:12786:23
at forEach (client/test/index.js:8490:21)
at loadModules (client/test/index.js:12770:6)
Here is my test:
import angular from 'angular';
import serviceModule from './auth.interceptor'
describe('wire.common.services', () => {
describe('AuthService', () => {
let AuthService;
beforeEach(angular.mock.module(serviceModule.name));
beforeEach(angular.mock.module(($provide) => {
$provide.factory('$q', () => ({}));
$provide.factory('$log', () => ({}));
}));
beforeEach(angular.mock.inject((_AuthService_) => {
AuthService = _AuthService_;
}));
it('should be a dummy test', () => {
expect(2).toEqual(2);
});
});
});
The actual code I'm testing:
export default function AuthInterceptor($q, $injector, $log) {
'ngInject';
return {
request(config) {
let AuthService = $injector.get('AuthService');
if (!config.bypassAuthorizationHeader) {
if (AuthService.jwtToken) {
config.headers.Authorization = `Bearer ${AuthService.jwtToken}`;
} else {
$log.warn('Missing JWT', config);
}
}
return config || $q.when(config);
},
responseError(rejection) {
let AuthService = $injector.get('AuthService');
if (rejection.status === 401) {
AuthService.backToDAS();
}
return $q.reject(rejection);
}
};
}
I don't understand why I'm getting this error - I provided all the dependencies for the service and am following what is outlined in the angular documentation. any help is appreciated!
Update, this is the code that I went with:
import angular from 'angular';
import AuthInterceptor from './auth.interceptor'
describe('Auth interceptor test', () => {
describe('AuthInterceptor test', () => {
let $httpBackend, $http, authInterceptor = AuthInterceptor();
beforeEach(angular.mock.module(($httpProvider, $provide) => {
$httpProvider.interceptors.push(AuthInterceptor);
$provide.factory('AuthService', () => ({
jwtToken: "hello",
backtoDAS: angular.noop
}));
}));
beforeEach(inject(function($injector) {
$httpBackend = $injector.get('$httpBackend');
$http = $injector.get('$http');
}))
it('should have a request function', () => {
let config = {};
expect(authInterceptor.request).to.be.defined;
expect(authInterceptor.request).to.be.a('function');
})
it('the request function should set authorization headers', (done) => {
$httpBackend.when('GET', 'http://jsonplaceholder.typicode.com/todos')
.respond([{
id: 1,
title: 'Fake title',
userId: 1
}]);
$http.get('http://jsonplaceholder.typicode.com/todos').then(function(transformedResult) {
expect(transformedResult.config.headers.Authorization).to.be.defined;
expect(transformedResult.config.headers.Authorization).to.contain('Bearer')
done();
})
$httpBackend.flush();
});
it('should have a responseError function', () => {
expect(authInterceptor.responseError).to.be.defined;
expect(authInterceptor.responseError).to.be.a('function');
//TODO: test return value
// see that AuthService.backToDAS()
})
it('the error function should call backtoDAS', (done) => {
//the URL should be one that gives me a 401
$httpBackend.when('GET', 'https://wwws.mint.com/overview.event')
.respond([{
id: 1,
title: 'Fake title',
userId: 1
}]);
$http.get('https://wwws.mint.com/overview.event').then(function(transformedResult) {
console.log(transformedResult);
done();
}, function(error){
console.log(error);
done();
})
});
})
});

This means that AuthInterceptor Angular module wasn't defined (and by the way, relying on name is unsafe).
AuthInterceptor isn't a module but an injectable function. It can be tested in functional fashion as $http interceptor:
beforeEach(angular.mock.module(($httpProvider) => {
$httpProvider.interceptors.push(AuthInterceptor);
});
...
it('...', () => {
$httpBackend.when(...).respond(...);
$http.get(...).then((interceptedResult) => {
expect(interceptedResult)...
});
$rootScope.$digest();
});
or directly:
it('...', () => {
let interceptor = $injector.invoke(AuthInterceptor);
expect(interceptor).toEqual({
request: jasmine.any(Function),
requestError: jasmine.any(Function)
});
var config = { headers: {} };
interceptor.request(config);
expect(config)...
});
Services that produce side effects (AuthService, $log) should be stubbed.

This means that ng module is failing to load. :) And this happens while bootstrapping the app and ng module is first in a three element array: ng, ['$provide', function($provide) { ... }] and my own application module. It fails when loading the first one.
I've looked at console and I've copied this error message from it. There's no other error. None.
I hope you clicked that specific link and see that it doesn't give you any specific ideas about it. Unfortunately I've added this GitHub issue after exhausting other resources. I'm currently debugging angular code to get any further.

Related

Unit testing angularjs - How to inject service dependency?

I introduced a MyDataApiService dependency into ThingBuilderService, and now ThingBuilderService tests are failing. How do I mock MyDataApiService and tell ThingBuilderService about it in tests?
export default class ThingBuilderService {
public static $inject = ['MyDataApiService'];
public myData: any[];
/**
* Construct an instance of ThingBuilderService.
*
* #param {xyz.MyDataApiService} myDataApiService The MyDataApiService object.
*/
constructor(myDataApiService: xyz.MyDataApiService) {
myDataApiService.getSomeData()
.then((response) => this.myData = response.data);
}
//...
}
Test: (I've included code showing part of what I think I need to do, but I don't understand how to fit the pieces together.
describe('Thing Builder Service', () => {
var service;
var mockMyDataApiService = {};
beforeEach(() => {
var mockMyDataApiService.getSomeData = () => {
var deferred = $q.defer();
deferred.resolve({ data: [
{
'ItemId': 1010101,
'Description': 'asdfasdf'
},
{
'ItemId': 1010102,
'Description': 'jkjkjkjk'
}
]});
return deferred.promise;
};
// *********** now what? ***********
// and do I have to do something to make '$q'
// available in the getSomeData function?
angular.mock.module('abc.module');
angular.mock.inject(_ThingBuilderService_ => {
service = _ThingBuilderService_;
});
});
// tests here
it('should ...', () => { ... });
});
Test runs are giving errors like this:
Error: [$injector:unpr] Unknown provider: MyDataApiServiceProvider <-
MyDataApiService <- ThingBuilderService
and this:
Error: Base URL not defined for MyDataApiService
You have to provide your mocked service:
angular.mock.module(function($provide) {
$provide.service(„MyDatApiService“, mockMyDataApiService);
});

How do I test a rejected Angular (1.6.1) promise in jasmine?

Below was my approach to testing rejected promises in angular 1.5.x. Updating to 1.6.1 has introduced the "Possibly unhandled rejection" error. I understand the purpose of this error, but I haven't figure out a simple way around it. Jasmine's (2.5.x) and.throwError() method seems to cause tests to fail by virtue of being an error.
describe('TestTarget', () => {
let $q, $rootScope, TestTarget, SomeModel;
beforeEach(() => {
SomeModel = jasmine.createSpyObj('SomeModel', ['get']);
module('something');
module($provide => {
$provide.value('SomeModel', SomeModel);
});
inject((_$q_, _$rootScope_, _TestTarget_) => {
$q = _$q_;
$rootScope = _$rootScope_;
TestTarget = _TestTarget_;
});
});
describe('get()', () => {
it('on error, adds a danger message', () => {
SomeModel.get.and.returnValue($q.reject());
// SomeModel.get.and.throwError(); // doesn't work either
TestTarget.get();
$rootScope.$digest();
expect(SomeModel.get).toHaveBeenCalled();
expect(<< the rejection outcome of TestTarget.get().then() >>);
});
});
});
I want to continue to pass rejected promises across my models, services, and controllers.
Let us say your actual code is like this:
angular.module('myApp').service('TestTarget', ['SomeModel', function(SomeModel) {
this.get = function() {
return SomeModel.get();
}
}]);
My assumption is you have no .catch block chained to the returning Promise.
In this case you can simply test it like this:
it('on error, adds a danger message', () => {
SomeModel.get.and.returnValue($q.reject('some error'));
TestTarget.get()
.then(function() {
fail('the promise should not have been resolved');
}).catch(function(err) {
expect(err).toBe('some error');
});
$rootScope.$digest();
expect(SomeModel.get).toHaveBeenCalled();
});
Notice that I add a catch block to my function call. The $rootScope.$digest() makes sure that the promise is resolved and all its chained then and catch blocks are getting called.
I had this issue with angular 1.6.1. Try callFake instead of returnValue in your test.
describe('TestTarget', () => {
let $q, $rootScope, TestTarget, SomeModel;
beforeEach(() => {
SomeModel = jasmine.createSpyObj('SomeModel', ['get']);
module('something');
module($provide => {
$provide.value('SomeModel', SomeModel);
});
inject((_$q_, _$rootScope_, _TestTarget_) => {
$q = _$q_;
$rootScope = _$rootScope_;
TestTarget = _TestTarget_;
});
});
describe('get()', () => {
it('on error, adds a danger message', () => {
SomeModel.get.and.callFake(() => $q.reject());
// SomeModel.get.and.throwError(); // doesn't work either
TestTarget.get();
$rootScope.$digest();
expect(SomeModel.get).toHaveBeenCalled();
expect(<< the rejection outcome of TestTarget.get().then() >>);
});
});
});

Protractor override "beforeAll" http mock

I'm using Protractor in order to get some End-to-End testing in my application. In a test, I mock the backend-calls with a MockModule as following:
describe('Lets test a feature' , function(){
beforeAll(function () {
var customerE2E = function () {
angular.module('customerE2E', ['customer', 'ngMockE2E'])
.run(function ($httpBackend) {
$httpBackend.whenPOST('/api/data').respond(200);
$httpBackend.whenGET(/^.*/).passThrough();
});
};
browser.addMockModule('customerE2E', customerE2E);
});
it('correctly demonstrates my problem', function () {
expect(element(by.css('h4')).getText()).toMatch('Hello world');
}
})
This works really good but my problem is that I also want to test my application when the post responds with a 404, 500 etc. I have solved this by having a new "describe"-function for each case but it would be nice to just be able to override this call from inside the "it"-functions.
it('correctly demonstrates my problem', function () {
expect(element(by.css('h4')).getText()).toMatch('Hello world');
}
it('show error due to bad request', function () {
/*
Replace $httpBackend.whenPOST('/api/data').respond(200);
with $httpBackend.whenPOST('/api/data').respond(404);
*/
expect(element(by.css('h4')).getText()).toMatch('Failed api call');
}
I'm really new to this so I am wondering if there is a nice way to achieve an easy way to override the earlier MockModuled that was set in the beforeAll-function.
You can use beforeEach() to achieve it.
describe('Test Mock Module',function () {
var statusCodes = [200,404,500];
var currentTestNumber = 0;
beforeAll(function () {
var customerE2E = function () {
angular.module('customerE2E', ['customer', 'ngMockE2E'])
.run(function ($httpBackend) {
$httpBackend.whenPOST('/api/data').respond(200);
$httpBackend.whenGET(/^.*/).passThrough();
});
};
browser.addMockModule('customerE2E', customerE2E);
});
/*Below method will be executed before each test and set the required response respectively*/
beforeEach(function () {
$httpBackend.whenPOST('/api/data').respond(statusCodes[currentTestNumber++]);
});
it('test 200 status code',function () {
expect(element(by.css('h4')).getText()).toMatch('Message for 200 status code');
});
it('test 404 status code',function () {
expect(element(by.css('h4')).getText()).toMatch('Message for 404 status code');
});
it('test 500 status code',function () {
expect(element(by.css('h4')).getText()).toMatch('Message for 500 status code');
});
});

AngularJS $httpBackend verify no interactions

I'm looking for a way to evaluate $httpBackend to see if there are any interactions. I want to make sure it has never been called at this point in my test case. I've checked the documentation here: https://docs.angularjs.org/api/ngMock/service/$httpBackend and haven't found the answer.
Class using $http
class HomeService {
/*#ngInject*/
constructor ($http, $log) {
this.http = $http;
this.log = $log;
}
submit(keycode) {
this.log.log("submitting key code: " + keycode);
if (keycode === "") {
return false;
}
this.http.post(`/api/keycode/${keycode}`).then ( (response) => {
this.log.log(response);
return true;
});
}
}
export default HomeService;
test case so far.
import HomeService from './home.service';
describe('HomeService', () => {
let homeService, $httpBackend;
beforeEach(inject(($injector) => {
$httpBackend = $injector.get('$httpBackend');
}));
afterEach(function() {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
describe('submit', () => {
it('submit empty keycode', () => {
homeService = new HomeService($httpBackend, console);
let value = homeService.submit("");
expect(value).to.be.false;
//valid no interactions with $httpBackend here!
});
});
});
Even though
afterEach(function() {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
may be enough to throw on unwanted requests, to tie an error to specific spec use:
expect($httpBackend.verifyNoOutstandingExpectation).not.toThrow();

Unit-Testing a service in Controller with Jasmine in AngularJS

In my Controller I've defined the following service:
CrudService.getAllGroups().$promise.then(
function (response) { $scope.groups = response; },
function (error) { //error code.. }
);
Well, I want to test this service whether it gets a response or not. In test script at first I've defined a function to check whether the service is defined at all.
Test code:
describe('Ctrl: TestCtrl', function () {
beforeEach(module('testApp'));
var scope,
CrudService,
ctrl,
backend;
beforeEach(inject(function ($controller, $rootScope, _CrudService_, $httpBackend) {
scope = $rootScope.$new();
ctrl = $controller('TestCtrl', {
$scope: scope
});
CrudService = _CrudService_;
backend = $httpBackend;
}));
it('should defined the service getGroups', function () {
expect(CrudService.getGroups).toBeDefined();
});
//this is wrong!
it('should returns a successful response', function () {
backend.expectGET('http://localhost:63831/api/group').respond(200, 'success');
backend.flush();
});
});
I don't know how to get a response in the test. I'm new in unit testing and need some help.
For a better comprehension here is the service code:
//CrudService file:
...
return {
getAllGroups: function () {
return ResService.group.query();
}
}
...
//ResService file:
return {
group: $resource(baseUrl + '/api/group/:Id', {
Id: '#Id'
}, {})
}
Do anyone has an idea?
It's incorrect in the sense that it's not a unit test. If you are testing controller here, then you should mock CrudService and test that $scope.groups has been assigned correctly.
beforeEach(function () {
module(function ($provide) {
$provide.factory('CrudService', function () {
return {
getAllGroups: function () {
return {
$promise: null // return an actual promise here
}
}
}
});
});
});
it('should set groups', function () {
expect($scope.groups).toEqual('success')
});
And you need a separate spec to test if CrudService calling backend correctly.

Resources