Tuesday, May 31, 2016

Testing Whether A Filter Was Called

Today I finally circled back on some old tests and decided to figure out how to tell whether a specific filter was called with the correct values.  This has been bothering me for a while, but the answer turned out to be pretty simple.  Let's say you have a controller that calls the built-in orderBy filter and passes an array of states (scope.states) to be sorted by id:
angular.module('app', []).controller('sampleController', function($scope, $filter) {
  $scope.states = [
    { name: 'Alabama', id: 'AL' },
    { name: 'Alaskas', id: 'AK' },
    { name: 'Arizona', id: 'AZ' },
    { name: 'Arkansas', id: 'AR' }
  ];
  
  $scope.sort = function() {
    return $filter('orderBy')(scope.states, 'id');
  };
});

The end result should be that these states get sorted so Alaska comes first, followed by Alabama, Arkansas, then Arizona (AK, AL, AR, AZ).  In order to verify this works as expected we can inject a new spy into the controller instead of using the expected $filter service.  Like this:
describe('test suite', function() {
  var scope, orderByFilterSpy, filterSpy;
  
  beforeEach(module('app'));
  
  beforeEach(inject(function(_$controller_, _$rootScope_) {
    scope = _$rootScope_.$new();
    
    orderByFilterSpy = jasmine.createSpy();
    filterSpy = jasmine.createSpy().and.returnValue(orderByFilterSpy);
    
    _$controller_('sampleController', { $scope: scope, $filter: filterSpy });
  }));
  
  it('should pass scope.states and \'id\' to orderByFilter', function() {
    scope.sort();
    
    expect(filterSpy).toHaveBeenCalled();
    expect(orderByFilterSpy).toHaveBeenCalledWith([
      {name: 'Alabama', id: 'AL'},
      { name: 'Alaskas', id: 'AK' },
      { name: 'Arizona', id: 'AZ' },
      { name: 'Arkansas', id: 'AR' }], 'id');
  });
});

What we end up with is one passing test.  Remember to trust that the orderBy filter does what you're expecting.  That means you're not actually testing whether the result of calling scope.sort is a sorted array.  Instead you just check that you passed the right values to the right filter.  If you're calling a custom filter, make sure to test that filter thoroughly, but separately.

No comments:

Post a Comment