Angular Unit tests: Mocking multiple independent promises - javascript

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();

Related

Difference between fake, spy, stub and mock of sinon library ( sinon fake vs spy vs stub vs mock )

I tried to understand difference between sinon library's fake, spy, stub and mock but not able to understand it clearly.
Can anybody help me to understand about it?
Just for understanding purpose call:
FuncInfoCollector = is a Function that records arguments, return value, the value of this(context) and exception thrown (if any) for all of its calls.
(this FuncInfoCollector is dummy name given by me, it is not present in SINON lib)
Fake = FuncInfoCollector + can only create a fake function. To replace a function that already exists in the system under test you call sinon.replace(target, fieldname, fake). You can wrap an existing function like this:
const org = foo.someMethod;
sinon.fake((...args) => org(...args));
A fake is immutable: once created, the behavior can't be changed.
var fakeFunc = sinon.fake.returns('foo');
fakeFunc();
// have call count of fakeFunc ( It will show 1 here)
fakeFunc.callCount;
Spy = FuncInfoCollector + can create new function + It can wrap a function that already exists in the system under test.
Spy is a good choice whenever the goal of a test is to verify something happened.
// Can be passed as a callback to async func to verify whether callback is called or not?
const spyFunc = sinon.spy();
// Creates spy for ajax method of jQuery lib
sinon.spy(jQuery, "ajax");
// will tell whether jQuery.ajax method called exactly once or not
jQuery.ajax.calledOnce
Stub = spy + it stubs the original function ( can be used to change behaviour of original function).
var err = new Error('Ajax Error');
// So whenever jQuery.ajax method is called in a code it throws this Error
sinon.stub(jQuery, "ajax").throws(err)
// Here we are writing assert to check where jQuery.ajax is throwing an Error or not
sinon.assert.threw(jQuery.ajax(), err);
Mock = Stub + pre-programmed expectations.
var mk = sinon.mock(jQuery)
// Should be called atleast 2 time and almost 5 times
mk.expects("ajax").atLeast(2).atMost(5);
// It throws the following exception when called ( assert used above is not needed now )
mk.expects("ajax").throws(new Error('Ajax Error'))
// will check whether all above expectations are met or not, hence assertions aren't needed
mk.verify();
Please have a look at this link also sinon.replace vs sinon.stub just to replace return value?
Just to add some more info to the otherwise good answer, we added the Fake API to Sinon because of shortcomings of the other original APIs (Stub and Spy). The fact that these APIs are chainable led to constant design issues and recurring user-problems and they were bloated to cater to quite unimportant use cases, which is why we opted for creating a new immutable API that would be simpler to use, less ambiguous and cheaper to maintain. It was built on top of the Spy and Stub Apis to let Fakes be somewhat recognizable and have explicit method for replacing props on objects (sinon.replace(obj,'prop',fake)).
Fakes can essentially be used anywhere a stub or spy can be used and so I have not used the old APIs myself in 3-4 years, as code using the more limited fakes is simpler to understand for other people.

Node.js how should I unit test a function calling other functions

I want to unit test a module I've built.
To give an impression of what it looks like pretty much..
MyModule:
function MyModule(...) {
var self = this;
MyModule.init.call(self, ...);
}
util.inherits(MyModule, MySuperModule);
MyModule.init = function(...) {
...
};
MyModule.prototype.logic = function(..., done) {
calls fnA, fnB, fnC, fnD conditionally
done(...)
};
MyModule.prototype.fnA = function(...) {
...
};
MyModule.prototype.fnB = function(...) {
...
};
MyModule.prototype.fnC = function(...) {
...
};
MyModule.prototype.fnD = function(...) {
...
};
MySuperModule:
function MySuperModule(...) {
...
}
MySuperModule.prototype,fn = function(..., done) {
var self = this;
...
self.logic(..., function done(...) {
...
done(...)
});
}
Now MyModule.logic() is never called explicitly by a user, it is only invoked MySuperModule.fn().
Same goes for all other MyModule functions which are called conditionally based on the given parameters being passed through the delegating chain.
My questions are as follow:
Do I need to test all MyModule functions separately or just test MySuperModule.fn() with different parameters covering all possible scenarios
I know I need to test function in isolation (which if I do my previous question is wrong to ask because than I won't really have tested MyModule functions at all), how would I do that with the MySuperModule.fn(), because its done() callback is being called with arguments dependent on what the MyModule.logic() done() callback was called with, which is again, dependent on the arguments supplied to MySuperModule.fn() arguments.
It really depends how you're injecting MyModule on MySuperModule. But first of all I would point out that in unit tests you have to test MyModule separately and MySuperModule with a Mocked version from MyModule and all other dependencies. This is because you don't want to test MyModule twice, no need for that.
So to create stubs there is a library called Sinon.JS which works really fine.
So if for any reason you just want to make a spy to MyModule, which means you are just attaching a listener to MyModule (it is applied to MyModule methods) which counts and tells if a given method is ever called and how.
var MyModuleMethodASpy = sinon.spy(MyModulem 'MethodA');
MySuperModule.someMethod();
assert(MyModuleMethodASpy.called)
So this code creates a spy, triggers some method on MySuperModule and checks if MyModule.MethodA() is ever called.
You can create stubs as well if you want to control what dependencies return on specific methods eg :
var MyModuleStub = sinon.stub(MyModule, 'MethodA').returns([...somedata]);
In my view you should certainly be testing the individual functions, regardless of whether or not they're called directly by a user.
The purpose of unit testing is to try to ensure that the individual units of your test do what they're expected to do. If you're (relatively) sure that your individual functions/units behave as expected, you can have more confidence that they'll work nicely with each other.
It's hard to really glean from your code snippets the nature of your module, so suggesting how to implement your tests is difficult. However, it seems like what you're asking is how to verify whether your done/callback function is called and with which arguments.
For that I would recommend using a stub. I usually use sinon but I'm sure other similar tools are available.
var sinon = require( "sinon" );
var should = require( "chai" ).should();
var yourModule = require( "your-module" );
var doneStub = sinon.stub();
yourModule.yourFunction( ..., doneStub );
doneStub.should.have.been.called;
var args = doneStub.getCall( 0 ).args;
args[ 0 ].should.be.eql( ... );
// etc etc
You should also consider using a test runner, I like mocha!
You should do progressive testing. You should test each and every function.
Here how can you proceed.
Write test case for parent function. Mock the inner function where it is calling. You can use sinon library for mocking.
For second question, you can use sinon mock's yield functionality to mock any callback function and you can specify also which output you want from that callback. In this way you can test your function for multiple custom output with different scenario.

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 to properly test an AngularJS Controller Function

We just started implementing jasmine tests in our AngularJS project and I have a question:
We want to test this controller function:
$scope.deleteClick = function () {
$scope.processing = true;
peopleNotesSrv.deleteNote($scope.currentOperator.operatorId, $scope.noteId, $scope.deleteSuccessCallback, $scope.deleteErrorCallback);
};
We wrote this following test:
it('deleteClick should pass proper parameters to peopleNoteSrv', function () {
$controllerConstructor('PeopleNoteEditCtrl', { $scope: $scope });
$scope.noteId = 5;
expect(function () { $scope.deleteClick(); }).not.toThrow();
});
This test makes sure that when we call the $scope.deleteClick() function that $scope.processing is set to true and that the call to peopleNotesSrv doesn't throw any errors because of invalid arguments. We are testing the two callback functions in separate tests.
Should we be testing that the peopleNotesSrv.deleteNote function was called so the test is more explicit? The way this test is written right now it doesn't really tell someone what the deleteClick() function does under the hood and that seems to be incorrect.
Ask yourself what you'd do if you had developed it using TDD. It pretty much goes the direction Sam pointed out, but here are some examples:
Controller Tests
start writing a test which would expect a deleteClick to exist.
Expect deleteClick to setup the loading state (check for processing = true)
Test whether a service is injected into the controller (peopleNotesSrv)
Check whether deleteClick calls the service (as already mentioned via spies)
Verify that $scope.noteId and the other $scope.params are present and set
This is as far as it relates to the Controller. All the criteria whether it fails or throws errors etc. should be tested in a Service.spec. Since I don't know your service in detail here some examples
Service Tests
Ensure deleteNote exists
Check what happens if wrong number of arguments (less or more) are supplied
Make some positive tests (like your noteId = 5)
Make some negative tests
Ensure callbacks are properly called
... and so on.
Testing for validity in controllers doesn't make a lot of sense because than you'd need to do it for every Controller you have out there. By isolating the Service as a separate Unit of Test and ensure that it fulfills all the requirements you can just use it without testing. It's kinda the same as you never would test jQuery features or in case of Angular jQLite, since you simply expect them to do what they should :)
EDIT:
Make controller tests fail on service call
Pretty easy lets take this example. First we create our Service Test to ensure that the call fails if not the proper number of arguments is supplied:
describe('Service: peopleNoteSrv', function () {
// load the service's module
beforeEach(module('angularControllerServicecallApp'));
// instantiate service
var peopleNoteSrv;
beforeEach(inject(function (_peopleNoteSrv_) {
peopleNoteSrv = _peopleNoteSrv_;
}));
it('should throw error on false number of arguments', function () {
expect(function() { peopleNoteSrv.deleteNote('justOneParameter'); }).toThrow();
});
});
Now to ensure that the test passes lets create the error throwing part in our service method
angular.module('angularControllerServicecallApp')
.service('peopleNoteSrv', function peopleNoteSrv() {
this.deleteNote = function(param1, param2, param3) {
if(arguments.length !== 3)
throw Error('Invalid number of arguments supplied');
return "OK";
};
});
Now lets create 2 demo controllers, FirstCtrl will do it properly, but SecondCtrl should fail
angular.module('angularControllerServicecallApp')
.controller('FirstCtrl', function ($scope, peopleNoteSrv) {
$scope.doIt = function() {
return peopleNoteSrv.deleteNote('param1', 'param2', 'param3');
}
});
angular.module('angularControllerServicecallApp')
.controller('SecondCtrl', function ($scope, peopleNoteSrv) {
$scope.doIt = function() {
return peopleNoteSrv.deleteNote('onlyOneParameter');
}
});
And both controller as a demo have following test:
it('should call Service properly', function () {
expect(scope.doIt()).toBe("OK");
});
Karma now spits out something like this:
Error: Invalid number of arguments supplied
at [PATH]/app/scripts/services/peoplenotesrv.js:15
at [PATH]/app/scripts/controllers/second.js:13
at [PATH]/test/spec/controllers/second.js:20
Thus you exactly know that you missed to update SecondCtrl. Of course this should work for any of your tests consuming the Service method.
Hope that's what you meant.
I think the answer is that it depends.
There are two cases:
1 - You also have a suite of tests for the peopleNotesSrv service.
In this case I would leave this test as-is or check a few more things around the specific functionality of $scope.deleteClick(), such as if there are any watchers on $scope.processing that do anything specific regarding a .deleteClick() call.
2 - You do not have any tests for all the possible functionality for the peopleNotesSrv service.
In this case I would write a more explicit test that does check that the .deleteNote() actually performed it's job.
In my opinion you should really build tests up and try to not test the same thing in more than one place, as this adds extra work and could produce holes in the tests if you think, "Well I can just test this specific case when it gets called from a specific function that calls it."
What if you ever want to reuse that deletNote() as part of a bigger function in a different place?Then you need to write another test for the same code because it is being called from a different function.
So I would aim for case 1, this way you can write all your tests for that service and then trust that those tests cover the rest of this particular test. If you throw errors on bad input or for failures to actually delete a note, you should trust that other code to test what it was designed to test. This will greatly speed up your test-writing time and increase the chance that your tests cover all the cases. It also keeps all the tests for that service in the same place in your test code.
I think also a good question to start with is what kind of test is this? Unit Test or End-to-End test?
I was assuming it was a Unit Test for my answer, if it was an End-to-End test, then you might want to keep following the function calls to verify everything is happening as you expect.
Here are some links on Unit Tests, End-to-End tests, and a pretty good article about both and Angular.
What's the difference between unit, functional, acceptance, and integration tests? (End-to-End tests can also be called Integration test)
http://www.sitepoint.com/unit-and-e2e-testing-in-angularjs/

how to check if methods have been called jasmine unit test

I'm new to unit testing, and after reading the docs I'm still confused on how to do checks on asynchronous methods, as far as i understand I will need to use runs() and wait(). However, instead of doing that, I could use spyOn to check if the method has been called instead.
this is the code i want to test
$scope.createMedicalServices = function(){
if($scope.newTreatment.async_selected_treatment.id == null){
$scope.newTreatment.description = $scope.newTreatment.description_no_id;
}
else{
$scope.newTreatment.description = $scope.newTreatment.description;
}
$scope.newTreatment.no_cpt_code = $scope.newTreatment.no_cpt_code;
$scope.newTreatment.cash_price = $scope.newTreatment.cash_price_input;
$scope.newTreatment.average_price = $scope.newTreatment.average_price_input;
$scope.newTreatment.service = $scope.newTreatment.async_selected_treatment.service;
var returnPromiseObject;
if ($scope.newTreatment.no_cpt_code) {
returnPromiseObject = ProviderMedicalService.createNew($scope.newTreatment);
}
else if($scope.newTreatment.async_selected_treatment.id == null){
returnPromiseObject = ProviderMedicalService.createNewCPT($scope.newTreatment);
}
else{
returnPromiseObject = ProviderMedicalService.createExisting($scope.newTreatment);
}
returnPromiseObject.then(
function (value){
$scope.newTreatment.id = value.id;
$scope.provider_medical_services.push($scope.newTreatment);
},
function (error){
console.log(error);
});
};
$scope.searchTreatments = function(value){
return Treatment.find(value).then(function(values){
return values;
});
};
and this is what i wrote so far, but its not much as i have no idea how to test it.
it('search treament should have been called and return a value'),function(){
scope.searchTreatments();
}
it('it should create medical service after submitting the form'),function(){
scope.createMedicalServices();
runs
}
Basically, I am content if the method just gets called properly, as the createMedicalService function is actually executed the moment the user clicks on the submit form, as its part of ng-submit on the html file.
Help and suggestions greatly appreciated
Unit testing can be a little confusing at first. There are a few principles that help with unit testing.
Singleness of Purpose. A component should just do one thing. Consider breaking your code into smaller parts. Move logic out of controllers into factories or services. These can be tested individually.
Inversion of Control. Angularjs does a great job at providing IoC built in. Pass in your dependencies as stubs, fakes or mocks into your unit.
Only test the interface. It is a bad practice to test the internal call structure of a unit. This ends up not testing the functionality, but rather the implimentation. Instead think about this example.
You have a controller that gets a rest service passed in called nameService. This controller has a method called submit(first, last) that when called, should called the method post(args) on the nameService.
This would allow you to create a stub of the nameService, create a spy on the post() method and pass in the stub to the controller we are testing. Then you can simply call submit() on the controller and test that it called the service.
In essence, you test that if you input something into your unit, your unit will output it as expected.

Categories

Resources