2013-07-13 14 views
19

Mam następującą dyrektywę AngularJS, która tworzy element input. Wejście ma atrybut ng-change, który uruchamia funkcję doIt(). W jednostce testowej mojej dyrektywy chcę sprawdzić, czy funkcja doIt jest wywoływana, gdy użytkownicy zmieniają dane wejściowe. Ale test nie przechodzi. Chociaż działa w przeglądarce podczas testowania ręcznego.Jak wywołać zmianę Ng w teście dyrektywy w AngularJS

dyrektywa:

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

Test:

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

żywo demo (ng-change): http://plnkr.co/edit/0yaUP6IQk7EIneRmphbW?p=preview


Teraz, test przechodzi jeżeli ręcznie powiązać change imprezę zamiast użycia atrybutu ng-change.

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

żywo demo (wiązanie ręczne): http://plnkr.co/edit/dizuRaTFn4Ay1t41jL1K?p=preview


Czy istnieje sposób na wykorzystanie ng-change i uczynić go testować? Dziękuję Ci.

+1

Zmienna ng jest wyzwalana przy zmianach modelu, a nie zdarzeń. dlaczego nie przetestować kodu kontrolera dla twojego "doIt" fn? – marko

+0

@marko, dzięki za dobre pytanie. Wszystko, co chcę zrobić w teście dyrektywy, to sprawdzenie, czy nie jest wywoływana, gdy użytkownik zmienia dane wejściowe. sam będzie wyśmiewany w teście dyrektywy, ponieważ jest już testowany w teście kontrolera, jak sugerujesz. – Evgenii

Odpowiedz

30

Od komentarz wyjaśniający:

Wszystko co chcę robić w teście dyrektywy jest, aby sprawdzić, doIt jest wywoływana, gdy użytkownik zmieni wejście.

, czy ekspresja wskazany przez ng-change jest prawidłowo ocenić, czy nie jest tak naprawdę odpowiedzialność dyrektywy ngModel, więc nie jestem pewien, że mogę przetestować go w ten sposób; zamiast tego ufałbym, że dyrektywy ngModel i ngChange zostały poprawnie zaimplementowane i przetestowane, aby wywoływać określoną funkcję, i po prostu przetestuj, czy wywołanie samej funkcji wpływa na dyrektywę w prawidłowy sposób. Do obsługi scenariusza pełnego wykorzystania można użyć testu end-to-end lub integracji.

Powiedział, że można zdobyć instancji ngModelController który napędza zmiany zwrotnego ngModel i ustawić wartość widok siebie:

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

Jak powiedziałem, choć czuję, jak to jest dotarcie zbyt daleko do obowiązków w zakresie ngModel, przełamywania czarnego boksu, jaki otrzymujesz dzięki dyrektywom, które są naturalnie składalne.

Przykład: http://plnkr.co/edit/BaWpxLuMh3HvivPUbrsd?p=preview


[Aktualizacja]

Po rozglądając się na źródło angularjs, stwierdziliśmy, że dodaje się również działa:

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

Wygląda na to, wydarzenie jest inne w niektórych przeglądarkach; input wydaje się działać w Chrome.

Przykład: http://plnkr.co/edit/rbZ5OnBtKMzdpmPkmn2B?p=preview

Oto relevant AngularJS code, która korzysta z usługi $sniffer aby dowiedzieć się, które wydarzenie do wyzwalania:

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

Nawet uwzględniając to, że nie jestem pewien, czy przetestować dyrektywy w ten sposób.

+4

Jeśli chodzi o zdarzenia, myślę, że 'input' jest dla wprowadzania tekstu i tym podobnych, a' change' dla pól wyboru i przycisków opcji. Tak przynajmniej działa u mnie testowanie PhantomJS. –

+1

Wygląda na to, że browserTrigger występuje tylko w kanonika-scenario.js. Oznacza to, że jest przeznaczony wyłącznie do testu e2e, prawda? Nie mogę użyć tej funkcji do testu jednostkowego, który celowo uważam. – unludo

+0

@unludo Być może; Chodzi mi tylko o to, że źródło AngularJS zostało napisane, by uruchamiać inne wydarzenie w niektórych przeglądarkach. –

0

Szukałem tej prostej linii przez długie godziny. Po prostu to zapisz tutaj.

Jak wybrać wartość z html-select, używając Karmy, a więc czy działa funkcja ng-change?

HTML:

Controller lub dyrektywa JS:

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

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

Karma Test fragment:

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

konsoli:

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

I googled "Kątowa dyrektywa wyzwalacza ng-zmiany", a to pytanie StackOverflow było najbliżej do czegokolwiek przydatnego, więc odpowiem "Jak wywołać zmianę Ng w dyrektywie", ponieważ inni są zobowiązani do wylądowania na tej stronie i nie wiem, jak inaczej podać te informacje.

Wewnątrz funkcji łącza w sprawie dyrektywy, to wywołać funkcję ng-change na elemencie:

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

element.trigger("change") i element.trigger("input") nie działa dla mnie, ani nic innego nie udało mi się znaleźć w Internecie.

Jako przykład uruchamiając NG-zmiany na rozmycie:

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

Przykro mi, że nie jest to bezpośrednio odpowiadając na pytanie OP. Będę więcej niż szczęśliwy, aby podjąć rozsądną radę, gdzie i jak udostępnić te informacje.

+1

Najlepiej byłoby zadać osobne pytanie, a następnie odpowiedzieć samodzielnie (brzmi dziwnie, ale jest to zachęcane). Poszukuję również czegoś, co nie jest do końca dobrym rozwiązaniem lub problemem, i szybciej znajdę odpowiedź, jeśli pojawi się konkretne pytanie. –

0

prosty i działa w jednostce testowej 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

starali się uzyskać to do pracy, ale nie w każdej próbie. W końcu doszedłem do wniosku, że moim problemem jest opcja ng-model-options z ustawieniem debounce na onUpdate.

Jeśli masz wadę, upewnij się, że opróżniłeś usługę $ timeout. W makiety kątowej ta usługa limitu czasu została rozszerzona o operację flush, która obsługuje wszystkie niespełnione żądania/akcje.

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