testing

2022-09-21T14:01:32.965469Z

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?

vemv 2022-09-21T14:13:04.948189Z

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
vemv 2022-09-21T14:14:02.496029Z

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

seancorfield 2022-09-21T17:53:03.989849Z

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).

2022-09-21T17:54:50.885009Z

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?

seancorfield 2022-09-21T18:24:18.305839Z

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

👍 1
seancorfield 2022-09-21T18:25:49.721859Z

(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)

seancorfield 2022-09-21T18:26:54.238889Z

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.

seancorfield 2022-09-21T18:27:43.641049Z

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.

2022-09-21T18:28:43.529979Z

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