【问题标题】:`with-redefs` not binding certain functions (Clojure)`with-redefs` 不绑定某些函数(Clojure)
【发布时间】:2018-01-21 20:50:52
【问题描述】:

我正在尝试在 Clojure 中测试“井字游戏”命令行游戏,并希望重新定义使用 read-line 模拟用户输入的函数。但是,with-redefs 没有像我预期的那样工作,而且(似乎)与其他测试不同。

这是我的测试

(ns game.setup-test
  (:require [clojure.test :refer :all]
            [clojure.string :as st]
            [game.helpers :as gh]
            [game.setup :refer :all]))


(deftest initial-setup-test
  (testing "Can set initial options"
    (with-redefs [get-game-type-input (fn [] "1")
                  get-marker-input (fn [] "X") 
                  get-starter-input (fn [] "1")]
      (do (initial-setup my-game)
       (is (= :human (get-in @my-game [:player1 :type])))
       (is (= :human (get-in @my-game [:player2 :type])))
       (is (= [:player1 :player2] (get @my-game :up-next)))
       (is (= "X" (get-in @my-game [:player1 :marker])))
       (is (= "O" (get-in @my-game [:player2 :marker])))))))

这是我在setup.clj 中的函数定义

(ns game.setup)

(defn get-input [requestor validator]
  (fn [] (do (requestor)
             (if-let [input (validator (read-line))]
                      input
                      (recur)))))

(def get-marker-input (get-input request-p1-marker valid-marker?))
(def get-game-type-input (get-input request-game-type valid-type?))
(def get-starter-input (get-input request-starter-type valid-starter?))


(def set-starter
  (partial set-game-option
           get-starter-input
           starter-opts
           swap-merge
           print-starter-player))

(defn initial-setup [game-state]
  (do (set-game-type game-state)
      (set-player-markers game-state)
      (set-starter game-state)))

(defn set-game-option [input-getter attr-opts updater printer game]
  (-> (input-getter)      ; gets user input
      attr-opts           ; returns corresponding update-map
      (#(updater game %)) ; updates the game-state with update-map
      printer))           ; prints the updated game-state

运行此测试时。我仍然看到 request-* 函数的 STDOUT 输出,并且测试等待输入到 STDIN。 所以,我假设with-redefs 没有像我预期的那样“重新定义”get-* 函数。

按我预期工作的不同测试

(deftest get-marker-input-test
  (testing "Reads perfect input"
    (with-redefs [read-line (fn [] "X")]
      (is (= "X" (get-marker-input))))))

感谢任何帮助! :)

【问题讨论】:

  • 我认为可能还有其他问题。你的with-redefs 看起来不错,我无法重现。也许发布你的其余代码(initial-setup 等),或者做一个最小的复制案例。如果您已经在此代码上迭代了一段时间,也请尝试重新启动您的 REPL。
  • 刚试过重启。没有影响。
  • 我可能会尝试从 9 恢复到 Java 8... 9 还存在一些其他问题。如果解决了问题,我会通知您。

标签: unit-testing clojure


【解决方案1】:

你没有看到你的redef生效的原因是被测试的函数set-starter通过initial-setup使用partialpartial 返回一个新函数,该函数保留对先前定义的函数的引用。

一个更简单的例子:

boot.user=> (def my-dumb-fn (partial str "prefix"))
#'boot.user/my-dumb-fn
boot.user=> (with-redefs [str (fn [s] "hello")] (str 23))
"hello"
boot.user=> (with-redefs [str (fn [s] "hello")] (my-dumb-fn 23))
"prefix23"

【讨论】:

  • 知道了。谢谢!您对以不同的方式编写测试有什么建议,以便我仍然可以测试initial-setup 函数的功能?
【解决方案2】:

尽量不要使用read-line,而只是使用read:它将输入字符串读入数据结构而不是字符串。此外,当它找到一个空格(或者更准确地说是阅读形式的结尾)时,它会停止读取输入,以防止其余部分被消耗。

一个特殊的内置宏with-in-str 允许您指定一些特定的输入用于测试目的,如下所示:

(with-in-str "X 0 X 0 0 X"
  (let [turn1 (read)
        turn2 (read)
        ;; more reads ...
        ]
    [turn1
     turn2
     ;; more read values
     ]))

返回

[X 0]

也就是说,没有必要重新定义函数。

【讨论】:

  • 很高兴知道!谢谢。
【解决方案3】:

正如@dpassen 所说,不要使用partial(我从不使用)。这是一个替代方案:

> lein new tupelo-app fred     ; make a new project `fred` with nice defaults
> cd fred

剪切和粘贴如下所示:

~/expr/fred > cat src/tst/fred/core.clj
(ns tst.fred.core
  (:use fred.core tupelo.core tupelo.test)
  (:require 
    [clojure.java.io :as io]
    [tupelo.string :as ts] )
  (:import [fred Calc]))

(def x 5)
(def  p1     (partial + x))
(defn p2 [y] (+ x y))

(dotest
  (spyx (p1 2))
  (spyx (p2 2))
  (with-redefs [x 10]
    (spyx (p1 3))
    (spyx (p2 3))))

我们得到结果:

---------------------------------
   Clojure 1.9.0    Java 9.0.1
---------------------------------
Testing fred.core

Testing tst.fred.core
(p1 2) => 7
(p2 2) => 7

(p1 3) => 8
(p2 3) => 13

因此,通过将p2 定义为一个函数(而不是partial 的“常量”var),我们得到了预期的结果并且with-redefs 可以正常工作。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-09-07
    • 2016-03-28
    • 2013-05-31
    • 1970-01-01
    • 1970-01-01
    • 2023-04-07
    • 2019-04-02
    • 1970-01-01
    相关资源
    最近更新 更多