How do I provide re-usable sample data values to my angularjs / jasmine unit-tests - angularjs

I would like to provide simple constant values such as names, emails, etc to use in my jasmine unit tests.
A similar question was asked here: AngularJS + Karma: reuse a mock service when unit testing directives or controllers
In c# I would create a static class to hold little snippets of mock data. I can then use these values throughout my unit tests, like this:
static class SampleData
{
public const string Guid = "0e3ae555-9fc7-4b89-9ea4-a8b63097c50a";
public const string Password = "3Qr}b7_N$yZ6";
public const string InvalidGuid = "[invalid-guid]";
public const string InvalidPassword = "[invalid-password]";
}
I would like to have the same convenience when testing my AngularJS app using Karma / Jasmine.
I know that I can define a constant object against my angular app, I already do this for constants I use in the real code, like this:
myApp.constant('config', {apiUrl:'http://localhost:8082'})
I could add another constant just like this but only containing sample data values for use in my unit tests, like this:
myApp.constant('sampleData', {email: 'sample#email.com'})
I could then just inject the mock constant object into my tests and off I go, like this
describe 'A sample unit test', ->
beforeEach -> module 'myApp'
beforeEach inject ($injector) ->
#sampleData = $injector.get 'sampleData'
email = #sampleData.email
# etc ...
However this seems a bit fishy to me. I don't want my production code to contain sample data that is only required by my unit-tests.
How would you conveniently provide your angular / jasmine unit tests with re-usable sample data values?
Thanks

There are two ways of doing this:
spy on function calls and return fake values.
create mock classes (and possibly mock data to initialise them) and load them wherever you need
The first one is alright when you only have to fake a few calls. doing that for a whole class is unsustainable.
For example, let's say you have a service that builds some special URLs. If one of the methods depends on absUrl, you can fake it by spying on the method in the $location object:
describe('example') function () {
beforeEach(inject(function () {
spyOn($location, 'absUrl').andCallFake(function (p) {
return 'http://localhost/app/index.html#/chickenurl';
});
}));
it('should return the url http://www.chicken.org') ... {
// starting situation
// run the function
// asserts
}
Now let's say that you have a Settings Service that encapsulates data like language, theme, special paths, background color, typeface... that is initialised using a remote call to a server.
Testing services that depend on Settings will be painful. You have to mock a big component with spyOn every time. If you have 10 services... you don't want to copypaste the spyOn functions in all of them.
ServiceA uses Settings service:
describe('ServiceA', function () {
var settings, serviceA;
beforeEach(module('myapp.mocks.settings')); // mock settings
beforeEach(module('myapp.services.serviceA')); // load the service being tested
beforeEach(inject(function (_serviceA_, _settings_) {
serviceA = _serviceA_;
settings = _settings_;
}));
container
for this test suite, all calls to the Settings service will be handled by the mock, which has the same interface as the real one, but returns dummy values.
Notice that you can load this service anywhere.
(If, by any reason, you needed to use the real implementation, you can load the real implementation before the mock and use spyOn for that particular case to delegate the call to the real implementation.)
Normally you'll place the mocks module outside of the app folder. I have a test folder with the unit tests, e2e tests and a lib folder with the angular-mocks.js file. I place my mocks there too.
Tell karma the files you need for the tests:
files: [
'app/lib/jquery/jquery-1.9.1.js',
'test/lib/jasmine-jquery.js',
'app/lib/angular/angular.js',
'app/lib/angular/angular-*.js',
'test/lib/angular/angular-mocks.js',
'test/lib/myapp/*.js', /* this is mine */
'app/js/**/*.js',
'test/unit/**/*.js'
],
The file tests/lib/myapp.mocks.settings.js looks just like any other module:
(function () {
"use strict";
var m = angular.module('myapp.mocks.settings', []);
m.service('settings', function () { ... })
})
Second problem (optional): you want to change quickly the dummy values. In the example, the settings service fetches an object from the server when it is instantiated for the first time. then, the service has getters for all fields. This is kind of a proxy to the server: instead of sending a request everytime you need a value, fetch a bunch of them and save them locally. In my application, settings don't change in the server in run-time.
Something like:
1. fetch http://example.org/api/settings
2. var localSettings = fetchedSettings;
3 getFieldA: function() { return localSettings.fieldA; }
Go and pollute the global namespace. I created a file settings.data.js with a content like:
var SETTINGS_RESPONSE = { fieldA: 42 };
the mock service uses this global variable in the factory to instantiate localSettings

Related

Angular Controller can inject service just fine but $injector.get can't?

I have an extremely edge case scenario where I have a callback method I have to define during config. Which means no scope, no factories, etc... My work around is to use the root injector ($injector) and get my other modules at runtime.
However, when I call $injector.get('myServiceName') in my call back (after the application is running) I get "unknown provider". The same service has no problem (and actually is) being injected into a before my line of code is running. If I call $injector.get("myServiceNameProvider") then I can get the provider back in my callback.. But another service that only has a factory I can't get at all.
So in this extremely bad practice, how can I snag the service I configured. Heck I can't even seem to get $rootScope..
Angular inits providers first, then factories/services - services are not available in app.config.
You can deo like this:
app.config(...
window.on('scroll', function() {
// this is executed later
$injector.get()...
})
Or use app.run where services are available.
I think I had similar problem few months ago... I need to construct breadcrumbs, but they had to be created in config phase (ui-router). I have done it like this (breadcrumbs-generation-service.provider.js):
var somethingYouHaveToHaveLater = undefined;
function BreadcrumbsGenerationService () {
this.createStateData = createStateData;
function createStateData (arg) {
somethingYouHaveToHaveLater = arg;
};
}
function BreadcrumbsGenerationServiceProvider () {
this.$get = function BreadcrumbsGenerationServiceFactory () {
return new BreadcrumbsGenerationService();
}
}
angular
.module('ncAdminApp')
.provider('BreadcrumbsGenerationService', BreadcrumbsGenerationServiceProvider);
Because service is used inside Angular configs, needs to be injected as provider to be available in config phase: Similar SO Question. Despite the fact is registered as BreadcrumbsGenerationService needs to be injected as BreadcrumbsGenerationServiceProvider to config phase and used with $get():
BreadcrumbsGenerationServiceProvider.$get().createStateData(someParams);
But in controller, inject it without Provider suffix (BreadcrumbsGenerationServic) and it behaves as normal service.

Angular 1: $injector can't find a provider dependency when injecting provider inside a provider

My use case is: we have several helper classes, A and B, that are services, A depends on B, and I wanted to make them providers so that they can be used in .config phase.
I followed this SO answer to load a provider inside a provider.
As you can see here, it works:
http://plnkr.co/edit/SIvujHt7bprFumhxwJqD?p=preview
var coreModule = angular.module('CoreModule', []);
coreModule.provider('Car', function() {
//CarProvider.engine
this.engine = 'big engine';
//Car
this.$get = function() {
return {
color: 'red'
};
};
});
coreModule.provider('ParameterService', ['$injector', function($injector) {
try {
var CarProvider = $injector.get('CarProvider');
this.deepEngine = CarProvider.engine;
console.log('deepEngine = ' + this.deepEngine);
} catch (e) {
console.log("nope!")
}
// ParameterService
this.$get = function() {
return {};
};
}]);
coreModule.config(function(CarProvider) {
console.log('configEngine = ' + CarProvider.engine); // big engine
});
This works if I have Car and ParameterService in one file in this order.
However when I split Car and ParameterService into multiple files on disk, or I define ParameterService before Car in the same file, $injector.get('CarProvider') inside ParameterService fails.
How do I fix the issue?
I want to have one provider/service per file and I don't understand what is missing.
The order in which the services are defined doesn't matter during run phase, where service instances are injected. But it does matter during configuration phase, where service providers are injected, i.e. in provider constructors and config blocks.
Providers and config blocks are executed in the order in which they are defined. If Car provider is defined after ParameterService provider or config block, CarProvider doesn't exist at the moment when those two are executed.
To avoid potential race conditions, one module per file pattern should be followed. This allows to keep the app highly modular (also beneficial for testing) and never care about the order in which the files are loaded. E.g.:
angular.module('app', ['app.carService', 'app.parameterService']).config(...);
angular.module('app.carService', []).provider('Car', ...);
angular.module('app.parameterService', []).provider('ParameterService', ...);
Module parts are executed in the order in which the modules are defined in angular.module array hierarchy, from children to parents.
The decision if config block needs its own module depends on what it does (mostly for testing reasons).
It is possible to have providers in different files. You just need to attach them to the first module that you created.
If your markup looks like this:
<script src="coreModule.js"></script>
<script src="parameterService.js"></script>
Then, in coreModule.js, define your module:
angular.module('CoreModule', [])
.provider('Car', function() {
...
}
Remember, the second parameter ([]) tells angular to create a new module.
Then, declare your other provider in a different file, and attach it to your existing 'CoreModule' module:
angular.module('CoreModule')
.provider('ParameterService', ['$injector', function($injector) {
...
}
Notice that we are only passing one parameter to .module(). This tells angular to add your provider to an existing module.
Plunkr Demo

Adding an attribute to a provider after config

I am using ng-flow, to upload files with a servlet but as I was securing the servlet I realized I need to pass the token to the headers so It would work and be secure. The problem is that ng-flow's settings are declared on a provider inside a .config box. And as I learned the hard way you can't inject stuff on .config because the injections are created after config.
angular.module('UploadModule', [ 'ngResource','flow' ,'AuthModule']).config(
[ 'flowFactoryProvider',function(flowFactoryProvider,$provide) {
//AuthService.getKeycloak();
flowFactoryProvider.defaults = {
target : '/ng-flow-java/upload',
permanentErrors : [ 500, 501 ],
maxChunkRetries : 1,
chunkRetryInterval : 5000,
simultaneousUploads : 4,
progressCallbacksInterval : 1,
withCredentials : true,
method : "octet",
headers : {'Authorization', 'Bearer + ' token}
};
flowFactoryProvider.on('catchAll', function(event) {
console.log('catchAll', arguments);
});
// Can be used with different implementations of Flow.js
// flowFactoryProvider.factory = fustyFlowFactory;
} ]);
I am really new to angular so I am looking for a way reassemble this code so I can add the token from my user.
Thanks
I think I don't understand what exactly is the problem but:
Actually each angular service x has a provider function. This function is a constructor function and will be instanciated by angular and its result is injectable in config blocks with name xProvider. This object should have a special $get function which is actually the factory for the service, meaning that it will be called to get the single instance of the service, when it is injected into some controller, directive, etc for the first time.
So you should think of an angular service as a function like this:
function SomeServiceProvider(){
this.$get = function SomeServiceFactory(){
}
this.someConfigurerFunction(){
}
}
which is registered with provider API:
someModule.provider("someService", SomeServiceProvider);
and angular internally will execute something like new SomeServiceProvider() and save it and makes in injectable in config blocks under the name of someServiceProvider. Further, when you ask for "someService", in a directive, controller, etc, for the first time, the injector will call something like new someServiceProvider.$get(), return its result to you and saves it in its registry for further injections.
When you use other higher level angular module APIs, like factory or service like this:
someModule.factory("anotherService", function AnotherServiceFactory(){
// code for creating service
});
the provider function is generated for you with only a $get function, so you don't see the provider function, but still there is a provider function for your service like this:
function AnotherServiceProvider(){
this.$get = AnotherServiceFactory;
}
and it's used as constructor to instantiate anotherServiceProvider which is injectable in config blocks.
You can find out good information about angular services in angular documentations for $provide and angular documentations for module API
Side Notes:
Good services usually come with a provider that enables you to configure service, but if not, you still can intercept the creation of the service with decorators and make service to work as you want.
The process of instantiating objects like services is not exactly like what I've said here (I mean new ...), but it doesn't changes the concepts described here.

How to avoid code duplication in AngularJS Jasmine tests

I'm part of a team that's been working on angularjs for quite a while now and our unit tests consist of files which contain both the test logic and provider mocks of each component that tested element relies on.
This is leading to more and more code duplication, can anyone recommend an angular-ey method whereby we could maintain a test infrastructure, mock a controller or service once and be able to inject that mocked service into other tests as required?
Edit The code I'm working with is proprietary but for an example consider you have 10 or 15 services which retrieve data via HTTP requests, format the data in different ways and return that data. One example might return arrays of data so in each file which relies on the service we would have initialising code like the following ( cut down and altered so please ignore any typos or other mistakes )
myDataService: {
getDataParsedLikeY: {
[{obj1},{obj2}...]
},
getDataParsedLikeX: {
[{obja},{objb}...]
}
beforeEach(angular.mock.module('myModule'));
beforeEach(angular.mock.inject(function(myDataService) {
myDataService = function( functionName ) {
return myDataService[functionName];
}
spyOn(myDataService).and.callThrough();
})
}
If you are looking for a way not to declare same mock codes in every test files, I would suggest something like below although I'm not sure this is angular-way or not.
[1] write the code to declare your mock services like below only for unit test and import after angular-mocks.js
In your-common-mocks.js(you can name the file as you like)
(function(window){
window.mymock = {};
window.mymock.prepare_mocks = function(){
module(function($provide) {
$provide.service('myDataService', function(){
this.getDataParsedLikeY = function(){
return 'your mock value';
};
});
});
};
})(window);
If you use karma, you could write like this to import the file in karma.conf.js.
files: [
...
'(somedirctory)/angular-mocks.js',
'(somedirctory)/your-common-mocks.js'
...
]
[2] use mymock.prepare_mocks function in your test files.
describe('Some Test', function() {
beforeEach(mymock.prepare_mocks);
it('getDataParsedLikeY', inject(function(myDataService){
expect('your mock value', myDataService.getDataParsedLikeY());
As a result of above, you have to write your mock code just one time and share the mock code in your every test files. I hope this could suggest you something to achieve what you want.
If your jasmine version is 2.x, you can write test code like below without writing mock service.
spyOn(YourService, 'somemethod').and.returnValue('mock value');
In jasmine 1.3 you need to adjust little bit.
spyOn(YourService, 'somemethod').andReturn('mock value');
I hope this could help you.

Angular JS: why the difference between module.config injection and controller injection?

This is something that I could not figure out from digging into the AngularJS code, maybe you can help solve the mystery.
To show it, I added a service to AngularJS seed project:
function MyServiceProvider() {
console.log('its my service');
this.providerMethod = providerMethod;
function providerMethod() {
console.log('its my service.providerMethod');
}
this.$get = $get;
function $get() {
var innerInjectable = {
name: 'stam'
};
return innerInjectable;
}
}
var serviceModule = angular.module('myApp.services', []).
value('version', '0.1').
provider('myservice',MyServiceProvider);
You can see that this provider exposes $get and a certain 'providerMethod'.
Now, for the injection usage:
If we call config, we can inject the whole class and get access to the 'outer' provider method:
serviceModule.config(function(myserviceProvider) {
console.log('myServiceProvider:',myserviceProvider);
myserviceProvider.providerMethod();
});
But when we inject this to a controller (note the Provider-less name), only the $get return value is exposed:
function MyCtrl1(myservice) {
console.log('MyCtrl1.myservice =',myservice,myservice.name);
}
MyCtrl1.$inject = ['myservice'];
Console output follows as it should:
its my service
myServiceProvider:
Constructor {providerMethod: function, $get: function}
its my service.providerMethod
MyCtrl1.myservice = Object {name: "stam"} stam
Can any one explain the difference? The reason?
many thanks for any thought
Lior
PS: I've seen this technique in angular-ui new ui-router (excellent project!). I need access to the outer provider class to do injection in jasmine and other places - to no avail
A provider is responsible for creating instances. In your example, you created a provider explicitly, but the truth is that every service has a provider, even if it's created automatically for it. [module].service() and [module].factory() are just shortcuts for [module].provider().
[module].config() is run during provider registrations and configurations so you get a change to access providers and do stuff with them. It's a place for configuration of things, hence the name.
From the documentation (http://docs.angularjs.org/guide/module):
Configuration blocks - get executed during the provider registrations
and configuration phase. Only providers and constants can be injected
into configuration blocks. This is to prevent accidental instantiation
of services before they have been fully configured.
Controllers, in the other hand, are instantiated AFTER services have been configured, so you're not supposed to mess with providers anymore. Everything has already been configured. You're ready to get their products now. In this phase, the injector just can't inject providers anymore, just instances (services) created by them.
If you register a service myService...
myModule.service('myService', function() {
// this is your service constructor
});
then you can access its provider, myServiceProvider, in a config function...
myModule.config(function(myServiceProvider) {
// to stuff with your provider here
});
but by the time controllers are being instantiated, you're supposed to ask for services, not their providers, so this will not work...
myModule.controller(function(myServiceProvider) {
...
});
whereas this will be fine...
myModule.controller(function(myService) {
...
});
If you're finding yourself needing to do configuration in a controller, you should stop and rethink the place of responsibilities.
From the Angular mailing list I got an amazing thread that explains service vs factory vs provider and their injection usage. I decided to put it in its own question here
the specific answer is: it is so by design, to allow configuration of the provider at config time.

Resources