Possible to write test for controller without $httpbackend? - angularjs

Im trying to understand Angularjs. I have a controller that contains one function and one http.get. Is it possible to write a Karma unittest for the function without mocking $httpbackend? Right now i get the error unexpected http call and the test newer runs.

If you are using angular-mocks.js, which I assume you are as you're using Karma, $httpBackend is already mocked. You just need to define your expectations, which can be very loose. Like so:
$httpBackend.when('GET', '/expected-url').respond(200, '');
If you want to use the real $httpBackend, don't include ngMock, but I won't recommend that :)

Related

When mocking a service, do I need to know exactly what it does and what each call returns?

New to karma and jasmine, setting up tests on a project I'm not very familiar with. In order to mock a service in a controller test, do I need to set up fake functions that return data in the format expected? Or is there an easier way to do this than going back and forth trying to set up dummy data, etc
I recommend getting Sinon.js, if you have Sinon all you have to do is get the interface and then you can tell it what each function should return in your tests.
Check out my answer for this post here.
Jasmine, Karma, Angular how to write test on my Angular app?

Angular ngmock httpBackend - ignore all but one request?

I am new to writing unit tests, so apologies if this is a dumb question. If I'm trying to test a method in an Angular controller that relies on mock data from a service call, and I want to also mock that service call (ngResource), is there a way to make httpBackend ignore other requests made in my controller on initialization?
I've tried placing my whenGET or expectGET definitions in before blocks, and only instantiating my controller within my test, but I always find that httpBackend is expecting other requests (Error: Unexpected request) when I call flush(). I do not want to write mocks for all other requests, just the one I'm using for this test.
Of course, this may be a stupid idea, as I can also just provide the fake data directly, and not test the service along with the controller's method. I've verified that this works. Maybe the correct answer is that I shouldn't be testing services from within a controller.
FWIW, I've also tried using Sinon fakeServer, and apparently it doesn't even pick up on Angular's XHR implementation (the server never responds).

Using Jamine to test an AngularJS service without mocks

I would like to use Jasmine to write tests (not unit tests) for an AngularJS service I have, which is a wrapper for a REST API I created on my server. The service call should actually get all the way to the server. No mocking needed.
I want to be able to test some scenarios involving combinations of few of these API calls.
I understand I should probably not be using angular-mocks.js but I can't figure out the syntax for getting access to the service instance in the test.
I have something like the code below. As you can see where the ?? I would like to assign the service reference to myService so I could use it in the tests.
beforeEach(function () {
module("myApp");
myService = ??
});
Also, should I include only the service file in the specRunner.html references list?
You will just need to have something like the following:
$httpBackend.whenGET(/\/your-url-here-\/.*/).passThrough();

Breezejs unit test with jasmine karma angular

I'm building an app based in Breeze and Angular
They work pretty well together but the unit test is a problem.
This is a pretty vanilla test but Breeze keeps getting in the middle:
describe('myController', function () {
beforeEach(inject(function ($injector) {
module('app');
$httpBackend = $injector.get('$httpBackend');
authRequestHandler = $httpBackend.whenGET().respond(200,
{"someStrings": ["foo", "bar"]})
//more uninteresting code...
createController = function () {
return $controller('myController', { '$scope': $rootScope });
};
}));
it('should fetch authentication token', function () {
$httpBackend.expectGET('/auth.py');
var controller = createController();
$httpBackend.flush();
});
The problem is that Breeze keeps being initialized. At execution, I receive the following message:
Error: cannot execute _executeQueryCore until metadataStore is populated.
//or,with different get: ... $httpBackend.when('GET', '/auth.py')
// .respond({ userId: 'userX' });
Error: Unexpected request: GET breeze/Breeze/Metadata No more request expected
How do I prevent or mock or stub Breeze so doesn't interfere with my tests... For instance, these tests are aimed to authentication, not data.
Breeze is not "getting in the middle" on its own. Breeze would not get involved in your $http authorization call. I'll eat my hat if you can show me that it does. You haven't shown that it does here.
But you have surfaced a very interesting point about application bootstrap design and the consequences of that design for testing.
Evidently, either your app module's start method or your controller's creation logic executes a Breeze query (perhaps both of them do). I deduce this from two facts:
The exception comes from executeQueryCore which only happens when you explicitly execute a Breeze query
You don't touch the controller in your test, neither in the beforeEach nor in the it which means these calls (and your auth call too) are made by some kind of automatic startup logic that executes before your it spec.
In your test you have taken the trouble to mock the auth call (which is in your startup logic somewhere) but not the Breeze calls.
I don't know what you actually want to test. Why would you test that the controller fetches an auth token? Is that really the controller's concern?
Perhaps you present this test merely to illustrate the problems you're having testing a controller without getting the real server involved?
Let me step back and make a more important and more general point. We must be wary of automatic startup logic whether it hides in an app module start or a controller's constructor. Be wary in particular of startup logic that involves calls to the server.
I tend to disable automated startup logic in most of my tests. I often substitute test doubles for the troublesome dependent services during my test module setup ... before calling ngMock's inject function. I make sure that the app.start method's callback ONLY uses dependent services that are easy to fake.
I you want to forge ahead using the actual dependencies by mocking the HTTP responses with $httpBackend, then you'll have to prepare $httpBackend for every request it receives from the startup code ... including the requests YOU are making with Breeze.
I'll end by reiterating that Breeze only does what you tell it to do. It is completely unaware of your direct-to-$http calls.

Angularjs suitable moment to launch a http request when app start

I just started to learn Angularjs 2 days ago. A question have confused me for all the 2 days.
I need to make a http request to the server to get some data when the app start, but I cannot find the suitable moment to do this.
I've tried to make a controller, which calls $http.get(). But It doesn't work. It seems that the controller won't be instantiated if it's never used in the html (not sure about this).
I also tried to find other ways, but I only found $http which is used for http request. And $http only appears in the controller.
Maybe I need use other Angularjs methods? Or, I should do the instantiate action manually?
Something like:
angular.module('yourApp').run(['$rootScope', '$http', function ($rootScope, $http) {
// do stuff here
}]);
From the documentation:
Run Blocks
Run blocks are the closest thing in Angular to the main method. A run block is the code which needs to run to kickstart the application. It is executed after all of the service have been configured and the injector has been created. Run blocks typically contain code which is hard to unit-test, and for this reason should be declared in isolated modules, so that they can be ignored in the unit-tests.

Resources