1

I have problem to write a unit test for my controller which test orderBy in the table with elements.

I have advert.html:

<body ng-app="motoAdsApp" ng-controller="AdvertsController">

  <form>
    <label>Sort by</label>
    <select ng-model="sortByCol" ng-options="s.name for s in sortByCols">
      <option value=""></option>
    </select>
  </form>

  <br/>

  <table border="1">
    <tr ng-repeat="advert in adverts| orderBy:sortByCol.key">
      <td>
        <span>{{advert.countryName}}, {{advert.regionName}}</span>
      </td>
      <td>{{advert.brandName}}</td>
      <td>{{advert.modelName}}</td>
      <td>{{advert.year}}</td>
      <td>{{advert.price}}</td>
    </tr>
  </table>

</body>

controllers.js:

var motoAdsApp = angular.module('motoAdsApp', []);

motoAdsApp.controller('AdvertsController', ['$scope', function($scope) {

  $scope.sortByCols = [{
    "key": "year",
    "name": "Year"
  }, {
    "key": "price",
    "name": "Price"
  }];

  $scope.adverts = [];
  var allAdverts = ADVERTS_RESPONSE;

  $scope.filter = {
    brandName: null,
    modelName: null,
    country: null,
    region: null,
    yearFrom: null,
    yearTo: null
  };

  $scope.$watch('filter', filterAdverts, true);

  function filterAdverts() {
    $scope.adverts = [];
    angular.forEach(allAdverts, function(row) {
      if (!$scope.filter.country) {
        $scope.filter.region = null;
      }
      if ($scope.filter.brandName && $scope.filter.brandName !== row.brandName) {
        return;
      }
      // ...
      $scope.adverts.push(row);
    });
  }

}
]);

My unit test controllersSpec.js:

describe('MotoAds controllers', function() {

  beforeEach(function() {
    this.addMatchers({
      toEqualData: function(expected) {
        return angular.equals(this.actual, expected);
      }
    });
  });

  beforeEach(module('motoAdsApp'));

  describe('AdvertsController', function() {
    var scope, ctrl, $httpBackend;

    beforeEach(inject(function(_$httpBackend_, $rootScope, $controller) {
      $httpBackend = _$httpBackend_;
      $httpBackend.expectGET('data/adverts.json').respond(ADVERTS_RESPONSE);

      scope = $rootScope.$new();
      ctrl = $controller('AdvertsController', {$scope: scope});
    }));

  it('should sort by year', function() {
      $httpBackend.flush();

      scope.$apply(function() {
        scope.sortByCol = { key: "year"};
      });

      var prevYear = 0;
      for (var i = 0; i < scope.adverts.length; i++) {
        var advert = scope.adverts[i];
        expect(advert.year).toBeGreaterThan(prevYear);
        prevYear = advert.year;
      }
    });     

  });

});

After run my test I have this:

Chrome 30.0.1599 (Windows Vista) MotoAds controllers AdvertsController should so
rt by year FAILED
        Expected 2010 to be greater than 2011.
        Error: Expected 2010 to be greater than 2011.
            at null.<anonymous> (D:/projects/motoads/test/unit/controllers
Spec.js:147:29)
        Expected 2009 to be greater than 2012.
        Error: Expected 2009 to be greater than 2012.

I think that the elements (adverts) are not sorted. Why? What I should do?

Plunker example (without runnable test)

4

1 回答 1

4

The orderBy filter, when used on an element like an ng-repeat, doesn't actually sort the array object that it filters. The array object remains unchanged, but the output array that the compiler uses to create all the html is modified.

So your $scope.adverts array never changes. The way it is output to the view changes, but not the array itself.

This portion of your unit test seems more like something you would test using an E2E test. The controller doesn't really care how the elements are displayed. That is more of a responsibility for the view. The controller is only preoccupied with whether or not the data is grabbed from the API and then handed to the view. Then the e2e test can actually scan the view and make sure that the elements are sorted correctly.

UPDATE:

If you wanted to call the orderBy filter in your test, you can do that using something similar to the following:

...
var scope, ctrl, $httpBackend, orderByFilter;
beforeEach(inject(function(_$httpBackend_, $rootScope, $controller, $filter) {
    $httpBackend = _$httpBackend_;
    $httpBackend.expectGET('data/adverts.json').respond(ADVERTS_RESPONSE);
    orderByFilter = $filter('orderBy');
    scope = $rootScope.$new();
    ctrl = $controller('AdvertsController', {$scope: scope});
}));
...
it('should do something', function(){
    var sortedArray = orderByFilter(scope.adverts, scope.sortByCol.key);
    // do something with the sorted array here
});
...

The docs for how to access the orderBy filter from javascript are found here

Hope that helps.

于 2013-11-04T20:53:28.737 回答