未定义不是对象(评估'value.phrase.replace')

时间:2016-10-05 05:51:23

标签: javascript angularjs unit-testing karma-jasmine

我的单元测试因以下错误消息而失败:

LOG: 'f40e0e47-6457-463b-a5f9-9dc97bd2d0ce'
LOG: [Object{phrase_id: 'f40e0e47-6457-463b-a5f9-9dc97bd2d0ce', phrase: 'Training {{group}} to develop their {{attribute}} by ensuring they are comfortable with {{factor}}'}]
PhantomJS 2.1.1 (Windows 8 0.0.0) PhraseDetailCtrl #initialisation should initialise the controller's scope with details on a single phrase FAILED
        TypeError: undefined is not an object (evaluating 'value.phrase.replace') in D:/myapp-mobile/www/js/myapp.controllers.js (line 87)
        D:/myapp-mobile/www/js/myapp.controllers.js:87:34
        forEach@[native code]
        D:/myapp-mobile/www/js/myapp.controllers.js:85:35
        then@unit-tests/phrase-detail.controller.tests.js:32:26
        PhraseDetailCtrl@D:/myapp-mobile/www/js/myapp.controllers.js:78:55
        PhraseDetailCtrl@[native code]
        instantiate@D:/myapp-mobile/www/lib/ionic/js/ionic.bundle.js:18010:61
        $controller@D:/myapp-mobile/www/lib/ionic/js/ionic.bundle.js:23412:39
        D:/myapp-mobile/www/lib/angular-mocks/angular-mocks.js:2221:21
        unit-tests/phrase-detail.controller.tests.js:44:31
        loaded@http://localhost:9876/context.js:151:17
PhantomJS 2.1.1 (Windows 8 0.0.0): Executed 3 of 3 (1 FAILED) (0.01 secs / 0.084 secs)

这是我试图测试的控制器:

function PhraseDetailCtrl($scope, $stateParams, PhraseService, CompetencyService, PopupService, ModalService, FilterService, $ionicLoading, $ionicActionSheet, $timeout) {

    // $ionicLoading.show({
    //  content: 'Loading',
    //  animation: 'fade-in',
    //  showBackdrop: true,
    //  maxWidth: 200,
    //  showDelay: 0
    // });


    // $timeout(function () {

        // $ionicLoading.hide();
        $scope.dataLoaded = true;
        $scope.phraseId = $stateParams.phraseId;

        console.log($scope.phraseId);

        PhraseService.getPhraseDetail($scope.phraseId).then(function(dataResponse) {
            $scope.phraseDetail = dataResponse.data;

            console.log($scope.phraseDetail);

            var replacers = FilterService.getItemFilter();

            Object.keys(replacers).forEach(function(key){
                 var value = $scope.phraseDetail;
                 value.phrase = value.phrase.replace(new RegExp(key, 'g'), replacers[key]);
            })
        })


        PhraseService.getPhraseCompetency($scope.phraseId).then(function(dataResponse) {
            $scope.phraseCompetencyList = dataResponse.data;
        })


        PhraseService.getPhraseExample($scope.phraseId).then(function(dataResponse) {

            var data = dataResponse.data;

        var phrase = data[0].phrase;
        // console.log("phrase: " + phrase);

        var tokens = data.map(function(tmp) {
            return tmp.example;
        });
        // console.log("tokens: " + tokens);

        var re = /{{[^}]*}}/gi;
        var placeholders = phrase.match(re);
        // console.log("placeholders: " + placeholders);

            if (tokens.length != placeholders.length) {
                throw new Error("Mismatch number of tokens and placeholders");
            }

            for (var i = 0; i < tokens.length; ++i) {
                var token = tokens[i];
                var placeholder = placeholders[i];
                phrase = phrase.replace(placeholder, "<button class=\"button button-small button-outline button-dark button-side-padding\">" + token + "</button>");
            }
        // console.log(phrase);

        $scope.phraseExample = phrase;

        })


        CompetencyService.getCompetencyList().then(function(dataResponse) {
            $scope.competencyList = dataResponse.data;
        })


    // }, 1000);


    // Action sheet

    $scope.showOptions = function() {

        $ionicActionSheet.show({
            titleText: 'Options',
            buttons: [
                { text: '<i class="icon ion-heart"></i>Add to Favourites' },
                { text: '<i class="icon ion-chatbubble"></i> Suggest Improvement' }
            ],
            cancelText: 'Cancel',
            buttonClicked: function(index) {
                if(index === 0) {
                    $scope.addtoFavouritesPopup();
                } else if(index === 1) {
                    $scope.suggestimprovementModal();
                }
                //console.log('BUTTON CLICKED', index);
                return true;
            },
            cancel: function() {
                //console.log('CANCELLED');
            },
        });

    };


    // Popups

    $scope.phraseExamplePopup = function() {
        var myPopup = PopupService.phraseExamplePopup($scope);
    };

    $scope.addtoFavouritesPopup = function() {
        var myPopup = PopupService.addtoFavouritesPopup($scope);
    };


    // Modals

    $scope.phraseCompetencyModal = function() {
        var vm = $scope;
        ModalService.show('templates/modals/phrase-competency-modal.html', 'PhraseDetailCtrl as vm');
    }

    $scope.suggestimprovementModal = function() {
        var vm = $scope;
        ModalService.show('templates/modals/suggest-improvement-modal.html', 'PhraseDetailCtrl as vm');
    }

}

我认为此时会出现错误消息,但我不确定如何在单元测试中模拟它:

Object.keys(replacers).forEach(function(key){
                 var value = $scope.phraseDetail;
                 value.phrase = value.phrase.replace(new RegExp(key, 'g'), replacers[key]);
            })

这是我的单元测试:

describe('PhraseDetailCtrl', function () {

  var $controller, $stateParams, $ionicModal, $ionicPopup, $PhraseService, $CompetencyService, $PopupService, $ModalService, $FilterService

  $stateParams = {
    phraseId: "f40e0e47-6457-463b-a5f9-9dc97bd2d0ce"
  };

  var response = {
    status: 200,
    data: [{
      "phrase_id": "f40e0e47-6457-463b-a5f9-9dc97bd2d0ce",
      "phrase": "Training {{group}} to develop their {{attribute}} by ensuring they are comfortable with {{factor}}"
    }]
  };

  beforeEach(module('ionic'));
  beforeEach(module('dingocv.services'));
  beforeEach(module('dingocv.controllers'));

  beforeEach(inject(function (_$controller_, _PhraseService_, _CompetencyService_, _ModalService_, _PopupService_, _FilterService_) {
    $controller = _$controller_;
    $PhraseService = _PhraseService_;
    $CompetencyService = _CompetencyService_;
    $ModalService = _ModalService_;
    $PopupService = _PopupService_;
    $FilterService = _FilterService_;

    spyOn(_PhraseService_, 'getPhraseDetail').and.callFake(function(){
      return{
        then: function(successCallback){
          successCallback(response);
        }
      }
    });
  }));

  describe('#initialisation', function() {

    var $scope, controller;

    beforeEach(function () {
      $scope = {};
      controller = $controller('PhraseDetailCtrl', {
        $scope: $scope,
        $stateParams: $stateParams,
        $ionicModal: $ionicModal,
        $ionicPopup: $ionicPopup,
        PhraseService: $PhraseService,
        CompetencyService: $CompetencyService,
        PopupService: $PopupService,
        ModalService: $ModalService,
        FilterService: $FilterService
      });
    });

    it('should initialise the controller\'s scope with details on a single phrase', function(){
      expect($PhraseService.getPhraseDetail).toHaveBeenCalled();
      expect($scope.phraseDetail).toBeDefined();
    });

  });

});

2 个答案:

答案 0 :(得分:1)

在尝试使用对象之前,您应检查null / undefined,例如

&#13;
&#13;
if(replacers!=undefined){
  Object.keys(replacers).forEach(function(key){
                 var value = $scope.phraseDetail;
                if(value !=undefined && value.phrase !=undefined){
                 value.phrase = value.phrase.replace(new RegExp(key, 'g'), replacers[key]);
                }
            })
}
&#13;
&#13;
&#13;

答案 1 :(得分:0)

在测试中,您有一个响应对象, <?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval"> <solid android:color="#EC2027" /> <size android:height="10dp" android:width="10dp" /> </shape> 属性为data

为了访问array of objects,您可以像phrase那样访问response.data[0].phrase。您在这里访问object属性的 0th data

var response = {
    status: 200,
    data: [{
      "phrase_id": "f40e0e47-6457-463b-a5f9-9dc97bd2d0ce",
      "phrase": "Training {{group}} to develop their {{attribute}} by ensuring they are comfortable with {{factor}}"
    }]
  };

controller代码中,您有此

 PhraseService.getPhraseDetail($scope.phraseId).then(function(dataResponse) { 
            // here "dataResponse.data" is returning an array
            $scope.phraseDetail = dataResponse.data;

            console.log($scope.phraseDetail);

            var replacers = FilterService.getItemFilter();
            var i = 0;
            Object.keys(replacers).forEach(function(key){
                 // Since $scope.phraseDetail is an array of objects, you have to select particular object in the array. 
                 var value = $scope.phraseDetail[i];
                 value.phrase = value.phrase.replace(new RegExp(key, 'g'), replacers[key]);
                 i++;
            })
        })

希望这有帮助。