eftest
eftest copied to clipboard
Fix NPE when `pretty/report` called outside test
report
can be run from code fixtures which caused
pretty/testing-vars-str
to throw a NullPointerException
because testing-vars is nil inside fixtures.
Can you provide an example of what you mean?
In a project I maintain, I do some cleanup work in a fixture. If there's a problem, I call report with a specific file/line based on my stack because the file/line of the cleanup job isn't really that interesting.
I can't give you my project, but here's an example of the sort of think I'm doing:
(ns eftest.fixture-fail
(:require [clojure.test :refer :all]))
(defn failing-fixture [f]
(f)
(clojure.test/report {:type :fail :message "This fixture noticed a problem"
:file "file_name.clj"
:line 10}))
(use-fixtures :each failing-fixture)
(deftest foo
(is (= 1 1)))
If I run this with lein test
, I get the following:
FAIL in clojure.lang.PersistentList$EmptyList@1 (file_name.clj:10)
This fixture noticed a problem
expected: nil
actual: nil
If I run it with lein-eftest
, I get:
% lein eftest
WARNING: humane-test-output not activated because clojure is version 1.7.0
Exception in thread "main" java.util.concurrent.ExecutionException: java.lang.NullPointerException, compiling:(/private/var/folders/2q/tk7cywk93217_d4pxn_5kft40000gn/T/form-init4376671560466612395.clj:1:125)
at clojure.lang.Compiler.load(Compiler.java:7239)
at clojure.lang.Compiler.loadFile(Compiler.java:7165)
at clojure.main$load_script.invoke(main.clj:275)
at clojure.main$init_opt.invoke(main.clj:280)
at clojure.main$initialize.invoke(main.clj:308)
at clojure.main$null_opt.invoke(main.clj:343)
at clojure.main$main.doInvoke(main.clj:421)
at clojure.lang.RestFn.invoke(RestFn.java:421)
at clojure.lang.Var.invoke(Var.java:383)
at clojure.lang.AFn.applyToHelper(AFn.java:156)
at clojure.lang.Var.applyTo(Var.java:700)
at clojure.main.main(main.java:37)
Caused by: java.util.concurrent.ExecutionException: java.lang.NullPointerException
at java.util.concurrent.FutureTask.report(FutureTask.java:122)
at java.util.concurrent.FutureTask.get(FutureTask.java:192)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at clojure.lang.Reflector.invokeMatchingMethod(Reflector.java:93)
at clojure.lang.Reflector.invokeNoArgInstanceMember(Reflector.java:313)
at eftest.runner$run_in_parallel$fn__2460.invoke(runner.clj:49)
at clojure.core$map$fn__4553.invoke(core.clj:2622)
at clojure.lang.LazySeq.sval(LazySeq.java:40)
at clojure.lang.LazySeq.seq(LazySeq.java:49)
at clojure.lang.RT.seq(RT.java:507)
at clojure.core$seq__4128.invoke(core.clj:137)
at clojure.core$dorun.invoke(core.clj:3009)
at eftest.runner$run_in_parallel.invoke(runner.clj:46)
at eftest.runner$test_vars$fn__2475.invoke(runner.clj:75)
at clojure.test$default_fixture.invoke(test.clj:674)
at eftest.runner$test_vars.invoke(runner.clj:70)
at eftest.runner$test_ns$fn__2490$fn__2491.invoke(runner.clj:83)
at clojure.core$with_redefs_fn.invoke(core.clj:7209)
at eftest.runner$test_ns$fn__2490.invoke(runner.clj:83)
at eftest.runner$test_ns.invoke(runner.clj:83)
at eftest.runner$test_all$fn__2498.invoke(runner.clj:90)
at clojure.core$map$fn__4553.invoke(core.clj:2624)
at clojure.lang.LazySeq.sval(LazySeq.java:40)
at clojure.lang.LazySeq.seq(LazySeq.java:49)
at clojure.lang.Cons.next(Cons.java:39)
at clojure.lang.RT.boundedLength(RT.java:1735)
at clojure.lang.RestFn.applyTo(RestFn.java:130)
at clojure.core$apply.invoke(core.clj:632)
at eftest.runner$test_all.invoke(runner.clj:91)
at eftest.runner$run_tests.invoke(runner.clj:148)
at user$eval2617$fn__2699.invoke(form-init4376671560466612395.clj:1)
at user$eval2617$fn__2669.invoke(form-init4376671560466612395.clj:1)
at user$eval2617.invoke(form-init4376671560466612395.clj:1)
at clojure.lang.Compiler.eval(Compiler.java:6782)
at clojure.lang.Compiler.eval(Compiler.java:6772)
at clojure.lang.Compiler.load(Compiler.java:7227)
... 11 more
Caused by: java.lang.NullPointerException
at java.util.concurrent.ConcurrentHashMap.get(ConcurrentHashMap.java:936)
at clojure.lang.Namespace.find(Namespace.java:188)
at clojure.core$find_ns.invoke(core.clj:3976)
at clojure.core$the_ns.invoke(core.clj:4008)
at clojure.core$ns_name.invoke(core.clj:4015)
at eftest.report.pretty$testing_vars_str.invoke(pretty.clj:35)
at eftest.report.pretty$eval2316$fn__2318.invoke(pretty.clj:109)
at clojure.lang.MultiFn.invoke(MultiFn.java:229)
at eftest.report.progress$eval2407$fn__2408$fn__2409.invoke(progress.clj:47)
at eftest.report.progress$eval2407$fn__2408.invoke(progress.clj:47)
at clojure.lang.MultiFn.invoke(MultiFn.java:229)
at eftest.fixture_fail$failing_fixture.invoke(fixture_fail.clj:6)
at clojure.test$compose_fixtures$fn__7664$fn__7665.invoke(test.clj:681)
at clojure.test$default_fixture.invoke(test.clj:674)
at clojure.test$compose_fixtures$fn__7664.invoke(test.clj:681)
at eftest.runner$test_vars$fn__2467.invoke(runner.clj:62)
at eftest.runner$wrap_test_with_timer$fn__2451.invoke(runner.clj:30)
at clojure.lang.AFn.applyToHelper(AFn.java:154)
at clojure.lang.AFn.applyTo(AFn.java:144)
at clojure.core$apply.invoke(core.clj:630)
at clojure.core$with_bindings_STAR_.doInvoke(core.clj:1868)
at clojure.lang.RestFn.applyTo(RestFn.java:142)
at clojure.core$apply.invoke(core.clj:634)
at clojure.core$bound_fn_STAR_$fn__4439.doInvoke(core.clj:1890)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at eftest.runner$test_vars$fn__2475$fn__2476$fn__2477.invoke(runner.clj:75)
at clojure.lang.AFn.run(AFn.java:22)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Tests failed.
Should report
be used for this? The documentation for clojure.test
explicitly says that:
the vars being tested will be a list in "
*testing-vars*
"
I guess you could argue that. In my case, I have a function in my test code that checks status codes and calls report
if it sees something fishy so I don't have to have status-checking code everywhere. That function gets called from fixtures during cleanup and sometimes something fishy crops up during cleanup.
I could wrap my (report)
calls with a *testing-vars*
but it seems nicer if eftest would use the information it has to generate a message.
I updated the test to set *testing-vars*
to []
instead of nil
because that's what's actually happening in real life.