【问题标题】:How to properly (unit) test Om/React components?如何正确(单元)测试 On/React 组件?
【发布时间】:2014-11-30 08:57:05
【问题描述】:

我已经开发了 Om/React 组件,但无法通过单元测试来推动我的开发,我感到非常不舒服。我试图设置我的 clojurescript 项目来对这些组件运行单元测试,到目前为止,我已经能够编写单元测试并实例化我的组件。我缺少的是确保我的组件正确响应某些事件的能力,例如onChange 这样我就可以模拟用户输入。

这是我的测试代码:

(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"    "foo@bar.com")
      (change-field! "userRequestedPassword" "secret")
      (change-field! "confirmPassword"       "nosecret")

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

此测试运行但失败,因为change-field! 函数实际上并未更改组件的状态。这是组件的(部分)代码(原谅重复......):

(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")))))

通过在测试中放置跟踪可以看到,当我触发change 事件时,组件的状态没有更新,尽管它被正确触发了。我怀疑这与 Om/React 的工作方式有关,包装 DOM 组件,但不确定如何处理。

【问题讨论】:

  • 只是为了确保:您的测试组件是否完全由 om 呈现(甚至只是“在内存中”?)您能否确认 DOM 元素是实际创建的,并且附加了 onChange 处理程序?跨度>
  • 是的。 on click 事件被触发,我可以看到消息通过 core.async 通道:这就是 submit-registration 所做的,将 xhrio 调用的结果发送到 source 通道,然后由 (go ...) 接收在测试中循环。
  • @insitu 也许不同的方法会有所帮助。我使用 mochify 测试反应组件,并在 mochify 的 wiki 页面中添加了一个示例:github.com/mantoni/mochify.js/wiki/…
  • @TJ。感谢您的指点。我也在用同样的方式思考,使用 React 自己的测试工具。但这可能需要一些工作才能正确集成到 clojurescript 和 Om 中。会看看我是否可以回到它...

标签: unit-testing reactjs clojurescript om


【解决方案1】:

您可以使用 react 库中的 ReactTestUtils 在组件中模拟事件。 我正在使用 mocha 并执行类似的操作来测试更改事件:

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); 
}

【讨论】:

    猜你喜欢
    • 2021-05-19
    • 1970-01-01
    • 1970-01-01
    • 2023-03-31
    • 1970-01-01
    • 1970-01-01
    • 2023-03-26
    • 1970-01-01
    • 2022-06-11
    相关资源
    最近更新 更多