# This won't work if you're not within the Opower firewall - all you need is zelda.js and lodash, which you can get with `bower install lodash`
$ bower install --save [email protected]:nick-heiner/zelda.git
Then, include <your bower install dir>/zelda/zelda.js
and <your bower install dir>/lodash/lodash.js
in your html or tests.
<your bower install dir>
defaults to components/
.
Place the following in a js file that is included by your project:
fixtures.js:
// change 'my-module-name' to be the name of your module
angular.module('my-module-name')
// The service you're mocking needs to be defined first
// If you don't have a real API yet, define a stub as follows:
.value('hammerTime', {})
.run(function($zelda) {
// wrap the angular service `hammerTime` with our mocks
$zelda.mock('hammerTime', function(fixture) {
// each fixture is a different scenario
// you could have separate fixtures for various interesting edge cases
// or forms the data could take
fixture('default', function(whenGet) {
// this will set up a mock so that calling `hammerTime.getIsHammerTime()` will return
// a promise that resolves to `true`
whenGet('isHammerTime').respond(true);
whenGet('mostRecentHammerTime').respond(new Date('1 Feb 2013'));
// this will set up a mock so that calling `hammerTime.getHasInitiatedHammerTime('Nick Cage')`
// will return a promise that resolves with `false`
whenGet('hasInitiatedHammerTime').withArgs('Nick Cage').respond(false);
});
// In this case, we decide we want another fixture to develop with a
// different form the data could take. You can have arbitrarily many fixtures.
fixture('notHammerTime', function(whenGet) {
whenGet('isHammerTime').respond(false);
whenGet('mostRecentHammerTime').respond(new Date('28 April 2011'));
// withArgs allows you to specify different responses for different
// arguments. Pass as many args as you'd like!
whenGet('hasInitiatedHammerTime').withArgs('Nick Cage').respond(true);
// This will make an http request to the url you specify and
// return that result.
whenGet('actuallyIsDolan').respondWithResource('gooby-plz.json');
// If you have to do something cRaZy, you can return your own promise
whenGet('powerLevel').respondWithPromise(function() {
function isOver9000(powerLevel) {
return powerLevel > 9000;
}
var deferred = $q.defer();
checkTheScouter().then(function(result) {
if (result > 9000) {
checkTheScouter().then(function(powerLevel) {
deferred.resove(isOver9000(powerLevel);
});
}
deferred.resove(isOver9000(result);
});
return deferred.promise;
});
});
});
});
Note that if you call whenGet('foo')
, it will produce a method called getFoo()
.
Because this file contains mocks, it only needs to be included at dev-time. In prod, it can be safely omitted.
As a comment above notes, zelda assumes that your API-object-to-mock already exists. If it doesn't yet, just use
app.value('myApi', {})
, and you'll be good to go. If your target object does exist, define it as you normally
would, and zelda will apply its own definitions on top of it, falling back to the real object when it doesn't
have a mock defined.
Unless you are building an app called my-module-name
that helps the user determine if it's hammer time or not,
you'll probably want to edit the above values.
Assuming that you're running fixture default
from above, when you inject hammerTime
,
you'll get an object that acts like the following:
{
getIsHammerTime: function() {
var response = $q.defer();
response.resolve(true);
return response.promise;
}
getMostRecentHammerTime: function() {
var response = $q.defer();
response.resolve(new Date('1 Feb 2013'));
return response.promise;
}
getHasInitiatedHammerTime: function(name) {
if (name === 'Nick Cage') {
var response = $q.defer();
response.resolve(false);
return response.promise;
}
}
getActuallyIsDolan: function() {
var response = $q.defer();
$http.get('gooby-plz.json').success(function(data) {
response.resolve(data);
});
return response.promise;
}
getPowerLevel: function() {
var response = $q.defer();
suppliedPromise().then(function(result) {
response.resolve(result);
});
return response.promise;
};
}
To switch between fixtures, call $zelda.setActiveFixtureName('someFixtureName')
from your code.
(In a future version, it may be possible to just use ?fixtureName=someFixtureName
in the url.)
If no fixture name is set, zelda will pick an arbitrary fixture to use.
To use the mocks, just inject the service you're wrapping:
angular
// you must add 'zelda' as a dependency of your angular module,
// or the dependency injector won't be able to find $zelda
.module('consumerApp', ['zelda'])
.directive('billCompare',
[ 'hammerTime',
function (hammerTime) {
return {
link: function postLink($scope, element, attrs) {
$scope.isHammerTime = hammerTime.getIsHammerTime();
hammerTime.getMostRecentHammerTime().then(function(mostRecentTime) {
$scope.timespanSinceHammerTime = getTimespan(mostRecentTime, new Date());
});
// We didn't mock this out, so it will just fall back
// to the real `hammerTime` object
$scope.hammerCount = hammerTime.getHammerCount();
}
};
}
]
);
The beauty of this approach is that at production time, you simply omit the fixtures.js file from above, and the app will fall back to real data. The app itself doesn't need to change anything to switch the data it's using.
For a more precise sense of going on, just read the code or the tests. They're short.