I'm newbie to AngularJs/NodeJs world, so forgive if this is a basic question to some.
So in a nutshell I've two controllers, the first controller $broadcast
an 'Id' and the second controller fetches that Id with $on
and then passes that Id to an intermediate service
, which makes an $http
ajax call and returns a single Book
object.
How do I unit test $scope.broadcast, $scope.$on using Jasmine
firstCtrl
.controller('firstCtrl', function($scope, ...){
$scope.selectGridRow = function() {
if($scope.selectedRows[0].total !=0)
$scope.$broadcast('id', $scope.selectedRows[0].id);//Just single plain ID
};
});
secondCtrl
.controller('secondCtrl',
function($scope, bookService) {
$scope.$on('id', function(event, id) {
bookService.getBookDetail(id).then(function(d) {
$scope.book = d.book;
});
});
});
expected Json obj
var arr = "book" : [ {
"id" : "1",
"name" : "Tomcat",
"edition" : "9.1"
}
]
Let me know if anyone wants me to post the $http
service that's used by the second controller.
expected behavior
So from the top of my head, ideally, I would like to test every possible scenario, but something like below, which can then expend:
expect(scope.book).toEqual(arr);
expect(scope.book).not.toEqual(undefined);
Thanks everyone!
First you should do the broadcast on $rootScope
then you can receive on $scope
.
Now to the testing. I assume you want to include real request to your API via bookService
and $http
. This can be mocked but I'll focus on the real call. Let me know if you need the mocked one.
Before the actual test, you will need to do some injections/instantiations:
- Initialize your app
- Inject
$controller
, $rootScope
, $httpBackend
and bookService
- Create scopes for firstController and SecondController and store it in a variable
- Store
bookService
and $httpBackend
in variables
- Instantiate the controllers and store them
Then in the actual test you must tell $httpBackend
what to do when it caches request for the books (or books). Construct $httpBackend.whenGET("/api/books/1").passThrough();
will pass request with url "/api/books/1"
to the server.
Next your must setup property selectedRows
on firstScope
so it fulfills the condition in function selectGridRow
in your firstCtrl
.
Now you can call function selectGridRow
to trigger the broadcast and API call. But you must wrap it in runs
function so Jasmine recognizes this as an async call and will wait for it to finish. The 'waiting' is defined in waitsFor
call. It will wait until it gets a book and it waits max 5000 ms then the test will be marked as failed.
Last step is to check expected result. We don't have to check for undefined
anymore as the test would not get to here anyway. The check must be wrapped again runs
call so it is executed afters successful 'waitsFor'.
Here is the full code:
describe("Broadcast between controllers", function () {
beforeEach(module('app')); //app initialization
var firstScope;
var secondScope;
var bookService;
var $httpBackend;
var firstController;
var secondController;
beforeEach(inject(function ($controller, $rootScope, _bookService_, _$httpBackend_) {
firstScope = $rootScope.$new();
secondScope = $rootScope.$new();
bookService = _bookService_;
$httpBackend = _$httpBackend_;
firstController = $controller('firstCtrl', { $scope: firstScope });
secondController = $controller('secondCtrl', { $scope: firstScope, bookService: bookService });
}));
it("should work", function () {
$httpBackend.whenGET("/api/books/1").passThrough();
firstScope.selectedRows = [{ id: 1, total: 1000 }];
secondScope.book = null;
runs(function () {
firstScope.selectGridRow();
});
waitsFor(function () {
return secondScope.book != null;
}, "Data not received in expected time", 5000);
runs(function () {
expect(secondScope.book[0].id).toEqual(1);
});
});
});