Mocking Angular $resource - javascript

Could anybody suggest me a way how to mock $resource object
I've searched though internet, but all my tries were finished by KARMA testing.
I don't need it.
My idea is to have just fake object, so I will be able to switch between $resource implementations in my app.
Thanks.

You can use $provide to do this.
angular.module(“MyApp”,[])
.config([“$provide”,function($provide){
$provide.decorator(“$resource”,function($delegate, myReplacementResource){
//$delegate is the original $resource, if you just want to modify it
//either inject a replacement resource that you have already registered
//as a factory (recommended). Or make it here.
return myReplacementResource;
});
}])

dskh presented one way to do it. Here's a another way which you might find to be easier... although it's ofen used for unit testing, you can use angular-mocks.js in your app as well:
app.run(function($httpBackend) {
$httpBackend.whenPOST('/string/match/url').respond(function (method, url, data) {
return [{some:data}];
});
$httpBackend.whenGET(/regexpmatch/).respond(function (method, url, data) {
return {some:{other:data}};
});
// pass through other stuff
$httpBackend.whenPOST(/.*/).passThrough();
$httpBackend.whenGET(/.*/).passThrough();
$httpBackend.whenDELETE(/.*/).passThrough();
$httpBackend.whenJSONP(/.*/).passThrough();
$httpBackend.whenPUT(/.*/).passThrough();
});

This plunk shows how I go about mocking resource objects, from a angular service, in a controller. I use SinonJs to fake a resource object. Then I basically fake the promise chain by injecting $q.
To fake the promise chain you need to get a defer object from $q, then get a promise from it.
In your tests, you then either fake a success or a failure by calling promise.resolve() or promise.reject() on that promise. You can fake data from the server by passing an object in as a parameter like this promise.reject(someData).
You then have to make sure that you scope.apply(). To make sure that whatever it is you wanted to do becomes visible on scope.
I'm not entirely sure if this is the right way to go about this, but it has been working for me.

Related

Angular Unit tests: Mocking multiple independent promises

This is a long one, so I will begin by asking the question I struggle with:
How do I resolve independent promises for the same function that has been run with different parameters in unit testing, and get different values?
I have difficulties with mocking an environment where multiple http-requests are executed, independent of each other, but with the same service-object.
It works in real application, but setting up a proper mocking environment for unit-testing (Jasmine, Karma) has proven quite difficult.
Let me explain the environment, and what I have tried to to:
First off, I have an Angular Controller that makes a single http-request with a custom service object, and mocking this in the tests works. Then I have made a Controller that makes multiple independent http-requests with the same service object, and I have attempted at expanding my unit testing to cover this one, given my success with the other controller.
Background on how it works in controller with single request/promise:
If you don't want to go through all this, you can jump straight to The real problem: Testing multiple independent requests and promises. You probably should.
Let us first go with the single-request controller and its working test, to have a foundation.
SingleRequestController
function OpenDataController($scope, myHttpService) {
$scope.parameterData = {requestString : "A"};
$scope.executeSingleRequest = function() {
myHttpService.getServiceData($scope.parameterData)
.then(function (response) {
$scope.result = response.data;
});
}
// Assume other methods, that calls on $scope.executeSingleRequest, $scope.parameterData may also change
}
As you probably figure, myHttpService is a custom service that sends a http-request to a set URL, and adds in the parameters passed on by the controller.
SingleRequestControllerTest
describe('SingleRequestController', function() {
var scope, controller, myHttpServiceMock, q, spy;
beforeEach(module('OppgaveregisteretWebApp'));
beforeEach(inject(function ($controller, $q, $rootScope, myHttpService) {
rootScope = $rootScope;
scope = rootScope.$new();
q = $q;
spy = spyOn(myHttpService, 'getServiceData');
// Following are uncommented if request is executed at intialization
//myHttpServiceMock= q.defer();
//spy.and.returnValue(myHttpServiceMock.promise);
controller = $controller('OpenDataController', {
$scope: scope,
httpService: httpService
});
// Following are uncommented if request is executed at intialization
//myHttpServiceMock.resolve({data : "This is a fake response"});
//scope.$digest();
}));
describe('executeSingleRequest()', function () {
it('should update scope.result after running the service and receive response', function () {
// Setup example
scope.parameterdata = {requestString : "A", requestInteger : 64};
// Prepare mocked promises.
myHttpServiceMock= q.defer();
spy.and.returnValue(myHttpServiceMock.promise);
// Execute method
scope.executeSingleRequest();
// Resolve mocked promises
myHttpServiceMock.resolve({data : "This is a fake response"});
scope.$digest();
// Check values
expect(scope.result).toBe("This is a fake response");
});
});
});
This is a light-weight pseudo copy of a real life implementation I'm working with. Suffice to say, I have, through trying and failing, discovered that for each and every call on myHttpService.getServiceData (usually by directly calling $scope.executeSingleRequest, or indirectly through other methods), the following has to be done:
myHttpServiceMock must be initialized anew (myHttpServiceMock= q.defer();),
initialize spy to return mocked promise (spy.and.returnValue(myHttpServiceMock.promise);)
Execute the call to the service
Resolve the promise (myHttpServiceMock.resolve({data : "This is a fake response"});)
Call digest (q.defer();)
So far, it works.
I know it's not the most beautiful code, and for each time the mocked promise has to be initialized and then resolved, a method encapsulating these would be preferable in each test. I've chosen to show it all here for demonstrative purpose.
The real problem: Testing multiple independent requests and promises:
Now, let us say the controller does multiple independent requests to the service, with different parameters. This is the case in a similar controller in my real life application:
MultipleRequestsController
function OpenDataController($scope, myHttpService) {
$scope.resultA = "";
$scope.resultB = "";
$scope.resultC = "";
$scope.resultD = "";
$scope.executeRequest = function(parameterData) {
myHttpService.getServiceData(parameterData)
.then(function (response) {
assignToResultBasedOnType(response, parameterData.requestType);
});
}
$scope.executeMultipleRequestsWithStaticParameters = function(){
$scope.executeRequest({requestType: "A"});
$scope.executeRequest({requestType: "B"});
$scope.executeRequest({requestType: "C"});
$scope.executeRequest({requestType: "D"});
};
function assignToResultBasedOnType(response, type){
// Assign to response.data to
// $scope.resultA, $scope.resultB,
// $scope.resultC, or $scope.resultD,
// based upon value from type
// response.data and type should differ,
// based upon parameter "requestType" in each request
...........
};
// Assume other methods that may call upon $scope.executeMultipleRequestsWithStaticParameters or $scope.executeRequest
}
Now, I realize that "assignToResultBasedOnType" may not be the best way to handle the assignment to the correct property, but that is what we have today.
Usually, the four different result-properties receive the same type of object, but with different content, in the real life application.
Now, I want to simulate this behavior in my test.
MultipleRequestControllerTest
describe('MultipleRequestsController', function() {
var scope, controller, myHttpServiceMock, q, spy;
var lastRequestTypeParameter = [];
beforeEach(module('OppgaveregisteretWebApp'));
beforeEach(inject(function ($controller, $q, $rootScope, myHttpService) {
rootScope = $rootScope;
scope = rootScope.$new();
q = $q;
spy = spyOn(myHttpService, 'getServiceData');
controller = $controller('OpenDataController', {
$scope: scope,
httpService: httpService
});
}));
describe('executeMultipleRequestsWithStaticParameters ()', function () {
it('should update scope.result after running the service and receive response', function () {
// Prepare mocked promises.
myHttpServiceMock= q.defer();
spy.and.callFake(function (myParam) {
lastRequestTypeParameter.unshift(myParam.type);
return skjemaHttpServiceJsonMock.promise;
// Execute method
scope.executeMultipleRequestsWithStaticParameters();
// Resolve mocked promises
myHttpServiceMock.resolve(createFakeResponseBasedOnParameter(lastRequestTypeParameter.pop()));
scope.$digest();
// Check values
expect(scope.resultA).toBe("U");
expect(scope.resultB).toBe("X");
expect(scope.resultC).toBe("Y");
expect(scope.resultD).toBe("Z");
});
});
function createFakeResponseBasedOnParameter(requestType){
if (requestType==="A"){return {value:"U"}}
if (requestType==="B"){return {value:"X"}}
if (requestType==="C"){return {value:"Y"}}
if (requestType==="D"){return {value:"Z"}}
};
});
This is what happens in the test (discovered during debug):
The spy function runs four times, and pushes in the values to the array lastRequestTypeParameter, which will be [D, C, B, A], which values are supposed will be popped to read A-B-C-D, to reflect the real order of the requests.
However, here comes the problem: Resolve happens only once, and the same response is created for all four result-properties: {value:"U"}.
The correct list is selected internally, because the promise-chain uses the same parameter values as was used in the service-call (requestType), but they all receive data only on the first response. Thus, the result is:
$scope.resultA = "U"; $scope.resultB = "U", and so on.... instead of U, X, Y, Z.
So, the spy function runs four times, and I had assumed that four promises were returned, one for each call. But as of now, there is only one resolve() and one q.digest().
I have tried the following, to make things work:
Four q.defer()
Four resolves
Four digests
Return an array with four different objects, corresponding to what I would expect in working test. (Silly, I know, it differs from the expected object structure, but what don't you do when you try to tweak anything to get a surprisingly working result?).
None of these work. In fact, the first resolve causes the same result to all four properties, so adding more resolves and digests will make little difference.
I have tried to Google this issue, but all I find are either multiple promises for different services, multiple chain-functions (.then().then()...), or nested asynchronous calls (new promise object(s) inside chain).
What I need is a solution for independent promises, created by running the same function with different parameters.
So, I will end with the question I opened up with:
How do I resolve independent promises for the same function that has been run with different parameters in unit testing, and get different values?
Jasmine is Angular-friendly Jack of all trades. It is generally suitable for the majority of front-end testing cases. It lacks in spying/mocking functionality, while Sinon offers much more power.
This may be the reason why Mocha/Sinon/Chai modular bundle may be preferred at some point, but the good thing about its modularity is that Sinon isn't tied to the bundle. Besides its tight relations with Chai, it can also be used with Jasmine matchers.
The thing that makes Sinon a better choice than Jasmine spies is that it is capable of programming spies expectations (withArgs(...).called...) and stubs responses (withArgs(...).returns(...)). Blue-collar mocking becomes a piece of cake:
var sandbox;
var spy;
// beforeEach
sandbox = sinon.sandbox.create();
// similar to Jasmine spy without callThrough
spy = sandbox.stub(myHttpService, 'getServiceData');
...
// it
spy.withArgs('A').returns({value:"U"});
spy.withArgs('B').returns({value:"X"});
...
// afterEach
sandbox.restore(); // the thing that Jasmine does automatically for its spies
Regarding once-resolved promise, this is the expected behaviour. As a rule of thumb fresh promises should be returned from mocked functions, never an existing object with .returnValue in Jasmine (or .returns in Sinon).
A callback function should be used to return a fresh promise on each call. If the promise should be resolved with predefined value, there may be several patterns to achieve this, the most obvious is using a variable
var mockedPromiseValue;
...
spy = spyOn(myHttpService, 'getServiceData')
.and.callFake(() => $q.resolve(mockedPromiseValue));
...
mockedPromiseValue = ...;
myHttpService.getServiceData().then((result) => {
expect(result).toBe(...);
})
// rinse and repeat
$rootScope.$digest();

Deferring promises in Karma test of angular ngResources

Currently I'm using ngResource for my RESTful API calls, and I'm using KARMA & jasmine to do my unit and integration tests.
Inside one Controller i have a function that expects promise to be finished:
var elem = new Element() // calling a ngResource Factory
elem.$save().then(function () {
$scope.elem.push(elem);
});
In my karma tests, i test if the list is empty, that call the function above and check if the $scope.elem Array does have the new created element. But since its a promise, KARMA test does not solve this. I tried to use $rootScope.apply(), but the $httpBackend expects that i define lots of calls, that are expected. But i just want to simulate the call.
Is there any elegant solution for that?
There is not elegant solution for this. Main purpose of testing is prepare "isolated" ecosystem for test. If you want to test ajax call , they must return something. You cant test and just tell "skip this promise and act as like it was success".
When you need to resolve any promise, i am using this.
$httpBackend.flush();
$rootScope.$apply();
This will call $httpBackend, and ofcourse it will expecting call. You have no choice
1.) Mock all backend calls (this is taken from my test)
identityBackend = $httpBackend.when("GET", AppConfig.API_IDENTITY_ENDPOINT + "/me",null,function(headers)
{
return headers.Authorization !== undefined;
}).respond(200, fakeAuthUser);
So it will respond with http 200 with fake json on request host/me , when authorization token inside headers is not undefined.
2.) Second choice, create mockable backend inside nodejs/express and mock all requests with jsons. Before starting jasmine test , you will also start this "fake" backend server.

How does AngularJS return a value from async call?

Watch this video,
https://www.youtube.com/watch?v=IRelx4-ISbs
You'll find that the code has a line wrote this:
$scope.twitterResult = $scope.twitter.get({q:$scope.searchTerm});
This is a litter quirk:
the 'get' method of 'twitter' is obviously a async function, how does it return a value to $scope.twitterResult???
jsFiddle(Can't work cause the twitter API has changed):
http://jsfiddle.net/johnlindquist/qmNvq/
This code $scope.twitter.get({q:$scope.searchTerm}); return defer object. Angular view arranged so that view will be update when defer object resolve. It's just a convenience.
$scope.twitterResult = $scope.twitter.get({q:$scope.searchTerm});
Your code is good for only angular binding.
But if you need to get data on run time, you need to write this below format.
If $scope.twitter is a URL,Then write it
$http.get($scope.twitter, {q:$scope.searchTerm}).success(function (response) {
$scope.twitterResult=response;
}
$http is must defined in contoller

Dojo and doh: test for specific topic subscriptions

Does anyone know a way to use doh to test if an object is subscribed to a specific topic?
I am struggling to find any docs desccribing this type of test.
The reason that I am asking this is becuase when I construct my widget I subscribe it to a topic. I wanted to have a unit test that tests if the widget always has that topic subscription after its construction.
My topic has a private variable that I use as the topic string when creating the subscription.
So for example here is a topic called "CustomTopic":
define([], function(){
var topicString= "topicString";
return {
TOPIC_STRING: function(){
return topicString;
}
}
})
and the constructor in my widget looks like:
constructor: function() {
topic.subscribe(CustomTopic.TOPIC_STRING(), function(params) {doSomething(params)});
}
So you can see how easy it would be to check for the topic subscription against the private variable value, if I could just figure out how to see all subscriptions my widget has?
For reference:
Dojo 1.8 docs
Dojo's test util "doh" docs
I suggest your testing will be more robust / useful if it concentrates on behaviour as opposed to implementation. In this case, it would make more sense to test if your widget responds to the topic (or better still, the event that causes the topic to be published, for more of an integration test) rather than attempting to catch the subscription itself.
Of course, you could try and wrap topic.subscribe (bad thing), or inspect your widget's private list of handles (another bad thing). Incidentally, I hope you are in fact saving the handle returned by topic.subscribe so that you can remove (previously, unsubscribe) it later when the object is destroyed.
Better than those would be to simply make a new object, publish to that topic, and see if doSomething is called, or if the desired result has occurred. While doh does not provide support for listening to function calls, dojo/aspect is ideal for this purpose. So you might have something like this :
var myWidget = new myWidget(); // presumably subscription happened,
// but we only care about later behaviour
var somethingDone = false;
aspect.after(window, "doSomething", function(){somethingDone = true;});
topic.publish(CustomTopic.TOPIC_STRING());
doh.assertTrue(somethingDone);
In fact, I assume doSomething is not a global method, so you'll have to scope that properly, but otherwise this should work fine. Currently topic.publish is synchronous, so this should work fine, but it could become async in the future, in which case you would want to avoid calling doh.assertTrue until some later stage.

Wrapping Backbone sync requests

I am writing a Backbone application, and I need to offer some feedback to users whenever a request to the server is made (annoying, I know, but I have no control over this behaviour of the application). The backend always reports an informative (at least in theory) message with every response, like
{
"status":"error",
"message":"something went really wrong"
}
or
{
"status":"success",
"message":"congratulations",
"data":{...}
}
What I would like to understand is where to put a hook for some kind of messaging service.
One possibility is the parse() method for models and collections. To avoid duplication, I would have to put it inside some model base class. It is still a bit annoying since all models and collections have their own parse() anyway.
A more reasonable place to look would be the Backbone.sync function. But I do not want to overwrite it, instead I would like to wrap it inside some other helper function. The problem here is that I cannot find a good hook where to put some logic to be executed with every request.
Do you have any suggestions on how to organize some piece of logic to be executed with every request?
Since Backbone.sync returns whatever $.ajax returns, it is easy to achieve what I want by using jQuery delegates, like this
var originalMethod = Backbone.sync;
Backbone.sync = function(method, model, options) {
var request = originalMethod.call(Backbone, method, model, options);
request.done(function(msg) {
console.log(msg);
});
request.fail(function(jqXHR, textStatus) {
console.log(jqXHR, textStatus);
});
return request;
};
Assuming you are using a recent (>1.5) jquery all results from sync will return the $.ajax promise.
You can do it then without overriding anything in sync by using that promise. For example, if you did a fetch(), you could do:
var p = mymodel.fetch();
p.done(function (res) { ... });
p.fail(function (err) { ... });
Of course you can also use callbacks in fetch options, but I find the above much cleaner. The same pattern applies for say save or anything that uses sync.

Categories

Resources