問題

私はいくつかの変数を監視するコントローラを単体テストするのに苦労しています。私の単体テストでは、スコープを呼び出すときでも、 $ watch 関数のコールバックを取得できません。 $ digest()。これはかなりシンプルなはずですが、私は運がありません。

私のコントローラには次のものがあります:

 angular.module('app')
  .controller('ClassroomsCtrl', function ($scope, Classrooms) {

    $scope.subject_list = [];

    $scope.$watch('subject_list', function(newValue, oldValue){
      if(newValue !== oldValue) {
        $scope.classrooms = Classrooms.search(ctrl.functions.search_params());
      }
    });
});
 

そしてここに私の単体テストがあります:

 angular.module('MockFactories',[]).
  factory('Classrooms', function(){
    return jasmine.createSpyObj('ClassroomsStub', [
      'get','save','query','remove','delete','search', 'subjects', 'add_subject', 'remove_subject', 'set_subjects'
    ]);
  });

describe('Controller: ClassroomsCtrl', function () {

  var scope, Classrooms, controllerFactory, ctrl;

  function createController() {
    return controllerFactory('ClassroomsCtrl', {
      $scope: scope,
      Classrooms: Classrooms
    });
  }

  // load the controller's module
  beforeEach(module('app'));

  beforeEach(module('MockFactories'));

  beforeEach(inject(function($controller, $rootScope, _Classrooms_ ){
    scope = $rootScope.$new();

    Classrooms = _Classrooms_;

    controllerFactory = $controller;

    ctrl = createController();

  }));

  describe('Scope: classrooms', function(){

    beforeEach(function(){
      Classrooms.search.reset();
    });

    it('should call Classrooms.search when scope.subject_list changes', function(){
      scope.$digest();
      scope.subject_list.push(1);
      scope.$digest();

      expect(Classrooms.search).toHaveBeenCalled();
    });
  });
});
 

私はすべてのスコープを置き換えようとしました。 $ digest()呼び出しをスコープに置き換えました。 $ apply()呼び出し。私はそれらを3回または4回呼び出そうとしましたが、$ watchのコールバックを呼び出すことはできません。

ここで何が起こっているのかについての考えはありますか?

更新: ここではもっと簡単な例を示しますが、これはモック、スタブビング、または注入工場には対処しません。

 angular.module('edumatcherApp')
  .controller('ClassroomsCtrl', function ($scope) {
    $scope.subject_list = [];
    $scope.$watch('subject_list', function(newValue, oldValue){
      if(newValue !== oldValue) {
        console.log('testing');
        $scope.test = 'test';
      }
    });
 

ユニットテスト:

 it('should set scope.test', function(){
  scope.$digest();
  scope.subject_list.push(1);
  scope.$digest();

  expect(scope.test).toBeDefined();
});
 

これは「定義されていない予想される未定義」でも失敗します。コンソールには何も記録されません。

更新2

私が指摘した2つの興味深いこと。

  1. 1つの問題は、コールバックが呼び出されたときに newValue と oldValue が同じであることです。私は両方ともコンソールに記録し、どちらも [] と等しいです。たとえば、 $ watch 関数を次のように変更した場合:

     $scope.$watch('subject_list', function(newValue, oldValue){
        console.log('testing');
        $scope.test = 'test';
    });
     

テストは正常に合格します。 newValueとoldValueが正しく設定されていない理由がわかりません。

  1. $ watchコールバック関数を名前付き関数に変更し、名前付き関数が呼び出されたかどうかを確認するだけで失敗します。たとえば、コントローラを次のように変更できます。

     $scope.update_classrooms = function(newValue, oldValue){
        $scope.test = 'testing';
        console.log('test');
    };
    
    $scope.watch('subject_list', $scope.update_classrooms);
     

私のテストをこれに変更します:

 it('should call update_classrooms', function(){
  spyOn(scope,'update_classrooms').andCallThrough();
  scope.$digest();
  scope.subject_list.push(1);
  scope.$digest();
  expect(scope.update_classrooms).toHaveBeenCalled();
});
 

「期待されるスパイ更新教室が呼び出された」と失敗します。

この場合、 'test'がコンソールにログインするため、update_tlastsは間違いなく呼び出されています。私は困惑しています。

  ベストアンサー

私は自分のコードベース内でこの問題に遭遇しました。答えは、コントローラをインスタンス化した直後に$ digest()呼び出しが必要であることが判明しました。あなたのテストでは、監視された変数の各変更後に手動でスコープを呼び出す必要があります。これには、コントローラが構築された後、最初の監視値を記録します。

さらに、コメントに Vitall が指定されているので、コレクションには $watchCollection() が必要です。

簡単な例でこの時計を変更すると、問題が解決されます。

 $scope.$watch('subject_list', function(newValue, oldValue){
    if(newValue !== oldValue) {
        console.log('testing');
        $scope.test = 'test';
    }
});
 

に:

 $scope.$watchCollection('subject_list', function(newValue, oldValue){
    if(newValue !== oldValue) {
        console.log('testing');
        $scope.test = 'test';
    }
});
 

私は違いを示すためにjsfiddleを作った:

http://jsfiddle.net/ydv8k4zy/ - 失敗したテストでオリジナル - $ watchCollectionではなく$ watchを使用するために失敗する

http://jsfiddle.net/ydv8k4zy/1/ - 機能バージョン

http://jsfiddle.net/ydv8k4zy/2/ - $ watchCollectionは初期スコープなしで失敗します。 $ digest。

2番目の失敗した項目の console.log で遊ぶと、スコープの後に古いものと新しいものと同じ値で時計が呼び出されることがわかります。 $ digest()(25行目)。

  同じタグがついた質問を見る

angularjsunit-testingangularjs-scopewatch