marick / Midje

Midje provides a migration path from clojure.test to a more flexible, readable, abstract, and gracious style of testing
MIT License
1.68k stars 128 forks source link

Using aot with code and tests intermingled fails #274

Open jaffee opened 10 years ago

jaffee commented 10 years ago

This https://gist.github.com/jaffee/10488707 has a minimal reproduction of the problem.

If you take the :aot out of project.clj, things work as expected, with it in there lein repl gives a huge stacktrace and hangs until the repl timeout is up.

As a general comment, it would be nice if the midje documentation had a full example of intermingling code and tests (including a project.clj and what should be imported/required in source files). Thanks

BinarySplit commented 9 years ago

I get this too on midje 1.6.3 running Clojure 1.6.0.

Here's the relevant part of the stack trace:

Caused by: java.lang.NullPointerException
    at java.util.concurrent.ConcurrentHashMap.hash(ConcurrentHashMap.java:333)
    at java.util.concurrent.ConcurrentHashMap.get(ConcurrentHashMap.java:988)
    at clojure.lang.Namespace.find(Namespace.java:188)
    at clojure.core$find_ns.invoke(core.clj:3728)
    at clojure.core$the_ns.invoke(core.clj:3760)
    at clojure.core$ns_name.invoke(core.clj:3767)
    at midje.Bootstrap$bootstrap.invoke(Bootstrap.clj:8)
    at midje.sweet__init.load(Unknown Source)
    at midje.sweet__init.<clinit>(Unknown Source)

I'm pretty new to Clojure, but this is my hypothesis: midje/sweet.clj calls (midje.Bootstrap/bootstrap) before it calls (ns midje.sweet), which breaks the namespace chain and causes *ns* to be null in midje.Bootstrap/bootstrap. midje.Bootstrap/bootstrap calls (ns-name *ns*), which causes a NullReferenceException. However, at this point, code is being compiled - it shouldn't be getting executed, should it?

This is potentially an issue with Clojure's AOT mode - not sure if there's any good reason for ns to be null in this situation. I don't really know.

marick commented 9 years ago

I think you are probably right.

I've never had much fun when I've used AOT, and I avoid it. Because of that, I'm not offhand sure how to work around it. The startup code is complex, and I doubt it needs to be that complex.

WhittlesJr commented 5 years ago

Yeah this is a problem for me too. I want to have Midje in my uberjar and I seemingly can't do so.

WhittlesJr commented 5 years ago

This issue alone made me switch to clojure.test, sadly. And clojure.test is awfully deficient, so it wasn't an easy decision to make.

My application is an automated test system for embedded devices, and I was using Midje to write my device tests as well as my unit tests. Thus I need the unit testing framework to be in my runtime application. Until now I've deployed to my test head server by literally just copying over the source and doing lein run, but the start-up time became atrocious once I reached a certain number of parallel application instances. So I really have to AOT compile into an uberjar, and Midje can't do that.

Granted, my use case is clearly out of scope from the original intent of the project, but I really hope someone can find a fix for this issue.

philomates commented 5 years ago

Hey @WhittlesJr I'm not super familiar with aot, so I don't know how hard fixing this would be. I suspect it isn't something trivial given the very dynamic nature of some of the core Midje code.

That said, if you would like a clojure.test experience that is similar to what Midje offers, I'd suggest trying https://github.com/nubank/matcher-combinators, https://github.com/pmatiello/mockfn/, and a test runner like https://github.com/lambdaisland/kaocha in combination. I've toyed with migrating a few services from Midje over to this setup and have been pleased with the results. matcher-combinators and mockfn are heavily inspired by Midje's feature set and have pretty much achieved feature parity with it.

WhittlesJr commented 5 years ago

@philomates ,

Thank you for the advice! I do use matcher-combinators, it was incredibly useful while I was using Midje and I was very glad to see that I could take it with me to clojure.test.

Thank you especially for pointing me at mockfn, I was worried about losing (provided ...) and I'm glad there's something else that can provide the same functionality.

For now I'm still using Midje in dev for (autotest) alone. I've spent a bit of time playing with test runners and Midje seems to have the nicest autotest capabilities.