2024 Clojure 状态调查! 分享您的想法。

欢迎!请参阅 关于 页面以获取更多关于如何操作的信息。

+2 投票
测试

如果在 clojure.test deftest 中发生异常,错误将传递给 clojure.test/report 并带有 :error 键,默认情况下将打印整个堆栈跟踪。堆栈包括测试框架本身,如果是通过外部测试运行器(如 lein test、clojure.main 等)调用,那么堆栈深度为 ~60 堆栈帧,这都是无关的噪音。

有一个 dyn var clojure.test/stack-trace-depth,默认为 nil(所有)。这个 dynvar 可以在调用 clojure.test 运行器时绑定,以便在测试期间发生异常时查看更少的堆栈跟踪,但这既难以设置外部测试运行器,又是易错的。

最好的做法是从堆栈的底部过滤掉框架噪音,只显示相关的帧。

给定一个测试像

(ns foo.core-test
  (:require [clojure.test :refer :all]))

(deftest a-test
  (testing "FIXME, I fail."
    (throw (ex-info "I suck" {:a 1}))
    (is (= 0 1))))

报告的错误(这里来自 lein test,但任何外部运行器类似),只有前两行是相关的,其余的是偶然的噪音

ERROR in (a-test) (core_test.clj:13)
Uncaught exception, not in assertion.
expected: nil
  actual: clojure.lang.ExceptionInfo: I suck
{:a 1}
 at foo.core_test$fn__364.invokeStatic (core_test.clj:13)
    foo.core_test/fn (core_test.clj:11)
    clojure.test$test_var$fn__9737.invoke (test.clj:717)        ;; noise from here down
    clojure.test$test_var.invokeStatic (test.clj:717)
    clojure.test$test_var.invoke (test.clj:708)
    clojure.test$test_vars$fn__9763$fn__9768.invoke (test.clj:735)
    clojure.test$default_fixture.invokeStatic (test.clj:687)
    clojure.test$default_fixture.invoke (test.clj:683)
    clojure.test$test_vars$fn__9763.invoke (test.clj:735)
    clojure.test$default_fixture.invokeStatic (test.clj:687)
    clojure.test$default_fixture.invoke (test.clj:683)
    clojure.test$test_vars.invokeStatic (test.clj:731)
    clojure.test$test_all_vars.invokeStatic (test.clj:737)
    clojure.test$test_ns.invokeStatic (test.clj:758)
    clojure.test$test_ns.invoke (test.clj:743)
    user$eval224$fn__287.invoke (form-init4292042596091073068.clj:1)
    clojure.lang.AFn.applyToHelper (AFn.java:156)
    clojure.lang.AFn.applyTo (AFn.java:144)
    clojure.core$apply.invokeStatic (core.clj:667)
    clojure.core$apply.invoke (core.clj:660)
    leiningen.core.injected$compose_hooks$fn__154.doInvoke (form-init4292042596091073068.clj:1)
    clojure.lang.RestFn.applyTo (RestFn.java:137)
    clojure.core$apply.invokeStatic (core.clj:665)
    clojure.core$apply.invoke (core.clj:660)
    leiningen.core.injected$run_hooks.invokeStatic (form-init4292042596091073068.clj:1)
    leiningen.core.injected$run_hooks.invoke (form-init4292042596091073068.clj:1)
    leiningen.core.injected$prepare_for_hooks$fn__159$fn__160.doInvoke (form-init4292042596091073068.clj:1)
    clojure.lang.RestFn.applyTo (RestFn.java:137)
    clojure.lang.AFunction$1.doInvoke (AFunction.java:31)
    clojure.lang.RestFn.invoke (RestFn.java:408)
    clojure.core$map$fn__5866.invoke (core.clj:2755)
    clojure.lang.LazySeq.sval (LazySeq.java:42)
    clojure.lang.LazySeq.seq (LazySeq.java:51)
    clojure.lang.Cons.next (Cons.java:39)
    clojure.lang.RT.boundedLength (RT.java:1792)
    clojure.lang.RestFn.applyTo (RestFn.java:130)
    clojure.core$apply.invokeStatic (core.clj:667)
    clojure.test$run_tests.invokeStatic (test.clj:768)
    clojure.test$run_tests.doInvoke (test.clj:768)
    clojure.lang.RestFn.applyTo (RestFn.java:137)
    clojure.core$apply.invokeStatic (core.clj:665)
    clojure.core$apply.invoke (core.clj:660)
    user$eval224$fn__299$fn__332.invoke (form-init4292042596091073068.clj:1)
    user$eval224$fn__299$fn__300.invoke (form-init4292042596091073068.clj:1)
    user$eval224$fn__299.invoke (form-init4292042596091073068.clj:1)
    user$eval224.invokeStatic (form-init4292042596091073068.clj:1)
    user$eval224.invoke (form-init4292042596091073068.clj:1)
    clojure.lang.Compiler.eval (Compiler.java:7177)
    clojure.lang.Compiler.eval (Compiler.java:7167)
    clojure.lang.Compiler.load (Compiler.java:7636)
    clojure.lang.Compiler.loadFile (Compiler.java:7574)
    clojure.main$load_script.invokeStatic (main.clj:475)
    clojure.main$init_opt.invokeStatic (main.clj:477)
    clojure.main$init_opt.invoke (main.clj:477)
    clojure.main$initialize.invokeStatic (main.clj:508)
    clojure.main$null_opt.invokeStatic (main.clj:542)
    clojure.main$null_opt.invoke (main.clj:539)
    clojure.main$main.invokeStatic (main.clj:664)
    clojure.main$main.doInvoke (main.clj:616)
    clojure.lang.RestFn.applyTo (RestFn.java:137)
    clojure.lang.Var.applyTo (Var.java:705)
    clojure.main.main (main.java:40)

我们正在其他地方进行框架过滤,我认为将此过滤到改进默认体验并不困难(仍可以是一个 dyn var,在需要时不进行过滤)。

2 个答案

0 投票
0 投票

我认为我们需要以某种方式保留固定帧。

ERROR in (a-test) (core_test.clj:13)
Uncaught exception, not in assertion.
expected: nil
  actual: clojure.lang.ExceptionInfo: I suck
{:a 1}
 at foo.core_test$fn__364.invokeStatic (core_test.clj:13)
    foo.core_test/fn (core_test.clj:11)
    clojure.test$test_var$fn__9737.invoke (test.clj:717)        ;; noise from here down
    clojure.test$test_var.invokeStatic (test.clj:717)
    clojure.test$test_var.invoke (test.clj:708)
    clojure.test$test_vars$fn__9763$fn__9768.invoke (test.clj:735)
    foo.core_test$eval139891$hello_fixture__139892.invoke (core_test.clj:237) ;; this is relevant too!!
    clojure.test$compose_fixtures$fn__9755$fn__9756.invoke (test.clj:694)
    clojure.test$default_fixture.invokeStatic (test.clj:687)
    clojure.test$default_fixture.invoke (test.clj:683)
    
这是一个好问题,需要更多分析。可能更有帮助的是,可以这样说:

活动固定帧:hello-fixture

也就是说,固定帧的行号相关吗?

如果异常来自于固定帧本身,可能我们想要的是不同的东西。
是的,已知/列出固定帧似乎是OK的。


但我们可以留一个属性来禁用它
`-Dclojure.test.cleanup-exception=false`
...