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: '[email protected]'})
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:
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With