2014-10-06 11 views
8

Mam opracowane komponenty Om/React, ale czuję się naprawdę niekomfortowo, ponieważ nie jestem w stanie poprowadzić mojego rozwoju za pomocą testów jednostkowych. Próbowałem skonfigurować mój projekt clojurescript, aby uruchomić testy jednostkowe na tych komponentach i do tej pory osiągnąłem punkt, w którym jestem w stanie pisać testy jednostkowe i tworzyć instancję moich komponentów. Brakuje mi możliwości zagwarantowania, że ​​moje komponenty będą odpowiednio reagować na niektóre zdarzenia, np. onChange, dzięki czemu mogę symulować dane wejściowe użytkownika.Jak prawidłowo (jednostka) przetestować komponenty Om/React?

Tu jest mój kodu testu:

(defn simulate-click-event 
    "From https://github.com/levand/domina/blob/master/test/cljs/domina/test.cljs" 
    [el] 
    (let [document (.-document js/window)] 
    (cond 
    (.-click el) (.click el) 
    (.-createEvent document) (let [e (.createEvent document "MouseEvents")] 
           (.initMouseEvent e "click" true true 
               js/window 0 0 0 0 0 
               false false false false 0 nil) 
           (.dispatchEvent el e)) 
    :default (throw "Unable to simulate click event")))) 

(defn simulate-change-event 
    "From https://github.com/levand/domina/blob/master/test/cljs/domina/test.cljs" 
    [el] 
    (let [document (.-document js/window)] 
    (cond 
    (.-onChange el) (do (print "firing on change on " el) (.onChange el)) 
    (.-createEvent document) (let [e (.createEvent document "HTMLEvents")] 
           (print "firing " e " on change on " (.-id el)) 
           (.initEvent e "change" true true) 
           (.dispatchEvent el e)) 
    :default (throw "Unable to simulate change event")))) 

(def sink 
    "contains a channel that receives messages along with notification type" 
    (chan)) 

;; see http://yobriefca.se/blog/2014/06/04/publish-and-subscribe-with-core-dot-asyncs-pub-and-sub/ 
(def source 
    (pub sink #(:topic %))) 

(defn change-field! 
    [id value] 
    (let [el (sel1 (keyword (str "#" id)))] 
    (dommy/set-value! el value) 
    (simulate-change-event el) 
    )) 

(deftest ^:async password-confirmation 
    (testing "do not submit if passwords are not equal" 
    (let [subscription (chan)] 
     (sub source :user-registration subscription) 
     (om/root 
     (partial u/registration-view source sink) 
     nil 
     {:target (sel1 :#view)}) 

     (go 
     (let [m (<! subscription)] 
     (is (= :error (:state m))) 
     (done) 
     )) 

     (change-field! "userRequestedEmail" "[email protected]") 
     (change-field! "userRequestedPassword" "secret") 
     (change-field! "confirmPassword"  "nosecret") 

     (simulate-click-event (sel1 :#submitRegistration)) 
    ))) 

Ten test jest wykonywany, ale nie dlatego, że funkcja change-field! nie zmienia stanu składnika. Oto (część) kod komponentu (odpuszczania powielania ...):

(defn registration-view 
    "Registration form for users. 

    Submitting form triggers a request to server" 
    [source sink _ owner] 
    (reify 

    om/IInitState 
    (init-state [_] 
       {:userRequestedEmail "" 
       :userRequestedPassword "" 
       :confirmPassword ""} 
       ) 

    om/IRenderState 
    (render-state 
    [this state] 
    (dom/fieldset 
     nil 
     (dom/legend nil "User Registration") 
     (dom/div #js { :className "pure-control-group" } 

       (dom/label #js { :for "userRequestedEmail" } "EMail") 
       (dom/input #js { :id "userRequestedEmail" :type "text" :placeholder "Enter an e-mail" 
           :value (:userRequestedEmail state) 
           :onChange #(om/set-state! owner :userRequestedEmail (.. % -target -value))})) 

     (dom/div #js { :className "pure-control-group" } 
       (dom/label #js { :for "userRequestedPassword" } "Password") 
       (dom/input #js { :id "userRequestedPassword" :type "password" :placeholder "Enter password" 
           :value (:userRequestedPassword state) 
           :onChange #(om/set-state! owner :userRequestedPassword (.. % -target -value))})) 

     (dom/div #js { :className "pure-control-group" } 
       (dom/label #js { :for "confirmPassword" } "") 
       (dom/input #js { :id "confirmPassword" :type "password" :placeholder "Confirm password" 
           :value (:confirmPassword state) 
           :onChange #(om/set-state! owner :confirmPassword (.. % -target -value))})) 


     (dom/button #js {:type "submit" 
         :id "submitRegistration" 
         :className "pure-button pure-button-primary" 
         :onClick #(submit-registration state sink)} 
        "Register"))))) 

Co widzę umieszczając ślady w testach jest, że stan elementu nie jest aktualizowana, kiedy wyzwolić change Zdarzenie, mimo że zostało poprawnie uruchomione. Podejrzewam, że ma to związek ze sposobem działania Om/React, zawijaniem komponentów DOM, ale nie wiem, jak sobie z tym poradzić.

+0

Wystarczy, aby mieć pewność: to testowany składnik świadczonych przez OM w ogóle (nawet tylko „pamięć”?) Czy możesz potwierdzić DOM elementy są rzeczywiście tworzone i dołączono procedurę obsługi OnChange? – phtrivier

+0

Tak. Zdarzenie 'on click' jest wyzwalane, widzę komunikat przechodzący przez kanał core.async: To właśnie robi' submit-registration', wysyłając wynik wywołania xhrio do kanału 'source', który następnie jest odbierany przez '' (idź ...) 'zapętla się wewnątrz testu. – insitu

+0

@insitu Może inne podejście pomoże. Testuję komponenty reagujące za pomocą mochify i dodałem przykład do strony wiki mochify: https://github.com/mantoni/mochify.js/wiki/Testing-a-ReactJS-Component-with-Mochify –

Odpowiedz

1

Możesz symulować zdarzenia w swoich komponentach za pomocą ReactTestUtils z bibliotek reagowania. Używam mokka i robi coś takiego przetestować zdarzenia change:

var comp = ReactTestUtils.renderIntoDocument(<Component />); 
var changingElement = ReactTestUtils.findRenderedDOMComponentWithClass(comp, 'el-class'); 
it ('calls myChangeMethod on change', function() { 
    ReactTestUtils.Simulate.change(changingElement); 
    assert(comp.myChangeEventMethod.called, true); 
}