Fork me on GitHub
#testing
<
2022-09-21
>
Noah Bogart14:09:32

at my work, we use Component to manage a lot of our stuff, including configuration. some of the configuration is for logging, like choosing the right level for various namespaces, choosing the printing format, etc. we don't create a global instance of the System at some top level, we use a :once fixture to create it per test namespace. I've realized recently that because we're not using that fixture in every test namespace, the ones without it are missing our logging config and so our test output comes out in awkward and verbose formats that we don't want. the solutions that come to mind are 1) go through every test file and add the fixture where missing, 2) change how leiningen runs our tests so it always loads a new "setup-test-stuff" namespace (that calls the config function) even if running a single test var has anyone run into this before? how do you handle creating "global" configuration for tests?

vemv14:09:04

logging is kind of special, it often doesn't need a Component (don't worry if you do have one) I do often create a setup.clj ns with logging config. It can be simply a compile-time side-effect i.e. no need to wrap it in def / defn

👍 1
vemv14:09:02

You can DRY the logging setup between test/setup.clj and whatever else is currently setting it up e.g. make setup.clj call a common function

seancorfield17:09:03

At work we use deps.edn` and Cognitect's test-runner and we've written a small wrapper for that which sets up stuff like this for our entire test suite run. We also use Polylith, which has an additional setup/teardown for "per-project" test suite runs which provides similar functionality (Polylith performs multiple test suites runs, once in each project context that depends on changed code).

Noah Bogart17:09:50

Ah, so you're not just calling test-runner's default, you're calling a specific function that you wrote that calls test-runner's functions. that's clever. does that handle passing args as well, such as select specific tests or namespaces?

seancorfield18:09:18

We call it as an exec fn so we pass everything in a hash map.

👍 1
seancorfield18:09:49

(defn test
  "Invoked via exec-fn (-X) and accepts the same options that Cognitect's
  test-runner uses (and just passes them straight through).

  Prior to invoking Cognitect's test-runner, we preload/start an
  application component if it is on the classpath which will cause
  the test fixtures to not stop the component at the end of each
  test (because they didn't start it), which shaves over a minute
  of the core worldsingles test run (and will improve tests elsewhere).

  Now that we invoke tests in a subprocess, we are safe to just let
  it exit immediately (we do not invoke the test runner manually)."
  [options]
  (try
    ((requiring-resolve 'ws.application-fixtures.interface/pre-test))
    (catch Throwable _))

  (let [{:keys [fail error]} (#'runner/do-test options)]
    (System/exit (if (zero? (+ fail error)) 0 1))))
(Polylith lets us specify that pre-test function is called before each test suite run too)

seancorfield18:09:54

Caveat: our build.clj has an exec-command that is like the java-command in tools.build but is designed on top of exec.jar and a bunch of undocumented stuff in the CLI.

seancorfield18:09:43

You can still do the above and call test-runner's regular -main function from a -main of your own, and use -M -m style invocation instead of -X.

Noah Bogart18:09:43

That's very cool, thank you for the code example