文章目录
  1. 1. Startup
  2. 2. Mocking Out Services
    1. 2.1. 方式二
  3. 3. Spies
  4. 4. Unit Testing Server Calls

Startup

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// File: chapter7/karma.conf.js
// Karma configuration
module.exports = function(config) {
  config.set({
    basePath: '',
    frameworks: ['jasmine'],
    files: [
      'angular.min.js',
      'angular-mocks.js',
      '*.js'
    ],
    exclude: [],
    port: 8080,
    logLevel: config.LOG_INFO,
    autoWatch: true,
    browsers: ['Chrome'],
    singleRun: false
  });
};
// File: chapter7/simpleCtrl2.js
angular.module('simpleCtrl2App', [])
.controller('SimpleCtrl2', ['$location', '$window',
  function($location, $window) {
    var self = this;
    self.navigate1 = function() {
      $location.path('/some/where');
    };
    self.navigate2 = function() {
    $location.path('/some/where/else');
  };
}]);
// File: chapter7/simpleCtrl2Spec.js
describe('SimpleCtrl2', function() {
  beforeEach(module('simpleCtrl2App'));
  var ctrl, $loc;
  beforeEach(inject(function($controller, $location) {
    ctrl = $controller('SimpleCtrl2');
    $loc = $location;
  }));
  it('should navigate away from the current page', function() {
    expect($loc.path()).toEqual('');
    $loc.path('/here');
    ctrl.navigate1();
    expect($loc.path()).toEqual('/some/where');
  });
  it('should navigate away from the current page', function() {
    expect($loc.path()).toEqual('');
    $loc.path('/there');
    ctrl.navigate2();
    expect($loc.path()).toEqual('/some/where/else');
  });
});

Mocking Out Services

Now for the purpose of our unit test, we want to mock out ItemService.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// File: chapter7/notesApp1.js
angular.module('notesApp1', [])
.factory('ItemService', [function() {
  var items = [
    {id: 1, label: 'Item 0'},
    {id: 2, label: 'Item 1'}
  ];
  return {
    list: function() {
      return items;
    },
    add: function(item) {
      items.push(item);
    }
  };
}])
.controller('ItemCtrl', ['ItemService', function(ItemService) {
  var self = this;
  self.items = ItemService.list();
}]);

This provider shares its namespace with the modules loaded before. So now we create our mockService and tell the provider that when any controller or service asks for ItemSer vice, give it our value.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// File: chapter7/notesApp1Spec.js
describe('ItemCtrl with inline mock', function() {
  beforeEach(module('notesApp1'));
  var ctrl, mockService;
  beforeEach(module(function($provide) {
    mockService = {
      list: function() {
        return [{id: 1, label: 'Mock'}];
      }
    };
    $provide.value('ItemService',mockService);
  }));
    
  beforeEach(inject(function($controller) {
    ctrl = $controller('ItemCtrl');
  }));
  it('should load mocked out items', function() {
    expect(ctrl.items).toEqual([{id: 1, label: 'Mock'}]);
  });
});

方式二

To change the preceding to be a more reusable, general-purpose mock of the ItemSer vice, we could do the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// File: chapter7/notesApp1-mocks.js
angular.module('notesApp1Mocks', [])
.factory('ItemService', [function() {
  return {
    list: function() {
    return [{id: 1, label: 'Mock'}];
  }
};

// File: chapter7/notesApp1SpecWithMock.js
describe('ItemCtrl With global mock', function() {
  var ctrl;
  beforeEach(module('notesApp1'));
  beforeEach(module('notesApp1Mocks'));
  beforeEach(inject(function($controller) {
    ctrl = $controller('ItemCtrl');
  }));
  it('should load mocked out items', function() {
    expect(ctrl.items).toEqual([{id: 1, label: 'Mock'}]);
  });
});

Spies

Spies allow us to hook into certain functions, and check whether they were called, how many times they were called, what arguments they were called with, and so on.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
describee('ItemCtrl with spies', function() {
  beforeEach(module('notesApp1'));
  var ctrl, itemService;
  
  beforeEach(inject(function($controller, ItemService) {
  // tell it to continue calling the actual service underneath by
  // calling andCallThrough on the spy.
    spyOn(ItemService, 'list').andCallThrough();
    itemService = ItemService;
    ctrl = $controller('ItemCtrl');
  }));
    
  it('should load mocked out items', function() {
    expect(itemService.list).toHaveBeenCalled();
    expect(itemService.list.callCount).toEqual(1);
    expect(ctrl.items).toEqual([
      {id: 1, label: 'Item 0'},
      {id: 2, label: 'Item 1'}
    ]);
  });
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// File: chapter7/notesApp1SpecWithSpyReturn.js
describe('ItemCtrl with SpyReturn', function() {
  beforeEach(module('notesApp1'));
    var ctrl, itemService;
    beforeEach(inject(function($controller, ItemService) {
      spyOn(ItemService, 'list')
      .andReturn([{id: 1, label: 'Mock'}]);
      itemService = ItemService;
      ctrl = $controller('ItemCtrl');
    }));
    it('should load mocked out items', function() {
      expect(itemService.list).toHaveBeenCalled();
      expect(itemService.list.callCount).toEqual(1);
      expect(ctrl.items).toEqual([{id: 1, label: 'Mock'}]);
    });
});

Unit Testing Server Calls

With AngularJS, as long as we include the angular-mocks.js file as part of the Karma configuration, AngularJS takes care of ensuring that when we use the $http service, it doesn’t actually make server calls.

1
2
3
4
5
6
7
8
9
10
11
12
// File: chapter7/serverApp.js
angular.module('serverApp', [])
.controller('MainCtrl', ['$http', function($http) {
  var self = this;
  self.items = [];
  self.errorMessage = '';
  $http.get('/api/note').then(function(response) {
    self.items = response.data;
  }, function(errResponse) {
    self.errorMessage = errResponse.data.msg;
  });
}]);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// File: chapter7/serverAppSpec.js
describe('MainCtrl Server Calls', function() {
  beforeEach(module('serverApp'));
  var ctrl, mockBackend;
  beforeEach(inject(function($controller, $httpBackend) {
    mockBackend = $httpBackend;
    mockBackend.expectGET('/api/note')
    .respond([{id: 1, label: 'Mock'}]);
    ctrl = $controller('MainCtrl');
    // At this point, a server request will have been made
  }));
    
  it('should load items from server', function() {
    // Initially, before the server responds,
    // the items should be empty
    expect(ctrl.items).toEqual([]);
    // Simulate a server response
    // $httpBackend.flush(3) flush three request
    mockBackend.flush();
    expect(ctrl.items).toEqual([{id: 1, label: 'Mock'}]);
  });
  afterEach(function() {
    // Ensure that all expects set on the $httpBackend
    // were actually called
    mockBackend.verifyNoOutstandingExpectation();
    // Ensure that all requests to the server
    // have actually responded (using flush())
    mockBackend.verifyNoOutstandingRequest();
  });
});
文章目录
  1. 1. Startup
  2. 2. Mocking Out Services
    1. 2.1. 方式二
  3. 3. Spies
  4. 4. Unit Testing Server Calls