Fork me on GitHub
#leiningen
<
2017-09-29
>
Shantanu Kumar20:09:50

Does anybody here know why does Leiningen report errors like Possibly confusing dependencies found: [org.clojure/clojure "1.8.0"] overrides [org.clojure/clojure "1.8.0"] with :pedantic? :abort?

gonewest81821:09:22

@nwjsmith, what’s the advantage over lein test, and if tests are successful lein uberjar and then ship?

nwjsmith21:09:04

@gonewest818 The advantage is that I guarantee that my tests pass against the production code.

nwjsmith21:09:55

If I build my production code afterwards, there's always a (slight) chance that something in my build process produces a deploy artifact that doesn't match what I tested

gonewest81821:09:45

but running your test jar with your uberjar on the classpath would bypass the normal entry point to the app… isn’t it still possible your test doesn’t match production?

nwjsmith21:09:38

Sorry, I don't understand. Could you describe an example scenario where this would be the case?

gonewest81821:09:56

Sorry. What I mean is, to build an uberjar you need to tell the compiler where the “main” is so that the app can run standalone. In project.clj you say something like :main myapp.foo where src/myapp/foo.clj contains (defn -main [& args] ...) and that’s the function where the app starts.

nwjsmith21:09:08

Oh! I'm not so concerned with testing the execution of my production uberjar, but rather the code which that uberjar runs.

gonewest81821:09:20

So in your test example, you seem to be running a different jar containing test cases java -jar tests.jar -cp uberjar.jar. Which means the app is possibly launching via a different “main” and depending how it sets itself up, that could be different too.

gonewest81821:09:30

sorry, crossed replies with you.

gonewest81821:09:21

Are they “unit” tests, or do the tests depend on the app being integrated together?

gonewest81822:09:40

Either way, I guess what I’m saying is, building the uberjar and invoking tests against code inside that uberjar may not be a perfect replica of production either.

nwjsmith22:09:01

You're 💯, that's certainly a risk. I'm trying to keep as much code out of -main as possible, but no matter what I do the code will be run with a different configuration than in production.

nwjsmith22:09:49

What this does ensure though is that the code I'm unit testing, and any resources, will be guaranteed to be that which runs in production

nwjsmith22:09:06

I got it working!

nwjsmith22:09:49

The invocation ends up looking like java -classpath myapp-standalone.jar:test-myapp-standalone.jar myapp.test_main

nwjsmith22:09:50

and since I'm not using lein test, I ended up with the following test "runner":

(ns myapp.test-main
  (:require [clojure.java.classpath :as classpath]
            [clojure.tools.namespace.find :as find-ns]
            [clojure.test :as test]
            [clojure.test.junit :as junit])
  (:gen-class))

(def ^:private test-ns-pattern
  #"myapp\..+-test")

(defn- run-tests
  []
  (let [namespaces (->> (classpath/classpath)
                        find-ns/find-namespaces
                        (filter (comp (partial re-matches test-ns-pattern)
                                      name)))]
    (apply require namespaces)
    (apply test/run-tests namespaces)))

(defn -main [& arguments]
  (if (= "junit" (first arguments))
    (junit/with-junit-output (run-tests))
    (run-tests)))

gonewest81822:09:36

can you share profile.clj ?

nwjsmith22:09:26

Yep! One minute while I clean it up a bit

nwjsmith22:09:01

(defproject myapp "0.0.1"
  :dependencies [[org.clojure/clojure "1.9.0-beta1"]]
  :profiles {:dev {:dependencies [[org.clojure/java.classpath "0.2.3"]
                                  [org.clojure/test.check "0.10.0-alpha2"]
                                  [org.clojure/tools.namespace "0.3.0-alpha4"]]
                   :source-paths ["dev"]}
             :release {:aot :all
                       :jar-name "myapp.jar"
                       :main myapp.main
                       :uberjar-name "release/myapp-standalone.jar"
                       :source-paths ["src"]
                       :resource-paths ["resources"]
                       :target-path "target/release/%s"}
             :test {:dependencies [[org.clojure/java.classpath "0.2.3"]
                                   [org.clojure/tools.namespace "0.3.0-alpha4"]
                                   [org.clojure/test.check "0.10.0-alpha2"]]
                    :main myapp.test-main
                    :aot [myapp.test-main]
                    :uberjar-name "test/myapp-test-standalone.jar"
                    :source-paths ^:replace ["test"]
                    :resource-paths ^:replace []
                    :target-path "target/%s"}}
  :jvm-opts ^:replace ["-server"])

rickmoynihan10:10:09

Interesting… I had thought about doing something similar, but didn’t feel like it was worth it because our test suite has its own profile with extra dependencies that could potentially cause differences in the system in undertest. I think lein test + just creating an uberjar is good enough, especially if you’re doing integration tests against the uberjar too.

rickmoynihan10:10:24

Also I’ve found just building an uberjar even for a library can be a useful test - as it can catch compilation & syntax issues.

nwjsmith23:09:33

^ this is what my CI looks like now, I build both jars then forward the release jar through the pipeline. The deploy step here is building a small Docker image containing just the release jar.