我正在尝试使用Midje单独测试我的路由。对于某些访问数据库的路由,我可以使用(provided ...)
将路由与真正的数据库调用隔离开来。我已经介绍了Friend进行身份验证,但我无法伪造对凭证功能的调用。
我的凭证功能看起来像这样(它是这样实现的,因为我不想让它被调用):
(defn cred-fn
[creds]
(println (str "hey look I got called with " creds))
(throw (Exception.)))
路由的中间件看起来像这样:
(def app
(-> app-routes
(wrap-json-body {:keywords? true :bigdecimals? true})
wrap-json-response
(wrap-defaults defaults)
(friend/authenticate
{:unauthorized-handler json-auth/login-failed
:workflows [(json-auth/json-login
:login-uri "/login"
:login-failure-handler json-auth/login-failed
:credential-fn auth/cred-fn)]})
(ring-session/wrap-session)))
我也尝试过不使用auth-json-workflow,路由的实现看起来几乎相同,我可以补充说,如果它有帮助,但我得到相同的结果。
然后我的测试看起来像这样(使用ring-mock):
(defn post [url body]
(-> (mock/request :post url body)
(mock/content-type "application/json")
app))
(fact "login with incorrect username and password returns unauthenticated"
(:status (post "/login" invalid-auth-account-json)) => 401
(provided
(auth/cred-fn anything) => nil))
(fact "login with correct username and password returns success"
(:status (post "/login" auth-account-json)) => 200
(provided
(auth/cred-fn anything) => {:identity "root"}))
然后我得到以下输出运行测试:
hey look I got called with {:password "admin_password", :username "not-a-user"}
FAIL at (handler.clj:66)
These calls were not made the right number of times:
(auth/cred-fn anything) [expected at least once, actually never called]
FAIL "routes - authenticated routes - login with incorrect username and password returns unauthenticated" at (handler.clj:64)
Expected: 401
Actual: java.lang.Exception
clojure_api_seed.authentication$cred_fn.invoke(authentication.clj:23)
hey look I got called with {:password "admin_password", :username "root"}
FAIL at (handler.clj:70)
These calls were not made the right number of times:
(auth/cred-fn anything) [expected at least once, actually never called]
FAIL "routes - authenticated routes - login with correct username and password returns success" at (handler.clj:68)
Expected: 200
Actual: java.lang.Exception
clojure_api_seed.authentication$cred_fn.invoke(authentication.clj:23)
所以我可以看到所提供的声明没有生效,我不知道为什么。有什么想法吗?
答案 0 :(得分:1)
我最近遇到了类似的问题,经过一番挖掘我
我想我明白为什么会这样。我们来看看如何
auth/cred-fn
的绑定随时间而变化。
(clojure.pprint/pprint (macroexpand '(defn cred-fn
[creds]
(println (str "hey look I got called with " creds))
(throw (Exception.)))))
(def
cred-fn
(clojure.core/fn
([creds]
(println (str "hey look I got called with " creds))
(throw (Exception.)))))
如上所示,defn
宏将当前命名空间中的符号cred-fn
实例化,并将其绑定到Var引用
你的虚拟功能。
(def app
(-> app-routes
(wrap-json-body {:keywords? true :bigdecimals? true})
wrap-json-response
(wrap-defaults defaults)
(friend/authenticate
{:unauthorized-handler json-auth/login-failed
:workflows [(json-auth/json-login
:login-uri "/login"
:login-failure-handler json-auth/login-failed
:credential-fn auth/cred-fn)]})
(ring-session/wrap-session)))
这是重要的一块。在编译时,我们通过线程
app-routes
通过一系列功能。其中一个功能
是friend/authenticate
,它使用密钥:workflows
获取地图。
:workflows
的值是填充了结果的向量
致json-auth/json-login
的电话,该电话会收到auth/credential-fn
作为参数。记住,我们在def里面,所以这就是全部
发生在编译时。我们在中查找符号cred-fn
auth名称空间,并传入符号绑定的Var
至。在这一点上,这仍然是虚拟实现。
据推测,json-auth/json-login
捕获了这种实现
并设置一个调用它的请求处理程序。
(fact "login with incorrect username and password returns unauthenticated"
(:status (post "/login" invalid-auth-account-json)) => 401
(provided
(auth/cred-fn anything) => nil))
现在我们在运行时。在我们的先决条件下,Midje重新启动了
符号auth/cred-fn
到引用模拟的Var。但是
我们auth/cred-fn
'时已经捕获def
的值
app
在编译时。
那么你发布的变通方法怎么样? (这实际上是导致我的线索 尤里卡时刻 - 谢谢你。)
(defn another-fn []
(println (str "hey look I got called"))
(throw (Exception.)))
(defn cred-fn [creds]
(another-fn))
在你的测试中......
(fact "login with incorrect username and password returns unauthenticated"
(:status (post "/login" invalid-auth-account-json)) => 401
(provided
(auth/another-fn) => nil))
(fact "login with correct username and password returns success"
(:status (post "/login" auth-account-json)) => 200
(provided
(auth/another-fn) => {:identity "root"}))
这是有效的,因为在编译时,auth/cred-fn
的值为。{1}}
被捕获是一个简单委托的功能
auth/another-fn
。请注意,auth/another-fn
尚未发生
评估了。现在在我们的测试中,Midje重新绑定auth/another-fn
引用模拟。然后它执行帖子,并在某处
在中间件中,auth/cred-fn
被调用。内
auth/cred-fn
,我们查找与auth/another-fn
绑定的Var(其中
是我们的模拟),并调用它。当然,现在的行为是
正如你第一次想象的那样。
这个故事的寓意是,careful with the def in Clojure
答案 1 :(得分:0)
我仍然不确定为什么会这样,但我有一个解决方法。如果我将credential-fn
替换为:
(defn another-fn
[]
(println (str "hey look I got called"))
(throw (Exception.)))
(defn cred-fn
[creds]
(another-fn))
然后在测试中为新函数创建一个假的,如下所示:
(fact "login with incorrect username and password returns unauthenticated"
(:status (post "/login" invalid-auth-account-json)) => 401
(provided
(auth/another-fn) => nil))
(fact "login with correct username and password returns success"
(:status (post "/login" auth-account-json)) => 200
(provided
(auth/another-fn) => {:identity "root"}))
我得到了我期待的结果。 cred-fn
仍会被调用,但由于another-fn
而无法调用provided
。
如果有人知道为什么会这样,我有兴趣知道。这可能是由于凭证函数被调用的方式? - https://github.com/marianoguerra/friend-json-workflow/blob/master/src/marianoguerra/friend_json_workflow.clj#L46