Customizing reporting

marick edited this page Jan 20, 2013 · 6 revisions
Clone this wiki locally

You can change the way Midje produces output. Currently, there are only two emitters:

  • midje.emission.plugins.default: The default
  • midje.emission.plugins.silence: No output. The same effect as setting the print level to :print-nothing.

You can create your own plugin by writing a namespace that implements some or all of nine reporting functions. One of those functions, fail, must handle eight different kinds of failures. You can replace some or all of the failure-handling functions.

Once you've written your namespace, you can refer to it in a configuration file with a line like one of these three:

(change-defaults :emitter 'myproj.emitter)                   ; a namespace in your project
(change-defaults :emitter "resources/emitter.clj")           ; a file in your project, relative to the project root
(change-defaults :emitter "/Users/marick/stuff/emitter.clj") ; a file with its absolute pathname

Plugin mechanics

Note: This interface is experimental. The purpose of this documentation is to tempt you to collaborate with me on writing a plugin, thus improving the interface. Mail me.

Here is an example of an emitter that overrides the finishing-top-level-fact function to print something:

(ns ^{:doc "Adding an emitter that brags about fact results"}
  (:require [midje.emission.plugins.util :as util]
            [ :as fact]
            [midje.emission.plugins.default :as default]
            [midje.emission.state :as state]))

(defn finishing-top-level-fact [fact]
  (util/emit-one-line (format "Dude! `%s` at line %d of %s totally finished!"
                              (fact/name fact)
                              (fact/line fact)
                              (fact/file fact)))

  ;; Plugins are not responsible for keeping track of successes and
  ;; failures. That happens independently, and you gain access to the
  ;; counts through the `midje.emission.state` namespace.
  (util/emit-one-line (format "We're up to %d passing checks!"

;; The emission map is how you hook your functions into the
;; system. It also makes it convenient to "inherit" from an
;; existing map.
(def emission-map (assoc default/emission-map
                         :finishing-top-level-fact finishing-top-level-fact))

;; Here's where the installation happens.
(state/install-emission-map emission-map)

The main API

Plugins are not responsible for obeying the [[print level|print levels]]. Plugin functions won't be called if the print level is too low. The following shows the lowest print level at which the function is called.

  • :pass :print-summary

    ... is called when a check succeeds. It takes no arguments.

  • :fail :print-summary

    ... takes a map of information, which differs depending on the type of failure. See below.

  • :future-fact :print-summary

    ... takes a fact's or =future=> check's description (a string) and a position (a filename / line number pair). Note that the description may be nil.

    Even if the print level is high enough, the :visible-future configuration setting might prevent the function from being called.

  • :starting-to-check-top-level-fact :print-facts

    ... is given a fact as its only argument. Facts are functions adorned with metadata.

    Tabular facts count as top-level facts. They have N facts nested within them, one for each table row.

  • :finishing-top-level-fact :print-facts

    ... is called when all the checks in the fact have been completed. It is given the fact as its argument.

  • :starting-to-check-fact :print-facts

    ... is given a fact as its argument. It is called for all facts, not just a top-level fact.

    For top-level-facts, :starting-to-check-top-level-fact is called first, then :starting-to-check-fact.

  • :finishing-fact :print-facts

    ... is given a fact as an argument. For a top-level fact, it is called first, and then :finishing-top-level-fact.

  • :possible-new-namespace :print-namespaces

    ... is called for each top-level fact (before :finishing-top-level-fact). Its argument is the namespace in which the fact was defined.

    For the sake of better error messages, it is also called just before Midje starts loading a namespace (as with the repl tools function load-facts or with lein midje).

  • :starting-fact-stream (always called)

    A fact stream is a series of zero or more facts to be checked. % lein midje and the repl tool functions load-facts, check-facts, and recheck-fact start fact streams. That's when this function is called. It takes no arguments.

    At the beginning of a fact stream, the pass/fail counters are reset to zero. This happens after :forget-everything, so that you can capture the values if you desperately want to.

  • :finishing-fact-stream :print-normally

    ... is called when a fact stream has been checked.

    When a fact stream is checked by lein midje or (load-facts), clojure.test deftests are also checked. In that case, the results are passed in as a single argument. The results are an ordinary clojure.test results map (:pass, :fail, :error, and :test), plus an additional key, :lines, that captures the test output.

    In other cases, no argument is given.

The failure API

fail is passed different maps, depending on the details of the failure. The type of failure is always the :type key in the map.

  • :actual-result-did-not-match-expected-value

    From, for example, (fact (+ 1 2) => (+ 1 5))

    • :expected-result (5 in this case)
    • :actual (3 in this case)
  • :actual-result-should-not-have-matched-expected-value

    From, for example, (fact (+ 1 2) =not=> (+ 1 2)

    • :expected-result (3 in this case)
    • :actual (3 in this case)
  • :actual-result-did-not-match-checker

    From, for example, (+ 1 2) => (roughly 10000)

    • :expected-result-form ((roughly 10000) in this case)
    • :actual (3 in this case)
    • :intermediate-results (a sequence of Clojure forms from chatty checkers
    • :notes (a sequence of strings)
  • :actual-result-should-not-have-matched-checker

    From, for example, (+ 1 2) =not=> (roughly 3)

    • :expected-result-form ((roughly 3) in this case)
    • :actual (3 in this case)
  • :some-prerequisites-were-called-the-wrong-number-of-times

    • :expected-count A number or sequence.
    • :actual-count
    • :expected-result-form
  • :prerequisite-was-called-with-unexpected-arguments

    TBD: It's complicated.

  • :parse-error

    • :notes Text about the error.
  • :exception-during-parsing

    • :macro-form The form being translated when the exception happened.
    • :stacktrace A stacktrace, filtered down to Midje functions. (Still largely useless)