Giter VIP home page Giter VIP logo

kaocha's Introduction

Full featured next generation test runner for Clojure.

Jump to Quick start | Docs

Projects

Project CI Docs Release Coverage
kaocha CircleCI cljdoc badge Clojars Project codecov
kaocha-cljs CircleCI cljdoc badge Clojars Project codecov
kaocha-cucumber CircleCI cljdoc badge Clojars Project codecov
kaocha-junit-xml CircleCI cljdoc badge Clojars Project codecov
kaocha-cloverage CircleCI cljdoc badge Clojars Project codecov
kaocha-boot CircleCI cljdoc badge Clojars Project codecov
deep-diff CircleCI cljdoc badge Clojars Project codecov

考察 [kǎo chá]

  • to inspect
  • to observe and study
  • on-the-spot investigation

See the Line Dict entry for an audio sample.

Need help?

Are you

There is also a #kaocha channel on Clojurians Slack (sign up here), where users can help each other.

Docs

Features

Features include

  • Filtering tests based on test names or metadata
  • Watch mode: watch the file system for changes and re-run tests
  • Pretty, pluggable reporting
  • Randomize test order
  • Detect when interrupted with ctrl-C and print report
  • Fail fast mode: stop at first failure and print report
  • Profiling (show slowest tests)
  • Dynamic classpath handling
  • Tests as data (get test config, test plan, or test results as EDN)
  • Extensible test types (clojure.test, Midje, ...)
  • Extensible through plugins
  • Tool agnostic (Clojure CLI, Leiningen, ...)

Quick start

This is no replacement for reading the docs, but if you're particularly impatient to try it out, or if you already know Kaocha and need a quick reference how to set up a new project, then this guide is for you.

Clojure CLI (tools.deps)

Add Kaocha as a dependency, preferably under an alias.

;; deps.edn
{:deps { ,,, }
 :aliases
 {:test {:extra-deps {lambdaisland/kaocha {:mvn/version "1.91.1392"}}
         :main-opts ["-m" "kaocha.runner"]}}}

Add a binstub called bin/kaocha

mkdir -p bin
echo '#!/usr/bin/env sh' > bin/kaocha
echo 'clojure -M:test "$@"' >> bin/kaocha
chmod +x bin/kaocha

If you're on windows and installed clojure into powershell then you can put this into kaocha.ps1 for use with powershell.exe

clojure -M:test "$args"

Or put this in kaocha.bat for use with cmd.exe

powershell -command clojure -M:test "%*"

Or put this in kaocha for use with msys2

powershell -command clojure -M:test "$@"

Leiningen

Add a profile and alias

;; project.clj
(defproject my-proj "0.1.0"
  :dependencies [,,,]
  :profiles {:kaocha {:dependencies [[lambdaisland/kaocha "1.91.1392"]]}}
  :aliases {"kaocha" ["with-profile" "+kaocha" "run" "-m" "kaocha.runner"]})

Add a binstub called bin/kaocha

mkdir -p bin
echo '#!/usr/bin/env sh' > bin/kaocha
echo 'lein kaocha "$@"' >> bin/kaocha
chmod +x bin/kaocha

Boot

In your build.boot add the Kaocha dependency, and import the Kaocha task

;; build.boot
(set-env! :source-paths #{"src"}
          :dependencies '[[lambdaisland/kaocha-boot "..."]])

(require '[kaocha.boot-task :refer [kaocha]])

Add a binstub called bin/kaocha

mkdir -p bin
echo '#!/usr/bin/env sh' > bin/kaocha
echo 'boot kaocha "$@"' >> bin/kaocha
chmod +x bin/kaocha

Clojure CLI (tools.deps) :exec-fn alternative

We also support using the Clojure CLI :exec-fn/-X. However, we recommend the binstub approach above because it allows you to use traditional long and short options. If you nonetheless prefer :exec-fn/-X, you can set up deps.edn:

;; deps.edn
{:deps { ,,, }
 :aliases
 {:test {:extra-deps {lambdaisland/kaocha {:mvn/version "1.91.1392"}}
         :exec-fn kaocha.runner/exec-fn
         :exec-args {}}}}

And then Kaocha can be invoked this way: clojure -X:test

Generally speaking, we recommend using tests.edn for all of your configuration rather than putting it in exec-args unless there's an alternative combination of options you frequently run.

In that case, you can put configuration options :exec-args as though it were tests.edn. Let's say you frequently use watch with :fail-fast and a subset of tests skipped. You could save that configuration with an additional alias: clojure -X:watch-test like so:

;; deps.edn
{:deps { ,,, }
 :aliases
 {:test {:extra-deps {lambdaisland/kaocha {:mvn/version "1.91.1392"}}
         :exec-fn kaocha.runner/exec-fn
         :exec-args {}}
 :watch-test {:extra-deps {lambdaisland/kaocha {:mvn/version "1.91.1392"}}
         :exec-fn kaocha.runner/exec-fn
         :exec-args {:watch? true
	 :skip-meta :slow
	 :fail-fast? true }}}}

If you wanted to turn off fail-fast temporarily, you could run clojure -X:watch-test :fail-fast? false

You can also pass exec-args on the command line like so:

clojure -X:test :print-config true :kaocha.plugin.randomize/seed 603964682

Babashka

Kaocha is compatible with Babashka.

You can create a bb.edn file:

{:paths ["src" "test"]
 :deps {lambdaisland/kaocha {:mvn/version "1.91.1392"}}}

Then you can create a binstub named bin/kaocha-bb:

#!/usr/bin/env bash
bb -m kaocha.runner/-main  $@

If you exclusively want to run tests using Babashka, you can of course call it bin/kaocha.

All tools

By default, Kaocha assumes that:

  • source files are in the src/ folder,
  • test files are in the test/ folder,
  • all test namespaces names end with -test (e.g. my-project.core-test). Also, the default test suite id is :unit (just unit on the command line).

If your tests don't seem to run (outcome is 0 tests, 0 assertions, 0 failures) you may need to write up your own configuration: add a tests.edn at the root of the project to configure actual test and source paths, and optionally set a reporter or load plugins (cf. Configuration in the documentation).

Example of a catch-all tests.edn config file (should run all tests found in src/ and /test, in any namespace).

#kaocha/v1
{:tests [{:id          :unit
          :test-paths  ["test" "src"]
          :ns-patterns [".*"]}]
          ;; :reporter kaocha.report.progress/report
          ;; :plugins [:kaocha.plugin/profiling :kaocha.plugin/notifier]
 }

Warning: this is not an optimal configuration. To avoid extra churn, you should try and target only folders and namespaces that actually contain tests.

Run your tests

bin/kaocha

# Watch for changes
bin/kaocha --watch

# Exit at first failure
bin/kaocha --fail-fast

# Only run the `unit` suite
bin/kaocha unit

# Only run a single test
bin/kaocha --focus my.app.foo-test/bar-test

# Use an alternative config file
bin/kaocha --config-file tests_ci.edn

# See all available options
bin/kaocha --test-help

Third party projects

  • kaocha-noyoda Don't speak like Yoda, write (is (= actual expected)) instead of (is (= expected actual))
  • kaocha-test-ns-hook A Kaocha plugin for the test-ns-hook feature in cloure.test.

Requirements

Kaocha requires Clojure 1.9 or later.

Lambda Island Open Source

Thank you! kaocha is made possible thanks to our generous backers. Become a backer on OpenCollective so that we can continue to make kaocha better.

 

kaocha is part of a growing collection of quality Clojure libraries created and maintained by the fine folks at Gaiwan.

Pay it forward by becoming a backer on our OpenCollective, so that we continue to enjoy a thriving Clojure ecosystem.

You can find an overview of all our different projects at lambdaisland/open-source.

 

 

Contributing

We warmly welcome patches to kaocha. Please keep in mind the following:

  • adhere to the LambdaIsland Clojure Style Guide
  • write patches that solve a problem
  • start by stating the problem, then supply a minimal solution *
  • by contributing you agree to license your contributions as EPL 1.0
  • don't break the contract with downstream consumers **
  • don't break the tests

We would very much appreciate it if you also

  • update the CHANGELOG and README
  • add tests for new functionality

We recommend opening an issue first, before opening a pull request. That way we can make sure we agree what the problem is, and discuss how best to solve it. This is especially true if you add new dependencies, or significantly increase the API surface. In cases like these we need to decide if these changes are in line with the project's goals.

* This goes for features too, a feature needs to solve a problem. State the problem it solves first, only then move on to solving it.

** Projects that have a version that starts with 0. may still see breaking changes, although we also consider the level of community adoption. The more widespread a project is, the less likely we're willing to introduce breakage. See LambdaIsland-flavored Versioning for more info.

License

Copyright © 2018-2023 Arne Brasseur and contributors

Available under the terms of the Eclipse Public License 1.0, see LICENSE.txt

kaocha's People

Contributors

alexchalk avatar alysbrooks avatar ariela147 avatar borkdude avatar cap10morgan avatar cjohansen avatar danielcompton avatar dharrigan avatar emlyn avatar fdserr avatar frenchy64 avatar humorless avatar imrekoszo avatar john-shaffer avatar lread avatar magnars avatar noahtheduke avatar oxalorg avatar plexus avatar rgkirch avatar rickmoynihan avatar robhanlon22 avatar simon-katz avatar socksy avatar solar05 avatar stig avatar the-alchemist avatar thumbnail avatar whittlesjr avatar zengxinhui avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

kaocha's Issues

Use Signal rather than shutdown hooks to detect Ctrl-C

An important feature of Kaocha is that when you interrupt a test run with Ctrl-C, (SIGINT) it will print out the results it has collected so far, so you get an overview of errors and failures with full description/location, and a test summary e.g. 5 tests, 3 pass, 1 error, 1 fail. This is a common (and IMO indispensable) feature of other test runners like RSpec, but AFAIK no other Clojure tool does this.

Currently we use Java shutdown hooks to detect Ctrl-C, but shutdown hooks don't always fire, so at the moment this feature doesn't always work. (source)

Instead we should use Signal handlers directly.(sun.misc.Signal / sun.misc.SignalHandler). Although in theory this limits us to specific JVM implementations, in practice it seems to work in Oracle and OpenJDK, as well as IBM's JVM, which is more than enough for me. Making it work on Windows could be a challenge.

Watch feature should ignore temporary files

It would be good if there was a way to specify patterns of filenames to ignore from the watch feature (e.g. .#* files created by emacs) so that tests are not needlessly reevaluated every time a temporary file changes.

0 test vars, 0 assertions, 0 failures

Hey there,

library looking neat. Kudos.

I'm having trouble getting to run it:

  • My ~/.lein/profiles.clj:
{:user {dependencies [[lambdaisland/kaocha "0.0-189" :exclusions [org.clojure/clojure]]]
...
  • My tests.edn:
{:tests [{:id :unit
          :test-paths ["test"] ; Correct one
          :source-paths ["src" "and-other-stuff"]}] ; Correct values
 }

I/O:

$ lein run -m kaocha.runner unit --fail-fast
Randomized with --seed 798394175
[]
0 test vars, 0 assertions, 0 failures

A number of tests should be run instead. How to debug this?

Cheers - Victor

Suggestion: While watching, re-run all tests on first SIGINT

Sometimes changes are not detected (a git rebase, changes in non-clj files). Sometimes Kaocha is waiting for a single test to turn green, but you would like to run all the tests.

My suggestion is a SIGINT (Ctrl-C) would make Kaocha go:

Re-running all tests. Interrupt a second time to quit.

Then wait for two seconds for a second SIGINT before running all the tests.

I'd be happy to contribute something like this if it sounds reasonable.

Installation instructions not working? replace :deps with :extra-deps

I was trying to get kaocha installed on a brand new project, and I always got:

Error building classpath. nil
java.lang.NullPointerException
	at clojure.core$update.invokeStatic(core.clj:6118)
	at clojure.core$update.invoke(core.clj:6108)
	at clojure.tools.deps.alpha$merge_alias_maps$fn__1002$fn__1004.invoke(alpha.clj:40)
	at clojure.core.protocols$iter_reduce.invokeStatic(protocols.clj:49)
	at clojure.core.protocols$fn__7839.invokeStatic(protocols.clj:75)
	at clojure.core.protocols$fn__7839.invoke(protocols.clj:75)
	at clojure.core.protocols$fn__7781$G__7776__7794.invoke(protocols.clj:13)
	at clojure.core$reduce.invokeStatic(core.clj:6748)
	at clojure.core$reduce.invoke(core.clj:6730)
	at clojure.tools.deps.alpha$merge_alias_maps$fn__1002.invoke(alpha.clj:39)
	at clojure.lang.ArrayChunk.reduce(ArrayChunk.java:58)
	at clojure.core.protocols$fn__7847.invokeStatic(protocols.clj:136)
	at clojure.core.protocols$fn__7847.invoke(protocols.clj:124)
	at clojure.core.protocols$fn__7807$G__7802__7816.invoke(protocols.clj:19)
	at clojure.core.protocols$seq_reduce.invokeStatic(protocols.clj:31)
	at clojure.core.protocols$fn__7833.invokeStatic(protocols.clj:75)
	at clojure.core.protocols$fn__7833.invoke(protocols.clj:75)
	at clojure.core.protocols$fn__7781$G__7776__7794.invoke(protocols.clj:13)
	at clojure.core$reduce.invokeStatic(core.clj:6748)
	at clojure.core$reduce.invoke(core.clj:6730)
	at clojure.tools.deps.alpha$merge_alias_maps.invokeStatic(alpha.clj:38)
	at clojure.tools.deps.alpha$merge_alias_maps.doInvoke(alpha.clj:35)
	at clojure.lang.RestFn.applyTo(RestFn.java:137)
	at clojure.core$apply.invokeStatic(core.clj:657)
	at clojure.core$apply.invoke(core.clj:652)
	at clojure.tools.deps.alpha$combine_aliases.invokeStatic(alpha.clj:59)
	at clojure.tools.deps.alpha$combine_aliases.invoke(alpha.clj:52)
	at clojure.tools.deps.alpha.script.make_classpath$create_classpath.invokeStatic(make_classpath.clj:57)
	at clojure.tools.deps.alpha.script.make_classpath$create_classpath.invoke(make_classpath.clj:52)
	at clojure.tools.deps.alpha.script.make_classpath$run.invokeStatic(make_classpath.clj:70)
	at clojure.tools.deps.alpha.script.make_classpath$run.invoke(make_classpath.clj:64)
	at clojure.tools.deps.alpha.script.make_classpath$_main.invokeStatic(make_classpath.clj:109)
	at clojure.tools.deps.alpha.script.make_classpath$_main.doInvoke(make_classpath.clj:84)
	at clojure.lang.RestFn.applyTo(RestFn.java:137)
	at clojure.lang.Var.applyTo(Var.java:702)
	at clojure.core$apply.invokeStatic(core.clj:657)
	at clojure.main$main_opt.invokeStatic(main.clj:317)
	at clojure.main$main_opt.invoke(main.clj:313)
	at clojure.main$main.invokeStatic(main.clj:424)
	at clojure.main$main.doInvoke(main.clj:387)
	at clojure.lang.RestFn.applyTo(RestFn.java:137)
	at clojure.lang.Var.applyTo(Var.java:702)
	at clojure.main.main(main.java:37)

Swapping my deps.edn to:

{:deps {}
 :aliases
 {:test {:extra-deps {lambdaisland/kaocha {:mvn/version "0.0-176"}}}}}

Seems to be working, Kaocha is downloaded and runs as expected.

Print captured output for successful tests

Kaocha currently captures output and only prints it when tests fail. I like the idea behind this, but the current implementation makes it a little too easy to forget stray prns around tests and implementation code. To me it would be ideal if:

  • Output is captured, but always printed, grouped by name of test
  • When there are both successful and failing tests, output from successful tests are printed before failing ones
  • A command line argument can be used to configure printing for the whole test run: never, only failing, always
  • Metadata can be used to configure printing per namespace/test: never, only failing, always

Unknown option --watch, what am I doing wrong?

I am getting "Unknown option" for --watch.

  • Other options seem to work (I tried --fail-fast and --print-config)
  • Watching works with #kaocha/v1 {:watch? true}
  • The --no-watch is also unknown.

I am using leiningen:

:profiles {:kaocha {:dependencies [[lambdaisland/kaocha "0.0-367"]]}}
:aliases {"kaocha" ["with-profile" "+kaocha" "run" "-m" "kaocha.runner"]}

Executable:

#!/bin/bash

lein kaocha "$@"

Output:

> bin/kaocha --watch
Unknown option: "--watch"
USAGE:

bin/kaocha [OPTIONS]... [TEST-SUITE]...

  -c, --config-file FILE     tests.edn  Config file to read.
      --print-config                    Print out the fully merged and normalized config, then exit.
      --print-test-plan                 Load tests, build up a test plan, then print out the test plan and exit.
      --print-result                    Print the test result map as returned by the Kaocha API.
      --fail-fast                       Stop testing after the first failure.
      --[no-]color                      Enable/disable ANSI color codes in output. Defaults to true.
      --[no-]watch                      Watch filesystem for changes and re-run tests.
      --reporter SYMBOL                 Change the test reporter, can be specified multiple times.
      --plugin KEYWORD                  Load the given plugin.
      --version                         Print version information and quit.
      --help                            Display this help message.
  -H, --test-help                       Display this help message.
      --[no-]randomize                  Run test namespaces and vars in random order.
      --seed SEED                       Provide a seed to determine the random order of tests.
      --skip SYM                        Skip tests with this ID and their children.
      --focus SYM                       Only run this test, skip others.
      --skip-meta SYM                   Skip tests where this metadata key is truthy.
      --focus-meta SYM                  Only run tests where this metadata key is truthy.
      --[no-]capture-output             Capture output during tests.

Options may be repeated multiple times for a logical OR effect.
Error encountered performing task 'run' with profile(s): 'base,system,user,provided,dev,kaocha'
Suppressed exit

Compatibility with fulcro-spec

fulcro-spec is an elaborate testing library that builds on top of clojure.test.

Fulcro-spec has its own organizational structure for tests, and has custom clojure.test events that correspond with them, like :begin-specification or :end-provided. It's intended to be used with its own reporter that knows how to handle these events.

The Issue

When Kaocha sees an event type it doesn't know about, it forwards it to the original clojure.test/report (so ignoring rebinding). If no defmethod's exist for these types then clojure.test/report just prints out the event map, and since the :kaocha/testable is in there this gets really noisy.

Why is it like that

In this case fulcro-spec provides both custom events and a custom reporter, expecting you to rebind clojure.test/report. Other libraries (e.g. assertion libraries) provide custom events but no custom reporter, instead extending the clojure.test/report method to handle these events. Both approaches are within scope of the intended use of clojure.test, but they are not compatible.

So Kaocha rebinds the reporter, but still forwards unknown events to the original reporter, assuming they will be handled on that level.

Possible solutions

As a first step we should check if clojure.test/report has a multimethod implementation for a given event type before forwarding the event. The default behavior of printing the event map is never useful.

Note that this could also be solved presently by adding the fulcro-spec events type to the kaocha hierarchy, making kaocha aware of them and thus preventing the forwarding.

Kaocha reporters and general compatibility

I'll have to try out fulcro-spec to better understand what its reporter is bringing to the table, and how well it plays with other libraries. E.g. I suspect that you can't use matcher-combinators with fulcro-spec's reporter.

Going forward we should make sure fulcro-spec can also work with Kaocha's reporters, since these provide facilities for extension by multiple parties, preventing some of the problems that extending clojure.test/report directly has.

Warn if plugin name in config is not a keyword

Plugin names are assumed to be keywords (it's an identifier for the plugin). However it's easy to mix this up and put in a symbol. We could allow this and coerce, but I'd rather enforce systematic use of keywords. That also has the benefit that symbols are still available for some later extension.

This isn't currently caught by spec I think because we let plugins run their config hooks first before validating the config, but maybe we should really validate twice, once before and once after the config hook.

Watch feature errors out when reloading ns

When I enable watch and save a file, kaocha shows the following stacktrace

java.io.FileNotFoundException: /home/mates/dev/matcher-combinators/test/matcher_combinators/4913 (No such file or directory)
	at java.io.FileInputStream.open0(Native Method)
	at java.io.FileInputStream.open(FileInputStream.java:195)
	at java.io.FileInputStream.<init>(FileInputStream.java:138)
	at clojure.java.io$fn__11162.invokeStatic(io.clj:229)
	at clojure.java.io$fn__11162.invoke(io.clj:229)
	at clojure.java.io$fn__11075$G__11068__11082.invoke(io.clj:69)
	at clojure.java.io$fn__11136.invokeStatic(io.clj:165)
	at clojure.java.io$fn__11136.invoke(io.clj:165)
	at clojure.java.io$fn__11088$G__11064__11095.invoke(io.clj:69)
	at clojure.java.io$reader.invokeStatic(io.clj:102)
	at clojure.java.io$reader.doInvoke(io.clj:86)
	at clojure.lang.RestFn.invoke(RestFn.java:410)
	at clojure.tools.namespace.file$read_file_ns_decl.invokeStatic(file.clj:24)
	at clojure.tools.namespace.file$read_file_ns_decl.invoke(file.clj:17)
	at clojure.tools.namespace.file$read_file_ns_decl.invokeStatic(file.clj:22)
	at clojure.tools.namespace.file$read_file_ns_decl.invoke(file.clj:17)
	at kaocha.watch$reload_file_BANG_.invokeStatic(watch.clj:20)
	at kaocha.watch$reload_file_BANG_.invoke(watch.clj:18)
	at clojure.core$run_BANG_$fn__8544.invoke(core.clj:7660)
	at clojure.core.protocols$iter_reduce.invokeStatic(protocols.clj:49)
	at clojure.core.protocols$fn__7952.invokeStatic(protocols.clj:75)
	at clojure.core.protocols$fn__7952.invoke(protocols.clj:75)
	at clojure.core.protocols$fn__7894$G__7889__7907.invoke(protocols.clj:13)
	at clojure.core$reduce.invokeStatic(core.clj:6773)
	at clojure.core$run_BANG_.invokeStatic(core.clj:7655)
	at clojure.core$run_BANG_.invoke(core.clj:7655)
	at kaocha.watch$run$fn__12098.invoke(watch.clj:53)
	at clojure.core$binding_conveyor_fn$fn__5575.invoke(core.clj:2022)
	at clojure.lang.AFn.call(AFn.java:18)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)

Allow configuring skip-meta / focus-meta at the top level

Currently the skip/focus configuration is part of the test suite. I think at least for the -meta versions we should also allow configuring them at the top level configuration.

This already kind of works, you can use kaocha.filter/focus-meta or kaocha.filter/skip-meta, but it's undocumented, and the shorthand versions only work in the test suite (:focus-meta and :skip-meta).

Also currently if either is specified on the command line then it will overrule what's configured, this is fine for --focus-meta I think, but not for --skip-meta, which should be additive.

Support for pytest xfail concept (variant of skip)

Hello,

I really like how this test runner is taking shape, thanks for making it!

From my experience with tests in many languages, two features are important: skip (already supported) and xfail. Quoting from https://docs.pytest.org/en/latest/skipping.html:

A xfail means that you expect a test to fail for some reason. A common example is a test for a feature not yet implemented, or a bug not yet fixed. When a test passes despite being expected to fail (marked with pytest.mark.xfail), it’s an xpass and will be reported in the test summary.

This is very powerful because when a xfail makes the test suite go red, you discover that you might have fixed a bug without knowing it :-) so you can remove the xfail and keep the test as a normal regression.

thanks!

Occasional opaque errors when using fail-fast

The fail-fast option is generally working for me, but sometimes I get the following:

Randomized with --seed 489190246
[......FException in thread "main" clojure.lang.ExceptionInfo: throw+: #:kaocha{:fail-fast true} #:kaocha{:fail-fast true}, compiling:(/private/var/folders/kx/y0pmr4bs5f3dcg4mvw6bg5nr0000gn/T/form-init5139607091255462395.clj:1:125)
	at clojure.lang.Compiler.load(Compiler.java:7526)
	at clojure.lang.Compiler.loadFile(Compiler.java:7452)
	at clojure.main$load_script.invokeStatic(main.clj:278)
	at clojure.main$init_opt.invokeStatic(main.clj:280)
	at clojure.main$init_opt.invoke(main.clj:280)
	at clojure.main$initialize.invokeStatic(main.clj:311)
	at clojure.main$null_opt.invokeStatic(main.clj:345)
	at clojure.main$null_opt.invoke(main.clj:342)
	at clojure.main$main.invokeStatic(main.clj:424)
	at clojure.main$main.doInvoke(main.clj:387)
	at clojure.lang.RestFn.applyTo(RestFn.java:137)
	at clojure.lang.Var.applyTo(Var.java:702)
	at clojure.main.main(main.java:37)
Caused by: clojure.lang.ExceptionInfo: throw+: #:kaocha{:fail-fast true} {:kaocha/fail-fast true}
	at slingshot.support$stack_trace.invoke(support.clj:201)
	at kaocha.report$fail_fast.invokeStatic(report.clj:251)
	at kaocha.report$fail_fast.invoke(report.clj:245)
	at kaocha.config$resolve_reporter$fn__46035$fn__46036.invoke(config.clj:101)
	at clojure.core$run_BANG_$fn__8431.invoke(core.clj:7635)
	at clojure.lang.ArrayChunk.reduce(ArrayChunk.java:63)
	at clojure.core.protocols$fn__7847.invokeStatic(protocols.clj:136)
	at clojure.core.protocols$fn__7847.invoke(protocols.clj:124)
	at clojure.core.protocols$fn__7807$G__7802__7816.invoke(protocols.clj:19)
	at clojure.core.protocols$seq_reduce.invokeStatic(protocols.clj:31)
	at clojure.core.protocols$fn__7835.invokeStatic(protocols.clj:75)
	at clojure.core.protocols$fn__7835.invoke(protocols.clj:75)
	at clojure.core.protocols$fn__7781$G__7776__7794.invoke(protocols.clj:13)
	at clojure.core$reduce.invokeStatic(core.clj:6748)
	at clojure.core$run_BANG_.invokeStatic(core.clj:7630)
	at clojure.core$run_BANG_.invoke(core.clj:7630)
	at kaocha.config$resolve_reporter$fn__46035.invoke(config.clj:101)
	at kaocha.monkey_patch$eval46196$fn__46197$fn__46198.invoke(monkey_patch.clj:50)
	at kaocha.type.var$eval46955$fn__46957.invoke(var.clj:32)
	at clojure.lang.MultiFn.invoke(MultiFn.java:233)
	at kaocha.testable$run.invokeStatic(testable.clj:91)
	at kaocha.testable$run.invoke(testable.clj:83)
	at kaocha.testable$run_testables.invokeStatic(testable.clj:122)
	at kaocha.testable$run_testables.invoke(testable.clj:112)
	at clojure.lang.Atom.swap(Atom.java:51)
	at clojure.core$swap_BANG_.invokeStatic(core.clj:2345)
	at clojure.core$swap_BANG_.invoke(core.clj:2337)
	at kaocha.type.ns$run_tests$fn__47019.invoke(ns.clj:52)
	at clojure.test$default_fixture.invokeStatic(test.clj:686)
	at clojure.test$default_fixture.invoke(test.clj:682)
	at kaocha.type.ns$run_tests.invokeStatic(ns.clj:52)
	at kaocha.type.ns$run_tests.invoke(ns.clj:48)
	at kaocha.type.ns$eval47023$fn__47024.invoke(ns.clj:70)
	at clojure.lang.MultiFn.invoke(MultiFn.java:233)
	at kaocha.testable$run.invokeStatic(testable.clj:91)
	at kaocha.testable$run.invoke(testable.clj:83)
	at kaocha.testable$run_testables.invokeStatic(testable.clj:122)
	at kaocha.testable$run_testables.invoke(testable.clj:112)
	at kaocha.type.clojure.test$eval47225$fn__47226.invoke(test.clj:16)
	at clojure.lang.MultiFn.invoke(MultiFn.java:233)
	at kaocha.testable$run.invokeStatic(testable.clj:91)
	at kaocha.testable$run.invoke(testable.clj:83)
	at kaocha.testable$run_testables.invokeStatic(testable.clj:122)
	at kaocha.testable$run_testables.invoke(testable.clj:112)
	at clojure.lang.AFn.applyToHelper(AFn.java:156)
	at clojure.lang.AFn.applyTo(AFn.java:144)
	at clojure.core$apply.invokeStatic(core.clj:657)
	at clojure.core$apply.invoke(core.clj:652)
	at kaocha.plugin.capture_output$eval45533$fn__45534$fn__45539$fn__45540$fn__45541.invoke(capture_output.clj:79)
	at clojure.core$with_redefs_fn.invokeStatic(core.clj:7434)
	at clojure.core$with_redefs_fn.invoke(core.clj:7418)
	at kaocha.plugin.capture_output$eval45533$fn__45534$fn__45539$fn__45540.doInvoke(capture_output.clj:79)
	at clojure.lang.RestFn.invoke(RestFn.java:421)
	at kaocha.api$run$fn__46263.invoke(api.clj:68)
	at clojure.core$with_redefs_fn.invokeStatic(core.clj:7434)
	at clojure.core$with_redefs_fn.invoke(core.clj:7418)
	at kaocha.api$run.invokeStatic(api.clj:59)
	at kaocha.api$run.invoke(api.clj:46)
	at kaocha.runner$_main_STAR_.invokeStatic(runner.clj:130)
	at kaocha.runner$_main_STAR_.doInvoke(runner.clj:72)
	at clojure.lang.RestFn.applyTo(RestFn.java:137)
	at clojure.core$apply.invokeStatic(core.clj:657)
	at clojure.core$apply.invoke(core.clj:652)
	at kaocha.runner$_main.invokeStatic(runner.clj:139)
	at kaocha.runner$_main.doInvoke(runner.clj:137)
	at clojure.lang.RestFn.invoke(RestFn.java:421)
	at clojure.lang.Var.invoke(Var.java:385)
	at user$eval43604.invokeStatic(form-init5139607091255462395.clj:1)
	at user$eval43604.invoke(form-init5139607091255462395.clj:1)
	at clojure.lang.Compiler.eval(Compiler.java:7062)
	at clojure.lang.Compiler.eval(Compiler.java:7052)
	at clojure.lang.Compiler.load(Compiler.java:7514)

Note that there are no project-specific ns's at all in the stacktrace.

Hope the report is useful!

tests.edn location + optional?

I always try to keep a projects root directory as clean as possible in order to make it easy to get an overview. For that it would be nice if tests.edn could be placed elsewhere as well — maybe at the root of the classpath (e.g. in resources)?

Also I'm fairly happy with the "zero-config" #kaocha {}, in this case creating the tests.edn file seems unnecessary?

Support for Fudje

Fudje is an alternative to Midje (#24), providing similar syntax, but based on clojure.test.

This means that at least in theory Kaocha is fully compatible with Fudje, just use the :kaocha.type/clojure.test type for your test suite.

That said I haven't actually tried it. Fudje defines some custom assertions, and so this ticket is mainly about checking that the various failures it might report are presented nicely by Kaocha's reporters.

Default ns-pattern "-test$" is confusing

Following README, it’s not clear why after everything is set up no tests have been found. Maybe change default ns-pattern value to ".*", with option to rewrite? Otherwise you might just get stuck, with no idea why it’s not working and how to continue. I spent a great deal of time figuring if it’s a classpath problem or what

Fail fast if clojure version < 1.9

We need 1.9 because of spec, we should find some way to communicate that immediately to the user, rather than erroring out on some failed require statement.

Same for Kaocha-cljs, which relies on ClojureScript 1.10 (and thus Clojure 1.10).

Support for expectations

Expectations is a minimalist testing framework built around the expect macro. (source)

(expect 1 1)
(expect #"foo" "boofooar")
(expect ArithmeticException (/ 12 0))
(expect :foo (in #{:foo :bar}))

Note that expectations has two "modes", a newer one based on clojure.test (defexpect ~= deftest), and the original one, which is completely seperate from clojure.test. The former one should work just fine with Kaocha's :kaocha.type/clojure.test, the latter is what this ticket is about.

Since expect just defines a var with metadata ^{:expectation true}, implementing the load step should be straightforward and largely mimic the clojure.test ns/var approach. To implement the run step we'll have to figure out how to capture pass/fail/error events from expectations and convert them into clojure.test/do-report style events.

A request for tips and tricks for extending kaocha

Hi! I am about to start work on my first kaocha plugin. When sitting down I found myself at a bit of a loss - wondering how to tackle the issue.

Do you have a preferred workflow when adding a new plugin? How do you test it in the repl? What's a good way to get a quick feedback cycle? Do you write tests for the plugin with kaocha? How do you avoid the plugin itself interfering with its tests?

I would be happy for just a few pointers in the right direction. Don't feel obligated to write an essay on the topic. :-)

Fails if optional argument of (is..) is nil

Apologies if I miss details as I am typing from a phone as I cannot access github from work.

Using clojure.test and trying out kaocha for the first time.

The ‘is’ function takes an optional 2nd argument to document the test. Whether it is idiomatic or not, I have a few clojure.spec based tests that look like (is (s/valid?...) (s/explain-str...)), which will print out a useful error message if a validation fails and nil otherwise.

This works with ‘lein test’ and the ‘run-tests’ function in the REPL, but kaocha throws its own spec error and fails.

The expected behavior would be to not fail.

I think you can reproduce this just by passing a nil second argument directly.

Thanks for your work!

Suggest making kaocha deps "provided" in leiningen based plugins to play nice with cljdoc

I had to make the kaocha deps "provided" to make a lein based plugin project play nice with cljdoc. For example:
https://github.com/borkdude/speculative-kaocha-plugin/blob/master/project.clj#L10

cljdoc includes the main deps + the test deps during analysis, but not the deps in the kaocha profile, so only putting them there will result in an analysis error on cljcdoc.

I think it would be helpful if this suggestion was made in the README of kaocha.

Add the notifier plugin to the main code base

Ryan McCuaig (@rgm) wrote a hook to call the system notifier when a test run passes or fails. I cleaned it up a bit to use some helper functions from kaocha.result, and coverted it into a plugin:

https://gist.github.com/plexus/e5befecc5b3f950d67944dd63401c580

Ryan agreed to add this to the main code base, which I think would be great, as this is generally quite useful, and doesn't require pulling in extra dependencies.

https://clojurians-log.clojureverse.org/kaocha/2019-01-19

Todo

  • Copy this over to src/kaocha/plugin/notifier.clj, and update the namespace and plugin name accordingly
  • Make the command configurable (current logic to find a command can still be used as a default, set in a config hook)
  • Add a feature test so that it becomes part of the documentation

Configuration:

(tip: have a look at the shellwords function in kaocha_integration.clj and maybe pull it out somewhere, as it'll be useful to parse the configured command)

#kaocha/v1
{:plugins [:kaocha.plugin/notifications]
 :kaocha.plugin.notifications/command "my-notifier %{title} %{message}"}

Enable a default test suite or suites in the configuration file

By default, running bin/kaocha runs all test suites. This is undesirable when some test suites are expensive or require specific environments, etc. It would be great to be able to configure which test suite or suites should run as the default when no test suite name is passed.

Leiningen does this via the :test-selectors key in project.clj.

Warn if `--focus` does not exist

When focusing on (or skipping) a test id that doesn't exist, it filters out all tests (or none). Instead it should provide a warning, even better would be if it would suggest test ids that look similar.

Cucumber and Clojurescript

Hey there,

First off, I really love what you are doing here with this library 😊. I particularly like how it bridges Clojure and Clojure(script) testing into one framework. With that being said, have you considered creating a Clojure(script) implementation of Cucumber? This seems to be a pain point for the Clojure(script) community especially with those utilizing Reagent and Re-frame.

Support for test-ns-hook

clojure.test has this feature where you can define a function named test-ns-hook, and instead of running the test vars in the current namespace, it will run that function.

My impression is that this is mostly used if you want to run your test vars in a very specific order, which in turn is something that's not often needed (and generally a smell).

Kaocha currently does not support this. The problem with it is that it makes things opaque, we lose all the rich information in the test plan regarding test vars, so e.g. filtering will not work.

The easiest way to make this work is to treat the test-ns-hook var as a single test var, and ignore any other test vars in that namespace. This should work, but it may produce sub-optimal results. e.g. say you have this code

(deftest aaa)
(deftest bbb)

(defn test-ns-hook []
  (aaa)
  (bbb))

These calls to (aaa) and (bbb) go through clojure.test/test-var, rather than through Kaocha's testable machinery, meaning events will not have the right :kaocha/testable associated with them. (this in a way makes sense, because there will be no testable for aaa or bbb, since they are ignored in favor of test-ns-hook).

There are probably (hacky) ways around this, but given how little this seems to be used I intend to start with the simple approach, so at least we have some (albeit suboptimal) support for this.

Watch mode improvements

There are several things regarding --watch mode that could be improved.

  • A configurable include/exclude mechanism, seeded from .gitignore #16
  • Watch changes to tests.edn (related to 1., but also requires using a new test config)
  • Drop core.async. It's the only place in the code where it's used, and a simple Java blocking queue would work just as well. This would help improve start up time.
  • Don't build when a source namespace has load errors. Kaocha turns errors during the load step into test failures, so that one broken test ns doesn't stop the build from running, but errors in source namespaces should be treated as a broken precondition and fixed first.

Filter out all kaocha-related stacktrace elements by default

When test blows up with unexpected exception:

ERROR in datascript.test.upsert/test-redefining-ids (db.cljc:1083)
Uncaught exception, not in assertion.
Exception: clojure.lang.ExceptionInfo: Conflicting upsert: -3 resolves both to 1 and 2
{:error :transact/upsert}
 at datascript.db$retry_with_tempid.invokeStatic (db.cljc:1083)
    datascript.db$retry_with_tempid.invoke (db.cljc:1081)
    datascript.db$transact_tx_data.invokeStatic (db.cljc:1142)
    datascript.db$transact_tx_data.invoke (db.cljc:1103)
    datascript.db$retry_with_tempid.invokeStatic (db.cljc:1091)
    datascript.db$retry_with_tempid.invoke (db.cljc:1081)
    datascript.db$transact_tx_data.invokeStatic (db.cljc:1142)
    datascript.db$transact_tx_data.invoke (db.cljc:1103)
    datascript.core$with.invokeStatic (core.cljc:229)
    datascript.core$with.invoke (core.cljc:222)
    datascript.core$with.invokeStatic (core.cljc:224)
    datascript.core$with.invoke (core.cljc:222)
    datascript.core$db_with.invokeStatic (core.cljc:241)
    datascript.core$db_with.invoke (core.cljc:237)
    datascript.test.upsert$fn__11538.invokeStatic (upsert.cljc:149)
    datascript.test.upsert/fn (upsert.cljc:138)
    kaocha.plugin.capture_output$eval2423$fn__2424$fn__2437$fn__2439$fn__2440.invoke (capture_output.clj:92)
    kaocha.type.var$eval3337$fn__3339$fn__3344.invoke (var.clj:33)
    kaocha.type.var$eval3337$fn__3339.invoke (var.clj:30)
    ...
    kaocha.plugin.capture_output$eval2423$fn__2424$fn__2429$fn__2430$fn__2431.invoke (capture_output.clj:83)
    ...
    kaocha.plugin.capture_output$eval2423$fn__2424$fn__2429$fn__2430.doInvoke (capture_output.clj:83)
    ...
    kaocha.testable$run.invokeStatic (testable.clj:108)
    kaocha.testable$run.invoke (testable.clj:99)
    ...
    orchestra.spec.test$spec_checking_fn$fn__3103.doInvoke (test.clj:125)
    ...
    kaocha.testable$run_testable.invokeStatic (testable.clj:158)
    kaocha.testable$run_testable.invoke (testable.clj:137)
    kaocha.testable$run_testables.invokeStatic (testable.clj:167)
    kaocha.testable$run_testables.invoke (testable.clj:161)
    ...
    kaocha.type.ns$run_tests$fn__3403.invoke (ns.clj:57)
    ...
    kaocha.type.ns$run_tests.invokeStatic (ns.clj:57)
    kaocha.type.ns$run_tests.invoke (ns.clj:53)
    kaocha.type.ns$eval3407$fn__3408.invoke (ns.clj:75)
    ...
    kaocha.plugin.capture_output$eval2423$fn__2424$fn__2429$fn__2430$fn__2431.invoke (capture_output.clj:83)
    ...
    kaocha.plugin.capture_output$eval2423$fn__2424$fn__2429$fn__2430.doInvoke (capture_output.clj:83)
    ...
    kaocha.testable$run.invokeStatic (testable.clj:108)
    kaocha.testable$run.invoke (testable.clj:99)
    ...
    orchestra.spec.test$spec_checking_fn$fn__3103.doInvoke (test.clj:125)
    ...
    kaocha.testable$run_testable.invokeStatic (testable.clj:158)
    kaocha.testable$run_testable.invoke (testable.clj:137)
    kaocha.testable$run_testables.invokeStatic (testable.clj:167)
    kaocha.testable$run_testables.invoke (testable.clj:161)
    kaocha.type.clojure.test$eval3958$fn__3959.invoke (test.clj:17)
    ...
    kaocha.plugin.capture_output$eval2423$fn__2424$fn__2429$fn__2430$fn__2431.invoke (capture_output.clj:83)
    ...
    kaocha.plugin.capture_output$eval2423$fn__2424$fn__2429$fn__2430.doInvoke (capture_output.clj:83)
    ...
    kaocha.testable$run.invokeStatic (testable.clj:108)
    kaocha.testable$run.invoke (testable.clj:99)
    ...
    orchestra.spec.test$spec_checking_fn$fn__3103.doInvoke (test.clj:125)
    ...
    kaocha.testable$run_testable.invokeStatic (testable.clj:158)
    kaocha.testable$run_testable.invoke (testable.clj:137)
    kaocha.testable$run_testables.invokeStatic (testable.clj:167)
    kaocha.testable$run_testables.invoke (testable.clj:161)
    kaocha.api$run$fn__3051.invoke (api.clj:95)
    ...
    kaocha.api$run.invokeStatic (api.clj:87)
    kaocha.api$run.invoke (api.clj:70)
    kaocha.runner$run.invokeStatic (runner.clj:117)
    kaocha.runner$run.invoke (runner.clj:68)
    kaocha.runner$_main_STAR_.invokeStatic (runner.clj:135)
    kaocha.runner$_main_STAR_.doInvoke (runner.clj:121)
    ...
    kaocha.runner$_main.invokeStatic (runner.clj:146)
    kaocha.runner$_main.doInvoke (runner.clj:144)
    ...
    clojure.main$main_opt.invokeStatic (main.clj:491)
    clojure.main$main_opt.invoke (main.clj:487)
    clojure.main$main.invokeStatic (main.clj:598)
    clojure.main$main.doInvoke (main.clj:561)
    ...
    clojure.main.main (main.java:37)

Here anything starting with kaocha., clojure.main and orchestra. is not informative and actually obscures interesting parts of test output. Maybe filter them out by default?

Improvement suggestion: Warning against using `is` with non-booleans

It is currently quite easy to misspell (is (= "string1" "string2")) as (is "string1" "string2") because of (is form msg) (where form can be any value) - see #64.

Maybe Kaocha could issue a warning when calling is with more than one argument and the first is a string? It's technically valid, but very likely a bug, and the warning could be silenced with (is (boolean form) msg).

Convert old feature tests to Cucumber

Doing integration testing of Kaocha within the same process/JVM is tricky, so for testing e.g. command line arguments it spins up a new process. I've gone through a few different approaches to how to write these tests. I first used regular clojure.test but was far from happy with the result. The tests themselves were messy (long multiline strings, annoying string quoting issues), and the failures weren't very usable (this huge blob is different from this huge blob, go figure it out).

The second approach was to write each test as a directory with tests.edn and args (i.e. list of command line options to pass to kaocha), and to then check what appears on stdout/stderr as well as the exit code against out, err and exit files in the same directory. A simple shell script would run these "features", using diff to check the results.

This worked actually surprisingly well, and I was quite happy with the result, but this broke down once I started testing against multiple versions of Java/Clojure, since e.g. stacktraces would be different across versions. It also suffers from the classic problem of overspecification. I don't care about each byte of output, I care about the bits that this current test is verifying.

So... third time's a charm, I'm converting the feature tests to Cucumber. I somewhat surprise myself, as I've been quite sceptical of cucumber in the past, but I think it's the right tool in this case, and I'm excited about the prospect of having these tests double as documentation.

Kaocha not finding all tests under catch-all config

It seems Kaocha doesn't pick up the configuration.
Also tried with other suite names than unit.

Catch-all test.edn

#kaocha/v1
{:tests [{:id          :unit
          :test-paths  ["test" "src"]
          :ns-patterns [".*"]}]}

Reproduce

$ chmod +x bin/kaocha && PATH=$PWD/bin:$PATH
$ kaocha unit
[(.)]
1 tests, 1 assertions, 0 failures.

Should be 3 tests

REPL

See dev/repl_session.clj

$ clj -Akaocha:dev

;;; clj tests
(require '[clojure.test :as t])
(require '[ut.core :as ut]
         'ut.core-test
         'ut.any-name
         :reload)
(t/run-all-tests)
; Ran 3 tests containing 3 assertions.
; 0 failures, 0 errors.
; {:test 3, :pass 3, :fail 0, :error 0, :type :summary}

;;; Kaocha tests
(require '[kaocha.repl :as k])
(k/run :unit)
; [(.)]
; 1 tests, 1 assertions, 0 failures.
; #:kaocha.result{:count 1, :pass 1, :error 0, :fail 0, :pending 0}

;;; NS patterns
(re-matches #".*" "ut.core")
;  "ut.core"

;;; Kaocha diagnose
(k/config)
(pp)
; {:kaocha.plugin.randomize/seed 1347114776,
;  :kaocha.plugin.randomize/randomize? true,
;  :kaocha/reporter [kaocha.report/dots],
;  :kaocha/color? true,
;  :kaocha/fail-fast? false,
;  :kaocha/plugins
;  [:kaocha.plugin/randomize
;   :kaocha.plugin/filter
;   :kaocha.plugin/capture-output],
;  :kaocha/tests
;  [{:kaocha.testable/type :kaocha.type/clojure.test,
;    :kaocha.testable/id :unit,
;    :kaocha/ns-patterns ["-test$"],
;    :kaocha/source-paths ["src"],
;    :kaocha/test-paths ["test"],
;    :kaocha.filter/skip-meta [:kaocha/skip]}],
;  :kaocha.plugin.capture-output/capture-output? true}

Better error when plugin not found

Currently when a plugin fails to load it results in a "no matching method for multimethod -register", this should really have a more descriptive error.

Support running clojure.spec.test.alpha/check

It might be good to consider how to support generative testing suites through clojure.spec and a kaocha wrapper over the clojure.spec.test.alpha/check runner (or possibly by providing an alternative implementation).

Here's a proposal as to how this might work:

Firstly there are a few behaviours of check that have implications that will need to be considered.

  1. Calling the 0-arity (check) finds all loaded fdef specs and executes them. The difficult thing here is that the fdefs and specs need to be loaded already for them to execute. So we'd need to provide a mechanism to do this. fdefs are named by the vars of the functions they check, so there is no way to provide a naming convention feature to find them.

The easiest approach here is probably just to let the user provide a set of namespaces to load before we trigger check to locate and test the specs, through something like :kaocha/ns-patterns "myapp.*". Spec "tests" will likely need their own :kaocha.testable/type (I'd suggest :kaocha.type/clojure.spec.test.alpha.check to allow for alternative runners in the future). It's also worth bearing in mind there may not be a need for the equivalent of :kaocha/test-paths as specs commonly exist in the application namespaces.

  1. The 1-arity (check sym-or-syms) allows you to provide either a single symbol representing an fdef to check, or a set of symbols to check. This should be configurable via the tests.edn and command line.

  2. (check sym-or-syms opts) takes the symbol/set as with the 1-arity but also takes a map of test.check/quick-check options and :gen overrides. These should both be configurable in the config, however only the quick-check options should be settable via the command line (it doesn't make much sense to supply :gens via the commandline). In particular these include: the :seed option which can likely be set to the same seed kaocha uses, and :max-size which is required for constraining the size of inputs and is very important to set if you want performant tests. Additionally it is important that users can set specs additional :num-tests property here which specifies the number of random tests to generate for each fdef.

I think it may also be useful for users to be able to orchestrate a set of check tests where multiple check calls can be made with different settings for each of the above options within a single suite (as with :core-specs suite below:

#kaocha/v1
{:tests     ;; [1]
    [{:id    :core-specs
              :type :kaocha.type/clojure.spec.test.alpha.check
              :kaocha/ns-patterns "myapp.*"
              :source-paths ["src"]
              :checks [{;; :syms #{myapp.foo/bar myapp.foo/baz}  ;; omitting :syms means find all fdefs
                              :clojure.spec.test.check {:num-tests 100 :max-size 20 }
                              {:syms #{myapp.flib/flip myapp.flib/flop} 
                              :clojure.spec.test.check {:num-tests 10 :max-size 10 }
      {:id :slow-specs
            :type :kaocha.type/clojure.spec.test.alpha.check
            :kaocha/ns-patterns "myapp.*"
            :source-paths ["src"]
            :clojure.spec.test.check {:num-tests 1000 :max-size 50 }
            :checks [{:syms :all-fdefs}]]}]}

Note here that options can be specified at multiple levels, with the inner in :checks overriding those provided in the wrapping layer. I think a reason to do this is to avoid complecting the suite with the sizes you need to supply to a test etc. Some specs are much more sensitive to sizing parameters than others, and may for example not terminate without large heap settings.

I think any sizing/num-tests options provided at the command line though would override everything within the config and apply to the whole suite.

Finally we would need to consider reporting. check emits a lazy sequence of reports containing the following information

:spec       the spec tested
:sym        optional symbol naming the var tested
:failure    optional test failure
::stc/ret   optional value returned by test.check/quick-check

These would then need to be summarised and printed in human readable form. Additionally if any errors/exceptions occur we should report them nicely with context. We should parse an ::s/failure data if it's present and provide a human readable explanation of the problem:

The value for :failure can be any exception. Exceptions thrown by
spec itself will have an ::s/failure value in ex-data:

:check-failed   at least one checked return did not conform
:no-args-spec   no :args spec provided
:no-fn          no fn provided
:no-fspec       no fspec provided
:no-gen         unable to generate :args
:instrument     invalid args detected by instrument

Boot support

I'd like to add Boot support soon, so all three major Clojure dependency/build tools are supported. This should be fairly straightforward as Kaocha has an easily consumable top level API. The biggest challenge will be to handle command line options.

Boot's deftask macro has a declarative syntax for specifying command line options, but Kaocha determines which command line arguments it recognizes at runtime, because plugins can add extra command line options. E.g. if you run bin/kaocha --plugin cloverage --help you'll see a much longer list than when you just run bin/kaocha.

The process is roughly like this

  • Run tools.cli/parse-opts using the built-in list of recognized flags/options
  • Look for any --config-file or --plugin arguments
  • Load the given config-file (or ./tests.edn)
  • Load all plugins specified in the config file or on the command line
  • Run the cli-options hook of any plugin that implements it, this yields the final list (in tools.cli format) of all recognized arguments
  • Run tools.cli/parse-opts again

I'm really not sure if something similar will be possible in Boot. If it is it would probably mean not using deftask, but instead using lower level APIs / creative metaprogramming.

An alternative would be to "encode" additional options, e.g.

boot kaocha --plugin foo --config-file bar --extra-opts '--plugin-opt-1 --plugin-opt-2'

But perhaps then the more general approach would be to allow specifying additional configuration keys as a clojure map. Well behaved plugins provide both CLI options and tests.edn config keys, so this should allow you to do all the same things.

boot kaocha --extra-config '{:my.plugin/foo 123}'

Support for Midje tests

Midje is probably the most extensive and most opinionated alternative to clojure.test. It provides both a completely new syntax for defining tests, and a very feature-complete runner for said tests.

Midje lives in a world of its own. Its source is unlike any Clojure code I've ever seen, and I've found getting my head around its scope and use not easy.

Nevertheless I think Midje support in Kaocha is important. I think that people who have chosen for Midje are generally quite happy with it once they get past the learning curve, because it does have some very nice features, but that they can also get frustrated because they have isolated themselves from the rest of the ecosystem. Kaocha could provide a link back to a wider world of Clojure testing.

I have an existing proof of concept for a kaocha-midje integration, although I admit it was created with much trial and error, and only a very limited understanding of midje's scope and internals. Still making that code work with the latest kaocha could be a good first step.

Midje test counts aren't shown

When you run tests via kaocha, unit tests are correctly counted in the summary at the end, but midje tests are not.

for instance, using the midje test runner on a project with both midje and clojure.test tests, you get the report:

>>> Midje summary:
All checks (219) succeeded.

>>> Output from clojure.test tests:

Ran 1 tests containing 6 assertions.
0 failures, 0 errors.

But using the kaocha runner on the same test suite results in

--- :unit ---------------------------
matcher-combinators.helpers-test

matcher-combinators.parser-test

matcher-combinators.printer-test

matcher-combinators.core-test

matcher-combinators.midje-test

matcher-combinators.matchers-test

matcher-combinators.test-test
  basic-matching

1 test vars, 6 assertions, 0 failures.

I stumbled upon https://github.com/plexus/kaocha-midje-test, and glancing at it, it looks like you are on the road to implementing this.

I didn't look to closely at your approach, but I wanted to offer that if there are any changes on midje that will make integrating with test runners such as kaocha easier, I will happily see if I can help in implementing them

Figured I'd open this so that midje users interested in using kaocha will know this is a current limitation.

Suggestion: Run all tests if no tests match --focus-meta

I like to run all the tests in a terminal with --watch. Sometimes I'd like to focus on just one namespace or a few tests, while I work on them. Afterwards I go back to running all the tests again.

The way --focus-meta works now, I have to restart Kaocha twice to do this. How about running all the tests when there are no matching tests? It might have to be opt-in, or come with a warning, if you are concerned about confusion.

Cold start performance

Kaocha looks extremely promising, but the time it takes to start Kaocha is significant. On a barebones test file, with no other dependencies, using the latest Clojure CLI run script, it takes an average of about 7 ±0.3 seconds to load and run on a 2018 MacBook Pro. Cognitect's test runner, on the other hand, completes in a fair consistent time of 1.7 seconds.

While Kaocha is clearly doing a lot more, four times the startup cost is significant, and there's a lot of stuff in kaocha.runner that's loaded in without any checks to see if it's necessary. For instance, kaocha.watch and all its associated dependencies are loaded, whether or not the --watch option is used. Similarly loading kaocha.config could be omitted if no configuration files exist. I suspect you could improve cold start times significantly just by avoiding loading namespaces you don't need up front.

Issue with nil values in new default diff reporter

 (is (= {:a nil} {:a {:b 1}}))

results into:

ERROR: Error in reporter: class java.lang.NullPointerException when processing :summary
java.lang.NullPointerException: null
 at clojure.lang.Reflector.invokeNoArgInstanceMember (Reflector.java:301)
    kaocha.report.printer$puget_printer$fn__4434.invoke (printer.clj:89)
    puget.printer$format_doc_STAR_.invokeStatic (printer.clj:192)
    puget.printer$format_doc_STAR_.invoke (printer.clj:188)
    puget.printer$format_doc.invokeStatic (printer.clj:203)
    puget.printer$format_doc.invoke (printer.clj:198)
    kaocha.report.printer$fn__4432.invokeStatic (printer.clj:75)
    kaocha.report.printer/fn (printer.clj:54)
    puget.printer$format_doc_STAR_.invokeStatic (printer.clj:194)
    puget.printer$format_doc_STAR_.invoke (printer.clj:188)
    puget.printer$format_doc.invokeStatic (printer.clj:203)
    puget.printer$format_doc.invoke (printer.clj:198)
    ...
    fipp.engine$serialize.invokeStatic (engine.cljc:15)
    fipp.engine$serialize.invoke (engine.cljc:12)
    ...
    fipp.engine$serialize.invokeStatic (engine.cljc:15)
    fipp.engine$serialize.invoke (engine.cljc:12)
    fipp.engine$eval3218$fn__3220.invoke (engine.cljc:63)
    ...
    fipp.engine$serialize.invokeStatic (engine.cljc:18)
    fipp.engine$serialize.invoke (engine.cljc:12)
    ...
    fipp.engine$serialize.invokeStatic (engine.cljc:15)
    fipp.engine$serialize.invoke (engine.cljc:12)
    fipp.engine$eval3199$fn__3201.invoke (engine.cljc:48)
    ...
    fipp.engine$serialize.invokeStatic (engine.cljc:18)
    fipp.engine$serialize.invoke (engine.cljc:12)
    ...
    fipp.engine$serialize.invokeStatic (engine.cljc:15)
    fipp.engine$serialize.invoke (engine.cljc:12)
    fipp.engine$eval3207$fn__3209.invoke (engine.cljc:55)
    ...
    fipp.engine$serialize.invokeStatic (engine.cljc:18)
    fipp.engine$serialize.invoke (engine.cljc:12)
    ...
    fipp.engine$serialize.invokeStatic (engine.cljc:15)
    fipp.engine$serialize.invoke (engine.cljc:12)
    fipp.engine$eval3177$fn__3179.invoke (engine.cljc:35)
    ...
    fipp.engine$serialize.invokeStatic (engine.cljc:18)
    fipp.engine$serialize.invoke (engine.cljc:12)
    fipp.engine$pprint_document.invokeStatic (engine.cljc:236)
    fipp.engine$pprint_document.invoke (engine.cljc:234)
    kaocha.report.printer$print_doc.invokeStatic (printer.clj:95)
    kaocha.report.printer$print_doc.invoke (printer.clj:94)
    kaocha.report$eval4539$fn__4540.invoke (report.clj:220)
    ...
    kaocha.report$eval4564$fn__4566.invoke (report.clj:239)
    ...
    kaocha.report$eval4579$fn__4580$fn__4588.invoke (report.clj:261)
    kaocha.report$eval4579$fn__4580.invoke (report.clj:259)
    ...
    kaocha.config$resolve_reporter$fn__4741$fn__4742.invoke (config.clj:116)
    ...
    kaocha.config$resolve_reporter$fn__4741.invoke (config.clj:116)
    kaocha.config$resolve_reporter$fn__4741$fn__4742.invoke (config.clj:116)
    ...
    kaocha.config$resolve_reporter$fn__4741.invoke (config.clj:116)
    kaocha.api$resolve_reporter$fn__4982.invoke (api.clj:49)
    ...
    kaocha.api$run$fn__4985.invoke (api.clj:88)
    ...
    kaocha.api$run.invokeStatic (api.clj:74)
    kaocha.api$run.invoke (api.clj:60)
    kaocha.watch$try_run$fn__13863.invoke (watch.clj:21)
    kaocha.watch$try_run.invokeStatic (watch.clj:20)
    kaocha.watch$try_run.invoke (watch.clj:19)
    kaocha.watch$run$fn__13870.invoke (watch.clj:62)
    ...
    java.util.concurrent.FutureTask.run (FutureTask.java:266)
    java.util.concurrent.ThreadPoolExecutor.runWorker (ThreadPoolExecutor.java:1149)
    java.util.concurrent.ThreadPoolExecutor$Worker.run (ThreadPoolExecutor.java:624)
    ...

Add validation to defplugin

It would be helpful if defplugin would complain if you used a hook which doesn't exist, or if the associated function has the wrong arity.

Don't print unrelated output with failing test

Currently Kaocha captures output and only prints it in case of failing tests. However, all the output is printed in one block, making it very hard to discern what output originated from which test. It would be nice if captured output was printed back grouped by the test that generated the output.

Make a distinction between load errors in the test file being loaded vs. in other namespaces.

If an exception occurs while Kaocha loads a test namespace, then a warning is printed, the :kaocha.type/namespace test is marked as failed, and loading/running continues.

This is so that when you have a typo which prevents a test namespace from loading, this doesn't stop the world. Instead it just runs the rest of the tests, and reports a failure pointing at the syntax error.

The problem with this approach is that loading a namespace could fail because a dependent namespace has issues. In the degenerate case a source namespace that is used indirectly in every test fails to load, causing a failure to be reported for every test namespace. This is especially easy to do (and annoying to encounter) in --watch mode.

Instead when loading a test namespace we should first evaluate its ns form. If that causes issues then we hard abort the test run. Only after the ns form has loaded do we load the full namespace, and turn load errors into test failures.

Cannot Locate Kaocha/Runner

I'm looking to see about introducing Kaocha/Cucumber/Selenium to my workspace as my new development team writes in Clojure. I would like to see homogenizing the languages we use for testing (currently Ruby/Cucumber/Watir) for this team as they are not very familiar with Ruby and I would be the only individual maintaining it. Unfortunately, it appears that the way in which I installed kaocha could have been problematic. I am receiving the following stack trace:

Exception in thread "main" java.io.FileNotFoundException: Could not locate kaocha/runner__init.class, kaocha/runner.clj or kaocha/runner.cljc on classpath. at clojure.lang.RT.load(RT.java:466) at clojure.lang.RT.load(RT.java:428) at clojure.core$load$fn__6824.invoke(core.clj:6126) at clojure.core$load.invokeStatic(core.clj:6125) at clojure.core$load.doInvoke(core.clj:6109) at clojure.lang.RestFn.invoke(RestFn.java:408) at clojure.core$load_one.invokeStatic(core.clj:5908) at clojure.core$load_one.invoke(core.clj:5903) at clojure.core$load_lib$fn__6765.invoke(core.clj:5948) at clojure.core$load_lib.invokeStatic(core.clj:5947) at clojure.core$load_lib.doInvoke(core.clj:5928) at clojure.lang.RestFn.applyTo(RestFn.java:142) at clojure.core$apply.invokeStatic(core.clj:667) at clojure.core$load_libs.invokeStatic(core.clj:5985) at clojure.core$load_libs.doInvoke(core.clj:5969) at clojure.lang.RestFn.applyTo(RestFn.java:137) at clojure.core$apply.invokeStatic(core.clj:667) at clojure.core$require.invokeStatic(core.clj:6007) at clojure.main$main_opt.invokeStatic(main.clj:491) at clojure.main$main_opt.invoke(main.clj:487) at clojure.main$main.invokeStatic(main.clj:598) at clojure.main$main.doInvoke(main.clj:561) at clojure.lang.RestFn.applyTo(RestFn.java:137) at clojure.lang.Var.applyTo(Var.java:705) at clojure.main.main(main.java:37)

I followed the steps from the installation section of the Readme and cannot determine where exactly this could be popping up. Any assistance is more than appreciated!

Support for TAP

It would be nice to have TAP compatible reporter, so it will work nicely with the whole existing ecosystem of tools.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.