[英]`with-redefs` not binding certain functions (Clojure)
我正在嘗試在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))))))
任何幫助表示贊賞! :)
您未看到重新定義生效的原因是,通過initial-setup
在測試set-starter
下的函數使用了partial
。 partial
返回一個新函數,該函數保留對先前定義的函數的引用。
一個簡單的例子:
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"
嘗試不使用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]
換句話說,不需要重新定義功能。
就像@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
的“ constant” var
),我們可以獲得預期的結果,並且with-redefs
可以正常工作。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.