#Lazytest

1 messages · Page 1 of 1 (latest)

ruby bay
#

i cut a new release of lazytest yesterday, including a feature i've wanted in clojure core forever

#
(defn test-fn
  {:test #(expect (= 0 (test-fn 1)))}
  [a]
  (+ a a))

(defn test-test-case
  {:test (it "test case example"
           (expect (= 1 (test-test-case 1))))}
  [a]
  (+ a a))

(defn test-suite
  {:test (suite
           (test-seq
            [(it "test-seq example" (expect (= 1 (test-suite 1))))
             (it "test-seq example two" (expect (= 0 (test-suite 1))))]))}
  [a]
  (+ a a))

(defn test-describe
  ([a]
   (+ a a))
  {:test (describe "top level"
           (it "test-describe example" (expect (= 1 (test-describe 1))))
           (it "test-describe example two" (expect (= 0 (test-describe 1)))))})
worldly spruce
#

that last one really surprises me, how does that syntax work with the test at the end of the defn after the fn tail?

ruby bay
#

all of those produce lazytest tests, and are appropriately run if they're included on the test path

#

the attr-map can be at the end of defn forms if the bodies are wrapped in parentheses

#
   :arglists '([name doc-string? attr-map? [params*] prepost-map? body]
                [name doc-string? attr-map? ([params*] prepost-map? body)+ attr-map?])
worldly spruce
#

oh, that's weird, I didn't know that one

#

that's cool!

ruby bay
#

yeah, it's really neat! it means your code bundles lazytest, which is a bummer, but it also means you get "Rich Comment Forms" directly with the code itself

#

i ran into this idea recently with Pyret (by the Brown University PLT folks), where any function can have a where: clause at the end which acts as a unit test: ```

fun sum(l):
cases (List) l:
| empty => 0
| link(first, rest) => first + sum(rest)
end
where:
sum([list: ]) is 0
sum([list: 1, 2, 3]) is 6
end

worldly spruce
#

that seems cool

ruby bay
#

cut a new release to add --watch support, so you can run your test suite every time you change a file

ruby bay
#

a lil sneak peak of what i'm working on:

  (given [state (volatile! [])]
    (describe "not order dependent"
      {:context [(after (vswap! state conj :after))
                 (before (vswap! state conj :before))]}
      (expect-it "temp" true))
    (expect-it "tracks correctly"
      (= [:before :after] @state)))
#

with this and an around helper, i think i'd consider the library on par with clojure.test. fixtures have been bugging me for a while, but cracking it as simply metadata on suites is the key, i think

ruby bay
#

okay, i'm a fucking god lol

#
(defn vconj! [volatile value]
  (vswap! volatile conj value))

(defdescribe complex-context-test
  (given [state (volatile! [])]
    (describe "top level"
      {:context [(before (vconj! state :before-top))
                 (before-each (vconj! state :before-each-top))
                 (after-each (vconj! state :after-each-top))
                 (after (vconj! state :after-top))]}
      (describe "middle level"
        {:context [(before (vconj! state :before-middle))
                   (before-each (vconj! state :before-each-middle))
                   (after-each (vconj! state :after-each-middle))
                   (after (vconj! state :after-middle))]}
        (describe "bottom level"
          {:context [(before (vconj! state :before-bottom))
                     (before-each (vconj! state :before-each-bottom))
                     (after-each (vconj! state :after-each-bottom))
                     (after (vconj! state :after-bottom))]}
          (expect-it "temp 1" (vconj! state :bottom-expect-1))
          (expect-it "temp 2" (vconj! state :bottom-expect-2)))))
    (expect-it "tracks correctly"
      (= [:before-top
          :before-middle
          :before-bottom
          :before-each-top
          :before-each-middle
          :before-each-bottom
          :bottom-expect-1
          :after-each-bottom
          :after-each-middle
          :after-each-top
          :before-each-top
          :before-each-middle
          :before-each-bottom
          :bottom-expect-2
          :after-each-bottom
          :after-each-middle
          :after-each-top
          :after-bottom
          :after-middle
          :after-top] @state))))
ruby bay
#

okay, i've done it. i have full before/after support plus namespace-level fixtures, which i think covers all of the existing clojure.test functionality

#

the fixture/context support works on suites and on text cases, so anywhere you can write {:focus true}, you can add lexically scoped fixtures

#

i played with full run support, but that's really opaque and feels naughty

#

if someone wants to do that, they can call main/run themselves.

#

i think the last thing i wanna do before i consider this fairly complete is deprecating the given macro in favor of :let in the suite metadata:

(describe "example"
  {:let [foo 100]}
  (it "works"
    (expect (= 100 foo))))
#

this would roughly match how doseq and for work, and would magically do the right thing instead of given, which requires fiddling

ruby bay
#

I've decided to pull the trigger on 1.0. I use this exclusively in splint, and having rewritten the internals to be more idiomatic (maps with children instead of nested seqs with metadata), I think this ready for prime-time.

  • Added before, after, before-each, and after-each hooks. These act as suite-specific fixtures, allowing for creating or setting temporary data across nested suites or test cases. before and after run where they're called, before-each and after-each are gathered in declaration order and called on each test-case.
(defdescribe context-test
  (let [state (volatile! [])]
    (describe "it runs both"
      {:context [(before (vswap! state conj :before))
                 (after (vswap! state conj :after))]}
      (expect-it "temp" true))
    (expect-it "tracks correctly"
      (= [:before :after] @state))))```
- Added around hook, which acts like a clojure.test fixture, taking a no-arg function of the nested suites/tests and letting you write arbitrary code inside (which handles binding needs):
```clojure
(describe "it works"
  {:context [(around [f]
               (binding [*foo* 100]
                 (f)))]}
  (expect-it "matches" (= 100 *foo*)))```
- Added set-ns-context! which alters the namespace's metadata to hold the provided context data, allowing for clojure.test-like use-fixture functionality.
#

i think at this point lazytest has all of the features of clojure.test

#

extensible reporters, custom assertion macros, running specific tests or a set of namespaces or the whole thing, test selectors through metadata like cognitect's test-runner, "testing" blocks, etc

ruby bay
#

lol idk why i did this but i implemented a bunch of "interfaces" for lazytest

#
(defcontext interface-context-test
  (specify "defcontext works"
    (expect true "expect works inside"))
  (context "context works"
    (specify "specify works"
      (expect true))))

(defsuite interface-tdd-test
  (suite "defsuite works"
    (expect true "expect works inside"))
  (suite "suite works"
    (test "specify works"
      (expect true))))

(deftest interface-deftest-test
  (is true "expect works inside")
  (testing "testing works"
    (is (= 7 (+ 3 4)) "is works"))
  (testing "are works"
    (are [x y] (= x y)
      2 (+ 1 1)
      4 (* 2 2))))

(facts interface-midje-test
  (fact "top-level facts works"
    (expect true "expect works inside"))
  (facts "facts works"
    (fact "fact works"
      (expect true))))
#

i briefly thought about implementing midje's "given => expected-result" form, but that's really annoying and midje is actually super complex so idk that i want to even act like lazytest can handle it

ruby bay
#
(ns noahtheduke.example-test
  (:require
   [lazytest.interfaces.qunit :refer [prep-ns-suite! module! test! assert!]]))

(prep-ns-suite!)

(module! "Group A")

(test! "foo"
  (assert! (true? (pos? 1)))
  (assert! (false? (pos? 0)) "Expected to be false"))
ruby bay
#

I've added doc-test support, so you can pass in markdown files with --md and they'll be scanned and treated as tests:

# Cool stuff is happening!
~~~clojure
(adder 5 6)
;; => 11
~~~

is treated as (defdescribe test-12345 (it "Cool stuff is happening!" (expect (= 11 (adder 5 6))))) And you can skip tests with Markdown's info-strings:

(System/exit 1)

I've also added preliminary support for Expectations v2: (it "just works" (expect String (name :foo)))

craggy void
#

Literate tests! 😍

ruby bay
#

these are all run in CI right now

ruby bay
#

a smaller release, but it cleans up :output and :reporter so all external-facing functions take :output which is converted to a reporting function in :reporter, and it allows for trailing args as the paths: clojure -M:dev:test test/unit test/integration

#

along with a fair amount of documentation

ruby bay
#

been a minute since i posted in here. i released 1.7.0 today which makes Expectations v2 a first class namespace

craggy void
#

i love having expectations of being lazy 🥰

ruby bay
#

this one includes hooks (plugin support), adds a new context function around-each, fixes expect-it to respect context functions, and cleans up the order of all of the context functions in general