0

I am trying to lay my hands on karma-jasmine and I am working on some basic examples to start with.

I have created one small plnkr which does 2 things only:

  1. Add the numbers
  2. display some hardcoded values on a button click

Here is my MainController.spec.js:

describe('Main Controller', function () {
    var $controller, MainController, UserFactory;
    var data = {
        "name": "Shashank",
        "rank": "1"
    }

    beforeEach(angular.mock.module('app'));

    beforeEach(inject(function (_$controller_, _UserFactory_) {
        $controller = _$controller_;
        UserFactory = _UserFactory_;
        MainController = $controller('MainController', {UserFactory: UserFactory});

        spyOn(MainController, 'add').and.callThrough();
        spyOn(MainController, 'getHardData').and.callThrough();
    }))

    it('should be defined', function () {
        expect(MainController).toBeDefined();
    })

    it('should have number = 3', function () {
        expect(MainController.number).toEqual(5)
    })

    it('add() should assign value properly', function () {
//        expect(MainController.add).toHaveBeenCalled();
    })

    it('getHardData should assign the exact data', function () {
//        expect(MainController.getHardData).toHaveBeenCalled();
//        expect(MainController.data).toEqual(data);
    })

})

For the mentioned controller in the attached plnkr, I think I should only test 2 things:

  1. Test that the addition is working correctly.
  2. Test that the data after button is correct

Questions:

  1. Uncommenting any of the above lines thorw error:

Chrome 55.0.2883 (Windows 7 0.0.0) Main Controller add() should assign value properly FAILED

Expected spy add to have been called. at Object. (controller/MainController.spec.js:28:36) Chrome 55.0.2883 (Windows 7 0.0.0): Executed 4 of 4 (1 FAILED) (0.016 secs / 0.012 secs)

  1. Also, in $controller('MainController', {UserFactory: UserFactory}); , I understand that we are assigning the values of factory but changing it to $controller('MainController', {}); is not throwing any error. WHY?

Please help me understand my silly mistakes.

Any good links to understand best practices/examples of karma & jasmine would be of great help

1 Answer 1

1

Looking at the controller, it exposes four properties - two which are functions (five properties if you count sumVal that is created in the add function).

The properties data and number can simply be asserted that they are initialized correctly after controller creation, like you have done with one of them.

When it comes to the add function, the most obvious test is that it should add the two arguments and assign the result correctly. As the function takes two parameters, there are plenty of other tests you can add. Calling the function without arguments, calling the function with one number and one string etc. If you want to test this usually depends on the use case and your testing strategy.

The getHardData function doesn't take any arguments so what you want to test is that it uses the UserFactory and that it assigns the result correctly.


The reason the tests fail if you uncomment the code is simply because the functions are never called. There is nothing in your controller that calls the add or getHardData methods upon controller initialization and there is nothing in your test that calls them.

For the test to work you would have to do:

it('add() should assign value properly', function() {
  MainController.add();
  expect(MainController.add).toHaveBeenCalled();
});

This is a useless test however, since you are not testing any of your own functionality.

Instead, you should for example do this:

it('add() should assign value properly', function() {

  expect(MainController.sumVal).toBeUndefined();

  MainController.add(5, 10);

  expect(MainController.sumVal).toBe(15);
});

You don't need to spy on the controller methods that you are testing, since you will be calling them manually.

You want to use spies when you have a controller method that calls another dependency, like getHardData.

You don't want the real implementation of UserFactory to be used in the test, so you can use a spy to interrupt the call and control what it returns.

For example:

it('getHardData should assign the exact data', function() {

  // Set up the spy and the return value
  spyOn(UserFactory, 'getData').and.returnValue(data);

  // Call the method that you are testing
  MainController.getHardData();

  // Assert that the dependency was called
  expect(UserFactory.getData).toHaveBeenCalled();

  // Assert that the returned data was assigned correctly
  expect(MainController.data).toEqual(data);
});

An alternative is to create a mock implementation and use that in your controller:

var MainController,
  data = {
    name: "Shashank",
    rank: "1"
  },
  UserFactory = {
    getData: function() {
      return data;
    }
  };

beforeEach(function() {

  angular.mock.module('app');

  inject(function($controller) {

    MainController = $controller('MainController', {
      UserFactory: UserFactory
    });
  });
});

it('getHardData should assign the exact data', function() {

  // Set up the spy
  spyOn(UserFactory, 'getData').and.callThrough();

  // Call the method that you are testing
  MainController.getHardData();

  // Assert that the dependency was called
  expect(UserFactory.getData).toHaveBeenCalled();

  // Assert that the returned data was assigned correctly
  expect(MainController.data).toEqual(data);
});

Note that the spy now uses and.callThrough() instead of and.returnValue(data). If you don't use this the call will be interrupted by the spy and your mock implementation will not be used.

The reason that $controller('MainController', {}); isn't throwing any error is because when passing an empty object as the second argument (or no argument at all - $controller('MainController')) the real dependencies will be injected automatically, which is the same as you have now.

Sign up to request clarification or add additional context in comments.

3 Comments

Thank you so much for this elaborate answer. It answers so many more doubts that I had in my mind. :)
@ShashankVivek And the add function assigns the result to sumVal?
Please disregard. my bad :3

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.