I have a resource which wraps a RESTful API. I use that resource from my controller to create new objects and save them, similar to this snippet from the Angular docs:
var newCard = new CreditCard({number:'0123'});
newCard.name = "Mike Smith";
newCard.$save();
When writing a unit test in Jasmine, I get the following error when the $save call is executed: "Cannot read property '$promise' of undefined".
What's the best approach to testing the method in my controller which contains the above code?
If you use Jasmine's spyOn() function to verify that a $resource method is called, it overwrites the original $resource method with one that implements the "spying" functionality.
If the code in your application relies on the $resource setting the $promise property, or it relies on the returned object/array from the $resource, the Jasmine's spy function won't return anything or set a value on the $promise property. As a result, perfectly fine code in your application will fail when being tested. A similar thing happens when you use $http with the then(), success(), or error() functions.
To work around that you can make Jasmine spy on the function as well as call the original function by doing something like this:
// Newer Jasmine 2.0 syntax:
spyOn(resource, "$save").and.callThrough();
// Older syntax:
spyOn(resource, "$save").andCallThrough();
Related
I haven't been able to find documentation in AngularJS docs on use-cases of $get. I'm trying to understand where and how to use it.
Below is an example from the AngularJS docs
function GoodProvider() {
this.$get = angular.noop;
}
Does this attach angular.noop to the GoodProvider function?
Yes, it attaches noop.
Look at the documentation on Providers, https://docs.angularjs.org/guide/providers
"The Provider recipe is syntactically defined as a custom type that implements a $get method. This method is a factory function just like the one we use in the Factory recipe. In fact, if you define a Factory recipe, an empty Provider type with the $get method set to your factory function is automatically created under the hood."
I want to have an assert that checks if the returned object from calling a method is a promise.
Is it enough to check if it exists and has a then method?
Or is there a better way of doing this since having a then method doesn't guarantee the object is a $q deferred promise?
EDIT:
The question is not duplicate of "Any way to know if a variable is an angularjs promise?".
The solution for that question is to ensure that a promise is returned using $q.when().
I'm asking how to know that an object is a promise, not how to make it one if it isn't.
Thanks.
As far as I can tell, there is no way to do this directly.
When constructing objects, i normally do it like this:
function Derp(){
this.constructor = Derp;
}
Derp.prototype.method = function(){};
So that I can:
expect( new Derp() instanceof Derp ).toBe(true);
but in both $q.promise and $q itself, there are no constructor or prototype properties except for the constructor property in their __proto__ (in chrome) objects; and even those just point to JavaScripts' fundamental Object.
So in order to test that something is a promise, you have to make the assumption that the object you're returning is a promise based on its' other properties:
expect(typeof result.when).toBe('function');
expect(typeof result.then).toBe('function');
I don't like it either, but that's the only way I can think of to do this.
In my AngularJS Controller, I have a $scope.init method that gets called when the controller gets initialized. $scope.init calls $scope.loadData and $scope.loadNames, which each fire an HTTP GET Request to an API to fetch some data.
I have written some unit tests using $httpBackend to mock the API responses and test $scope.loadData and $scope.loadNames. Those work fine but something that I can't seem to test is the $scope.init method. Is there a way to mock or stub a method that gets called on controller initialization? What are the best practices for this?
You can verify that the scope objects changed in $scope.init have the proper values after the controller initialization. You can even try to change the values of those objects, and rerun the init method, and check that everything matches afterwards
You can test:
the state of the controller is as expected after it is "initialized"
verify that loadnames and loaddata gets called (jasmine's spyOn+toHaveBeenCalled)
play with a promise object mocking the loadnames/loaddata methods response to verify your behavior under unexpected conditions
Here is the sample code:
App.controller('Crtl', ["$scope", function ($scope) {
$scope.FetchDetail = function () {
var accNum = this.customer.accNo;
GetAccountDetails(accNum);
}; }]);
I am new to Jasmine and right now I am writing unit tests for my angular code. Here I created FetchDetail function which then calls javascript function GetAccountDetails(accNum).
How can I test this sample using Jasmine.
It depends if you need to stub it (i.e. capture and change its behaviour) or if it is enough to spy on it (watch it). Either way, you would be better off injecting it, so you can control it.
I have been using sinon http://sinonjs.org quite happily.
If you want to spy on it, i.e. watch its behaviour without changing it, then before calling it, you would do
var spy = sinon.spy(GetAccountDetails);
Then you can check if it was called, or what arguments were, etc.
spy.calledOnce === true; // if it was called once
spy.called === true; // if it was called at all
spy.firstCall.args[0]; // first argument to first call
// etc. - check out the docs
If you need to stub it, use the stub functionality, where you can then define what the behaviour should be. You get all of the spying features, but also control what the response is. However, it is hard to spy on a global, as opposed to a method of an existing object.
I'm using Jasmine to unit test my Angular app. It's pretty easy to test if a method of a function has been called using something like:
spyOn($rootScope, "$emit");
expect($rootScope.$emit).toHaveBeenCalled();
But I can't find a way to check when a function has been called (without a method), for e.g. I'm using $anchorScroll(); in one controller and I have no idea where to apply the above code to this guy. I've seen some Jasmine examples where they were using expect(window.myFunction()).toHaveBeenCalled(), but this doesn't work with Angular's DI.
I can't try it myself at the minute but maybe you could just inject a mock $anchorScroll instead?
var $anchorScroll = jasmine.createSpy('anchorScroll');
$controller('MyCtrl', {
$anchorScroll: $anchorScroll
});
expect($anchorScroll).toHaveBeenCalled();
This should just create a blank spy, one which will take any arguments and do nothing but keep track of the calls for test usage.