2013-07-13 36 views
19

我有以下創建input元素的AngularJS指令。輸入具有運行doIt()函數的ng-change屬性。在我的指令的單元測試中,我想檢查當用戶改變輸入時是否調用doIt函數。但是測試沒有通過。儘管它在手動測試時可用於瀏覽器。如何在AngularJS中觸發指令測試中的ng-change

指令:

... 
template: "<input ng-model='myModel' ng-change='doIt()' type='text'>" 

測試:

el.find('input').trigger('change') // Dos not trigger ng-change 

現場演示(NG-變化):http://plnkr.co/edit/0yaUP6IQk7EIneRmphbW?p=preview


現在,測試通過,如果我手動綁定change事件,而不是使用ng-change屬性。

template: "<input ng-model='myModel' type='text'>", 
link: function(scope, element, attrs) { 
    element.bind('change', function(event) { 
    scope.doIt(); 
    }); 
} 

現場演示(手動綁定):http://plnkr.co/edit/dizuRaTFn4Ay1t41jL1K?p=preview


是否有使用ng-change並使其可測試的方法嗎?謝謝。

+1

在模型變化時觸發ng變化,而不是事件。爲什麼不只是測試你的「doIt」fn的控制器代碼? – marko

+0

@marko,謝謝你的好問題。我想在指令的測試中做的是檢查doIt在用戶改變輸入時被調用。 doIt本身會在指令的測試中被嘲弄,因爲它已經在控制器的測試中進行了測試,正如您所建議的那樣。 – Evgenii

回答

30

從您的解釋性註釋:

所有我想在指令的測試做的是檢查,當用戶改變輸入doIt被調用。

無論是否通過ng-change指示的表達式計算正確與否真的是ngModel指令的責任,所以我不知道我會測試它以這種方式;相反,我相信指令ngModelngChange已經被正確地實現並被測試來調用指定的函數,並且只是測試調用函數本身以正確的方式影響指令。可以使用端到端或集成測試來處理完全使用場景。

這就是說,你可以得到驅動ngModel變化回調ngModelController實例的保持和自己設置視圖值:

it('trigger doIt', function() { 
    var ngModelController = el.find('input').controller('ngModel'); 
    ngModelController.$setViewValue('test'); 
    expect($scope.youDidIt).toBe(true); 
}); 

就像我說的,不過,我喜歡這種感覺是達到過遠離ngModel的責任,打破你用自然組合指令獲得的黑盒子。

例子:http://plnkr.co/edit/BaWpxLuMh3HvivPUbrsd?p=preview


[更新]

在AngularJS源環顧四周後,我發現,以下也適用:

it('trigger doIt', function() { 
    el.find('input').trigger('input'); 
    expect($scope.youDidIt).toBe(true); 
}); 

它看起來像事件在一些瀏覽器中是不同的; input似乎適用於Chrome。

例子:http://plnkr.co/edit/rbZ5OnBtKMzdpmPkmn2B?p=preview

這裏是relevant AngularJS code,它使用$sniffer服務找出觸發哪個事件:

changeInputValueTo = function(value) { 
    inputElm.val(value); 
    browserTrigger(inputElm, $sniffer.hasEvent('input') ? 'input' : 'change'); 
}; 

即使有這個,我不知道我會測試這樣的指令。

+4

關於事件,我認爲「輸入」是用於文本輸入等,「改變」是用於選擇框和單選按鈕。至少這就是我在PhantomJS上運行測試的工作。 –

+1

似乎browserTrigger只存在於angular-scenario.js中。這意味着它僅用於e2e測試,對吧?我無法將此功能用於單元測試,這是我相信的目的。 – unludo

+0

@unludo也許;我的觀點只是AngularJS源代碼是爲了在某些瀏覽器中觸發不同的事件而編寫的。 –

0

我在長時間尋找這條簡單的線路。 只是爲了保存在這裏。

如何從html選擇值,使用Karma,所以得到ng-change函數工作?

HTML:

控制器或指令JS:

$scope.itemTypes = [{name: 'Some name 1', value: 'value_1'}, {name: 'Some name 2', value: 'value_2'}] 

    $scope.itemTypeSelected = function() { 
    console.log("Yesssa !!!!"); 
    }; 

噶測試片段:

angular.element(element.find("#selectedItemType")[0]).val('value_1').change(); 
    console.log("selected model.selectedItemType", element.isolateScope().model.selectedItemType); 

控制檯:

'Yesssa !!!!' 
'selected model.selectedItemType', 'value_1' 
0

我使用了「角度指令觸發器ng-change」,這個StackOverflow問題是我得到的最有用的東西,所以我會回答「如何在指令中觸發ng-change」,因爲其他人一定會登陸這個頁面,我不知道如何提供這些信息。

裏面的指令鏈接功能,這將觸發元素上的NG-切換功能:

element.controller('ngModel').$viewChangeListeners[0](); 

element.trigger("change")element.trigger("input")沒有工作對我來說,也沒有什麼事我可以在網上找到。

舉個例子,觸發模糊的NG-變化:

wpModule.directive('triggerChangeOnBlur', function() { 
    return { 
     restrict: 'A', 
     link: function (scope, element, attrs) { 
      element.on('blur', function() { 
       element.controller('ngModel').$viewChangeListeners[0](); 
      }); 
     } 
    }; 
}]); 

我很遺憾,這不是直接回答OP的問題。我將非常樂意就如何分享這些信息採取一些合理的建議。

+1

理想情況下,你會問一個單獨的問題,然後自己回答(聽起來很奇怪,但它被鼓勵)。 我也在尋找一些不太符合OP或者你的問題的東西,並且如果有關於它的具體問題會更快找到答案。 –

0

簡單,它的工作原理 在你的單元測試ENV:

spyOn(self, 'updateTransactionPrice'); 


var el = compile('<form name="form" latest novalidate json-schema="main.schema_discount" json-schema-model="main._data"><input type="text" ng-model="main._data.reverse_discount" ng-class="{ \'form-invalid\': form.reverse_discount.$invalid }" ng-change="main.transactionPrice(form);" name="reverse_discount" class="form-control-basic" placeholder="" ng-disabled="!main.selectedProduct.total_price"></form>')(scope); 
el.find('input').triggerHandler('change'); 

expect(self.updateTransactionPrice).toHaveBeenCalled(); 
0

一直試圖得到這個工作,但未能在每一次嘗試。最後得出結論,我的ng-model-options在onUpdate上有一個反彈設置,是問題所在。

如果您有一個debounce,請確保您使用$超時服務刷新。在角模擬中,這個超時服務已經通過刷新操作進行了擴展,該操作處理所有未完成的請求/操作。

var tobetriggered = angular.element(element[0].querySelector('.js-triggervalue')); 
    tobetriggered.val('value'); 
    tobetriggered.trigger('change'); 
    $timeout.flush();