TDD AngularJS Controller method with a wrapped asynch Call

Posted on 7/10/2015 @ 1:45 AM in #Vanilla .NET by | Feedback | 1345 views

AngularJS is awesome for many reasons, one of which is, it lends itself so well to TDD. Everything is testable, controllers, modules, directives.
I like to have Karma running on the side as I write my code, so if I break something, I know immediately. It can also be integrated with Jenkins etc. if you prefer.
There is an upfront investment sure! But damn, once you’ve built this scaffolding, you’ll wonder how you ever wrote code without all this scaffolding before.

Anyway, sometimes things are not very straightforward though. One example is, when you have a controller like this --

   1:  angularModule.controller('someController', ['$scope', 'someService',
   2:      function ($scope, someService) {
   3:          $scope.someMethod = function () {
   4:              someService.someAsyncMethod().then(function (response) {});
   5:          };
   6:  }]);

.. the above .. where the someService.someAsyncMethod returns a promise.

How the hell do you write a test for that?
The answer is, you write two tests,

The first test – tests the service and it’s asynch method. (Not showing that here).
And a second test – that tests the controller and merely verifies that the someAsyncMethod is called. You do so using a spy object, like this -

   1:  describe("someController", function () {
   2:      var someServiceMock;
   3:      beforeEach(inject(
   4:          function ($q) {
   5:              someServiceMock = jasmine.createSpyObj('someService', ['someAsyncMethod']);
   6:              someServiceMock.someAsyncMethod.and.returnValue($q.when());
   7:          }));
   8:   
   9:      beforeEach(inject(function ($rootScope, $controller) {
  10:          scope = $rootScope.$new();
  11:          someController = function () {
  12:              return $controller('someController', {
  13:                  '$scope': scope,
  14:                  someService: someServiceMock
  15:              });
  16:          };
  17:      }));
  18:   
  19:      it("Should attempt to call the asynch method", function () {
  20:          var controller = someController();
  21:          scope.someAsyncMethod();
  22:          expect(someServiceMock.someAsyncMethod).toHaveBeenCalled();
  23:      });
  24:  });

As you can see here, we are creating a mock service object to represent the methods we expect to have called. We are also returning a fake value back – you may want to return a more meaningful value depending upon the exact nature and complexity of your test. I like to keep my tests as simple as possible. Once you have this mock object, you simply check if it was called. But how do you know the method succeeded? The answer is – that is what the first test (that I didn’t show in this blogpost) is for. That other method is as simple as testing a simple service asynch call… no rocket science there.

Sound off but keep it civil:

Older comments..