0

I'm very new to the AngularJs unit testing with Jasmine.So could you tell me how can I test below mentioned controller and countyService.getAllCountiesAsync() method using Jasmine.Thanks in advance.

Note : The controller below is having more than 50 injected services (I have shown few below).So I don't know which method is good for mock those also ?

Controller :

(function () {
    appModule.controller('myController', [
        '$scope', '$modalInstance', 'abp.services.app.property', 'abp.services.app.county', 'abp.services.app.propertyClass', 'abp.services.app.schoolDistrict'
        function ($scope, $modalInstance, propertyService, countyService, propertyClassService, schoolDistrictService) {

   vm.getAllCounties = function () {
                countyService.getAllCountiesAsync().success(function (result) {
                    vm.counties = result.items;
                });
            };

            vm.getAllCounties();

} ]);
})();

WebApi method :

  public async Task<ListResultOutput<CountyListDto>> GetAllCountiesAsync()
        {
            var counties = await _countyRepository
                .GetAllListAsync();

            return new ListResultOutput<CountyListDto>(counties.OrderBy(o => o.Name).MapTo<List<CountyListDto>>());
        }
Daan van Hulst
  • 1,426
  • 15
  • 32
Sampath
  • 63,341
  • 64
  • 307
  • 441

2 Answers2

2

You should write test cases for service and controller.

For services 'Daan van Hulst' has already given answer and for controller see below code:

describe('service tests', function () {

var $compile,$controller,myController, $rootScope, propertyService, countyService, propertyClassService, schoolDistrictService;
//All module dependencies   
beforeEach(module('your-app-name'));

//inject required services and _$controller_ to create controller
beforeEach(inject(function(_$compile_,_$controller_, _$rootScope_, _propertyService_, _countyService_, _propertyClassService_, _schoolDistrictService_) {

$compile = _$compile_;
        $rootScope = _$rootScope_;
$controller = _$controller_; // This is IMP
    countyService = _countyService_;
    // remianig services
    // Now create controller 
    myController = $controller('myController', {
                $scope : scope,
                propertyService : propertyService // all other services
    });}

it('should test something', function() {
    spyOn(countyService, 'getAllCountiesAsync').and.callFake(function () {
                var d = q.defer();
                d.resolve({ items: [{data:'somedata'}] });
                return d.promise;
            });     
    myController.getAllCounties();

    expect(myController.counties).not.toBe(null);

});

Update

I might have made mistakes, but this is the idea:

describe('service tests', function () {

var $compile, $rootScope, scope, vm, propertyService, countyService, propertyClassService, schoolDistrictService;
  beforeEach(module('your-app-name'));
  beforeEach(inject(function(_$compile_, _$rootScope_, $controller, _propertyService_, _countyService_, _propertyClassService_, _schoolDistrictService_) {
    $compile = _$compile_;
    $rootScope = _$rootScope_;
    scope = $rootScope.$new();
    propertyService = _propertyService_;
    countyService = _countyService_;
    propertyClassService = _propertyClassService_;
    schoolDistrictService = _schoolDistrictService_;

    vm = $controller('myController', {'$scope': scope})

    spyOn(countyService, "getAllCountiesAsync").and.callFake(function() {
        var deferred = $q.defer();
        deferred.resolve({data: [{id:0}]});
        return deferred.promise;
    });
  }));

  it('can do remote call', inject(function() {

    //Arrange
    result = [{id:0}];

    // Act
    vm.getAllCounties();

    // Assert
    expect(vm.counties).toBe(result); //assert to whatever is resolved in the spyOn function

  });   

});
}
Daan van Hulst
  • 1,426
  • 15
  • 32
  • Could you explain a bit about the `it('should test something', function() {` method ? B'cos I'm very new to this subject.Thanks. – Sampath Jan 07 '16 at 09:26
  • When we test any controller then we should spy the services (means mock). In the method I have used spyOn method to spy getAllCountiesAsync function of countyService. When controller's method get's invoked then controller will not call actual service instead our spy will get execute. May be this will help u to understand. – Pravin Sonawane Jan 07 '16 at 09:53
  • Is that mean, when we call the `myController.getAllCounties();` method inside the test method,will it call the mock service (`spyOn`) ? If so from where the data coming for the test (hence it's not calling the actual service on the webApi )? What is the value of the `myController.counties` inside the test method, you have shown above ? Thanks. – Sampath Jan 07 '16 at 10:04
  • Data will come from spy service. In our case data will be { items: [{data:'somedata'}] } object. While writing test cases for controller our focus should be on controller code not on service. For service have to write different test case files. – Pravin Sonawane Jan 07 '16 at 10:19
  • Do you know any trick or concept where test a controller without injecting all the services ? B'cos on my controller above is having more than 50 services.Thanks. – Sampath Jan 07 '16 at 11:15
  • @Sampath I am quite sure that you do not have to inject the services in the controller. I am not a 100% sure, but I think that these will automatically get resolved by creating the controller. You can see this in my example below. I think this answer is also missing a scope.$apply before the expect. But the first thing I would ask myself is why does one controller have 50 services injected? It might be worth looking at splitting up the controller to share functionality. – Daan van Hulst Jan 07 '16 at 17:01
1

I assume that you create Angular services for all your services and that you app is working. Then, you can inject them in your tests:

describe('service tests', function () {

var $compile, $rootScope, propertyService, countyService, propertyClassService, schoolDistrictService;
  beforeEach(module('your-app-name'));
  beforeEach(inject(function(_$compile_, _$rootScope_, _propertyService_, _countyService_, _propertyClassService_, _schoolDistrictService_) {
    $compile = _$compile_;
    $rootScope = _$rootScope_;
    propertyService = _propertyService_;
    countyService = _countyService_;
    propertyClassService = _propertyClassService_;
    schoolDistrictService = _schoolDistrictService_;
  }));

  it('should test something', function() {
    expect(propertyService).toBeDefined();
    expect(countyService).toBeDefined();
    expect(propertyClassService).toBeDefined();
    expect(schoolDistrictService).toBeDefined();
  });

});

Update

I accidentally posted my solution in the answer above, so corrected it now. You can create your controller with $controller and pass in a scope object. You can also pass in any other dependencies. Then create a spy on the service, and once it gets called, call a different function which resolves a promise with mock data:

describe('service tests', function () {

var $compile, $rootScope, scope, vm, propertyService, countyService, propertyClassService, schoolDistrictService;
  beforeEach(module('your-app-name'));
  beforeEach(inject(function(_$compile_, _$rootScope_, $controller, _propertyService_, _countyService_, _propertyClassService_, _schoolDistrictService_) {
    $compile = _$compile_;
    $rootScope = _$rootScope_;
    scope = $rootScope.$new();
    propertyService = _propertyService_;
    countyService = _countyService_;
    propertyClassService = _propertyClassService_;
    schoolDistrictService = _schoolDistrictService_;

    // Create the controller, and pass in the scope with possible variables that you want to mock.
    vm = $controller('myController', {'$scope': scope})

    //Create a spy on your getAllCountiesAsync function and make it return a mock promise with mock data.
    spyOn(countyService, "getAllCountiesAsync").and.callFake(function() {
        var deferred = $q.defer();
        deferred.resolve({data: [{id:0}]});
        return deferred.promise;
    });
  }));

  it('can do remote call', inject(function() {

    //Arrange
    result = [{id:0}];

    // Act
    vm.getAllCounties();

    //I think that you also have to do this, but I am not a 100% sure.
    scope.$apply();

    // Assert
    expect(vm.counties).toBe(result); //assert to whatever is resolved in the spyOn function

  });   

});
}
Daan van Hulst
  • 1,426
  • 15
  • 32
  • Could you tell me how to test this method `vm.getAllCounties` ? Thanks. – Sampath Jan 07 '16 at 08:58
  • You should look into create a spy for your service and mocking the answer. Example: http://stackoverflow.com/questions/23705051/how-do-i-mock-a-service-that-returns-promise-in-angularjs-jasmine-unit-test. – Daan van Hulst Jan 07 '16 at 09:04
  • Thanks for the link.I'll read it.But I'm very new to this subject.The explanation you have put above is very clear for me.So when you'll have a time,Can you put a test for the `vm.getAllCounties` function also ? Then I can understand the concept through my example itself. Thanks. – Sampath Jan 07 '16 at 09:15
  • Sorry,I cannot see any update on the above post ? Where is the test case for the `vm.getAllCounties` method ? Thanks. – Sampath Jan 07 '16 at 13:56
  • 1
    I must have closed my laptop too fast because I was in a bit of a hurry to go to a convention. Will upload it later today, sorry. – Daan van Hulst Jan 07 '16 at 14:04
  • OK sure.Hope you'll upload later.Thanks. – Sampath Jan 07 '16 at 14:10