AngularJS unit testing. HttpBackend - angularjs

I have AngularJS app with unit tests covering it. I added one more http request in .run part of application which checking user authentication. Now about 60% of my tests fails because they found 'unexpected http request'. Even directives tests are fail. How can I avoid running this request in tests? How can I easily mock this request in all tests? Im trying to not to put httpBackend.expect('GET', 'URL') to each test - that's too much work.
Testing with Jasmine + Karma
Thanks. I can give more details if needed.

There are a few ways to do this:
1.) Include a beforeEach that Tim Castelijns suggested but for every spec file
2.) Include the beforeEach in a separate file called app-mocks.js & include in your karma.conf.js (I'm guessing you have one).
karma.conf.js
files: [
'location/of/app-mocks.js',
'src/app/**/*.js'
],
app-mocks.js
(function () {
'use strict';
beforeEach(inject(function ($httpBackend) {
$httpBackend.whenGET('blahblahblah.com/cat/:id/food').respond('');
}));
})();
3.) Separate out user authentication logic from the core app and create an $http wrapper service (suggested). Thus you could have (simplified example):
app.js
angular.module('myApp', ['myApp.services']);
user-service.js
angular.module('myApp.services', [])
.factory('User', function ($http) {
function authenticate() {
// authenticate user
}
return {
authenticate: authenticate
};
});
and same for your directives, controllers, etc. This way, you can unit test all of your components in isolation without having to worry about intertwined logic that is actually specific to one type of functionality. Also, when you do test core app functionality, you can just mock or spy on your User service. In your spec files you would inject only the module & functionality you are testing. So instead of injecting your entire core app module, you would:
To test services
beforeEach(module('myApp.services'));
To test directives
beforeEach(module('myApp.directives'));
etc.
Of Note: When your application starts to get big, I would consider organizing your app components based on feature rather than by type as this example above does.

You can use a beforeEach block to define a call that all tests should expect
var httpBackend;
beforeEach(inject(function(_$httpBackend_) {
httpBackend = _$httpBackend_;
httpBackend.expect('GET', 'URL').respond('');
}
Note the .respond('') I added, an expect call needs this, see the documentation if you want to know why.
This beforeEach functionality comes with the widely used jasmine testing framework.

Related

Usage of spec.js file in AngularJS

This might sound a stupid question, but I want to know its answer. What is the spec.js file in AngularJS and what is its use? Is it used for testing purpose?
EDIT- Below is the code of file phone-detail.component.spec.js
'use strict';
describe('phoneDetail', function() {
// Load the module that contains the `phoneDetail` component before each test
beforeEach(module('phoneDetail'));
// Test the controller
describe('PhoneDetailController', function() {
var $httpBackend, ctrl;
var xyzPhoneData = {
name: 'phone xyz',
images: ['image/url1.png', 'image/url2.png']
};
beforeEach(inject(function($componentController, _$httpBackend_, $routeParams) {
$httpBackend = _$httpBackend_;
$httpBackend.expectGET('phones/xyz.json').respond(xyzPhoneData);
$routeParams.phoneId = 'xyz';
ctrl = $componentController('phoneDetail');
}));
it('should fetch the phone details', function() {
jasmine.addCustomEqualityTester(angular.equals);
expect(ctrl.phone).toEqual({});
$httpBackend.flush();
expect(ctrl.phone).toEqual(xyzPhoneData);
});
});
});
Use of spec.js is for writing you unit test cases for your angular application.
We write test cases in angular using Jasmine & Karma.
Jasmine is a Behavior Driven Development testing framework for JavaScript. It does not rely on browsers, DOM, or any JavaScript framework. Thus it's suited for websites, Node.js projects, or anywhere that JavaScript can run.
https://github.com/jasmine/jasmine
Karma is essentially a tool which spawns a web server that executes source code against test code for each of the browsers connected. The results of each test against each browser are examined and displayed via the command line to the developer such that they can see which browsers and tests passed or failed.
https://karma-runner.github.io/1.0/index.html
Question - What is the spec.js file in AngularJS ?
Answer - AngularJS uses Jasmine as a testing framework and tests written in Jasmine are called specs. Filename written related to Jasmine must be .spec.ts, the filename extension convention is adhered by configuration of
karma( the test runner tool ).
Question - what is its use? Is it used for testing purpose?
Answer - Karma the test runner gets the specifications for testing from this file. yes, it is used for testing :)
Reference https://angular.io/guide/testing

Sails + Angular + Jasmine

I use SailsJS for backend and Angular for front End. So far, they work well. SailsJS backend logic has tests in Mocha. I am trying to add some tests for front end Angular.
Angular's documentation on testing is cryptic to me. After reading its documentation for quite some time, I still have no idea about where to start.
1) Where should I put the unit test code for Angular in a SailsJS project? Now Angular code lives under assets/js. Should I put the test under the same directory, like the following?
assets/js/MyAngularCode.js
assets/js/MyAngularCode_test.js
MyAngularCode.js
(function(){
var myApp = angular.module('myApp', []);
myApp.controller('FirstController', [$scope, function($scope){
//blah blah
}]);
})();
What should I write in MyAngularCode_test.js to test any logic in FirstController?
2) How to run Jasmine tests for any given file directory structure in a SailsJS + Angular project?
How to make "Jasmine init" and "Jasmine" work in such a project. I assume need a Jasmine.json file. But where should it be?
3) For example, I have a file,
FirstTest.js
'use strict';
describe("A suite", function() {
beforeEach(angular.mock.module('myApp'));
it("contains spec with an expectation", function() {
expect(true).toBe(true);
});
});
Where should I put it and what should I do so it will not show the following error
ReferenceError: angular is not defined
Since you want just any opinion...
You can use yeoman for scaffolding; this will help you create pages and directory structure.
Use generator-angular since you are working with angular
yo angular:controller foobar will create files for your controller as well as the test files.
It will create a package.json with a test command (grunt test) which works out of the box.
There is a tutorial here: http://yeoman.io/codelab.html
Just clone this github repo, it's a blank app and uses Angular for the front-end https://github.com/sgress454/angular-on-sails

How to see $log calls in Terminal when running angularjs unit tests with karma?

In a project based on angular-seed project, I am running unit tests with ./scripts/test.sh --log-level debug but none of the log messages that I see are coming from my app. How do I get to see them? In my app, I am logging with $log.
Thanks. As mentioned, tests can inject console for $log using $provide. For posterity, $provide is not available via inject(function(...) { ... }) but instead must be injected into a function argument to module:
beforeEach(module('myapp', function($provide) {
// Output messages
$provide.value('$log', console);
}));
Got it working! Had I used console.log directly it would have worked but now that I am using $log I had to patch it in a beforeEach (looks like by default it wasn't wired by angularjs):
$provide.value('$log', console);
for clarity, you must structure it like this:
beforeEach(module('myapp'));
beforeEach(module('myapp', function($provide) {
// Output messages
$provide.value('$log', console);
}));

How to deal with angular module's config function when unit testing?

When setting up a unit test suite for an angular application using Karma/Jasmine, is it recommended to include the js with the app module's config function in the test's files?
I've read that it is suggested to exclude this from testing, however that seems awkward because there's often critical setup that happens in the config function that would prevent the application from working.
What's the best practice around this? Create a mock config function that does the same thing in a 'mocked' manner?
I'm running across this issue myself but want to understand the broader strategy:
How do unit test with angular-translate
In my application, I ended up using the following solution:
Define an "appBase" module with all the config and run functions that I want to run when unit-testing and create another "app" module which declares "appBase" module as a dependency and includes all the config and run functions that I don't what to run when unit-testing. Then all my unit tests use the "appBase" module, while the final application uses the "app" module. In code:
angular.module('appBase', ['dependencies'])
.config(function() {
// This one will run when unit-testing. Can also set-up mock data
// that will later be overridden by the "app" module
});
angular.module('app', ['appBase'])
.config(function() {
// This function will only run in real app, not in unit-tests.
});

Importance of order in registering provider and configuring a module in angular.js

it seems that from angular's point of view the order of registering of a service provider and the module configuration code is important: in order for the configuration code to find the provider, the provider should be registered before.
This was a total surprise for me, as I thought that angular first processes all provider registrations, to make them available for DI, and then calls config callbacks, like this:
module.config(function(myServiceProvider) {...});
Please see here a very short test that demonstrates the problem. It fails on "unknown provider", you can see it in the JS console: http://plnkr.co/edit/jGJmE2Fq7wOrwubdlTTX
Am I missing anything here? Is it an expected angular behavior?
Thanks.
Looks like this behavior has changed in more recent versions of Angular (not sure when exactly). I modified your Plunker to point from 1.0.7 to 1.3.0 and it worked without error as you had originally expected.
Similar example of code that works:
var myModule = angular.module('myModule', []);
myModule.config((myServiceProvider) => {
});
myModule.service('myService', () => {
});
Running a config for a provider before the provider is registered with the module should work just fine as you were expecting.
Reference
For reference, this reported issue appears to be the one to have fixed it: https://github.com/angular/angular.js/issues/6723
The Angular documentation for module state that:
Recommended Setup
While the example above is simple, it will not scale to large applications. Instead we recommend that you break your
application to multiple modules like this:
A service module, for service declaration
A directive module, for directive declaration
A filter module, for filter declaration
And an application level module which depends on the above modules, and which has initialization code.
As you are using a single module that you call app, you are creating a dependency between that module's config and declaration of a provider. What you should have done is to place all your providers into a separate module, such as:
var appr = angular.module('appr', [])
.provider('myService', function() {
this.$get = function() {};
})
Then you declare the dependency of your app using:
var app = angular.module('plunker', ['appr']);
Check out the updated Plunker: http://plnkr.co/edit/Ym3Nlsm1nX4wPaiuVQ3Y?p=preview
Also, instead of using the generic provider, consider using more specific implementation of provider such as controller, factory or service. Take a look at Module API documentation for more detail.

Resources