lazytest

hadils 2025-10-11T16:58:41.012809Z

Hi! Thank you for this excellent library! I have a small problem. In the code below, the first expect that refers to @services-proxy executes after all tests, regardless of the sleep times I put in.

(ns rama.core-module-test
  (:require
   [hashp.preload]
   [rama.core-module :as sut]
   [shared.schema :as schema]
   [com.rpl.rama :as r]
   [com.rpl.rama.path :as rp]
   [com.rpl.rama.test :as rtest]
   [lazytest.core :as lt :refer [defdescribe describe expect it expect-it]]))

(defdescribe core-module-test
  (describe "service"

            (schema/init-registry)

            (with-open [ipc (rtest/create-ipc)]
              (rtest/launch-module! ipc sut/CoreModule {:tasks 4 :threads 4})
              (let [module-name (r/get-module-name sut/CoreModule)
                    service-depot (r/foreign-depot ipc module-name "*service-depot")
                    services (r/foreign-pstate ipc module-name "$$services")
                    services-proxy (r/foreign-proxy [] services)]

                (let [new-service (schema/->CreateService "mow-lawn" "mow lawn" "" "lawn-care" "per-sqft" true)
                      {id "core"} (r/foreign-append! service-depot new-service)
                      _ (Thread/sleep 50)
                      result (r/foreign-select-one [] services)]
                  (it "should create a service"
                      (expect
                       (= {id {:id id
                               :name "mow lawn"
                               :description ""
                               :category "lawn-care"
                               :price-units "per-sqft"
                               :active? true
                               :deleted? false}} result))
                      (expect
                       (= {id {:id id
                               :name "mow lawn"
                               :description ""
                               :category "lawn-care"
                               :price-units "per-sqft"
                               :active? true
                               :deleted? false}} @services-proxy))))
                (let [updated-service (schema/->UpdateService
                                       "mow-lawn"
                                       [(schema/->ServiceEdit :description "foo bar baz")])
                      {id "core"} (r/foreign-append! service-depot updated-service)
                      _ (Thread/sleep 50)
                      result (r/foreign-select-one [] services)]
                  (it "should update a service"
                      (expect
                       (= {id {:id id
                               :name "mow lawn"
                               :description "foo bar baz"
                               :category "lawn-care"
                               :price-units "per-sqft"
                               :active? true
                               :deleted? false}} result))
                      (expect
                       (= {id {:id id
                               :name "mow lawn"
                               :description "foo bar baz"
                               :category "lawn-care"
                               :price-units "per-sqft"
                               :active? true
                               :deleted? false}} @services-proxy))))))))
(This refers to Rama code).

2025-11-07T19:33:57.142249Z

I've pushed some changes to a branch, let me know if you think these two sections are more clear about the differences. https://github.com/NoahTheDuke/lazytest/tree/nb/push-nyzlkooxrnqm?tab=readme-ov-file#writing-tests-with-lazytest https://github.com/NoahTheDuke/lazytest/tree/nb/push-nyzlkooxrnqm?tab=readme-ov-file#common-patterns

seancorfield 2025-11-07T20:16:41.002269Z

The side-by-side comparison with clojure.test is awesome -- thank you!

👍 1
2025-11-07T20:28:54.809969Z

glad that helped. i feel like there are better examples but they start to get unwieldy when they grow too big lol

andyhorng 2025-11-07T23:20:15.266809Z

Thank you! that's great. it is written very clearly and helped me a lot

👍 1
2025-11-07T23:27:03.185449Z

i merged that branch to main

🎉 1
seancorfield 2025-11-08T00:10:25.765279Z

I appreciate it. Documentation is hard.

➕ 1
seancorfield 2025-10-11T17:20:23.412629Z

I suspect you're falling foul of a common misunderstanding about LazyTest: when that defdescribe executes, it produces a function -- a test suite -- that is later executed by the runner, so everything "between" the describe and the it has already executed when the test suite is created, and then the it / expect parts run:

user=> (defdescribe foo
  #_=>   (println "one")
  #_=>   (describe "outer stuff"
  #_=>             (println "two")
  #_=>             (it "inner stuff"
  #_=>                 (println "three")
  #_=>                 (expect (nil? (println "four")))
  #_=>                 (println "five"))
  #_=>             (println "six"))
  #_=>   (println "seven"))
#'user/foo
user=> (run-test-var #'foo)
one
two
six
seven
three
four
five
Ran 1 test cases in 0.00069 seconds.
0 failures.
{:total 1, :pass 1, :fail 0}
See how one, two, six, seven are all printed before three, four, five?

seancorfield 2025-10-11T17:20:45.914379Z

and:

user=> (foo)
one
two
six
seven
#lazytest.suite.Suite{:type :lazytest/var, :doc "foo", :children [#lazytest.suite.Suite{:type :lazytest/suite, :doc "outer stuff", :children [#lazytest.test_case.TestCase{:type :lazytest/test-case, :doc "inner stuff", :body #function[user/foo-20716--20717/fn--20718/fn--20723/it--19991--auto----20728], :context {}, :ns #namespace[user], :file "NO_SOURCE_PATH", :line 5, :metadata nil, :column 13}], :context {}, :ns #namespace[user], :file "NO_SOURCE_PATH", :line 3, :var nil, :metadata nil, :column 3}], :context {}, :ns #namespace[user], :file "NO_SOURCE_PATH", :line nil, :var nil, :metadata {:var #'user/foo}}

hadils 2025-10-11T17:21:33.539479Z

Ah. Thank you very much @seancorfield!

seancorfield 2025-10-11T17:21:49.876669Z

I think this catches everyone out at least once or twice 🙂

1
seancorfield 2025-10-11T17:34:52.170219Z

@nbtheduke Is there a GH issue open about adding an example like this to the docs, to show how best to set up this sort of side-effect based testing?

seancorfield 2025-10-11T17:37:27.548359Z

Ah, it was this issue and it is closed: https://github.com/NoahTheDuke/lazytest/issues/24 @hadilsabbagh18 I guess the addition Noah added to the README to address #24 still needs further elaboration to clarify the behavior?

2025-10-11T17:45:14.911489Z

yeah, seems i need to be way more explicit about this. if you have any suggestions or a PR even, i'm all for it

andyhorng 2025-11-07T00:04:48.156779Z

I'm running into the exact same issue: the resource created by with open is already closed by the time the test runs. I'm curious: does lazytest only handle this using dynamic variables and binding to manage the resource? I find that approach a bit tedious.

2025-11-07T00:45:03.443059Z

atoms, volatiles, dynamic variables

2025-11-07T00:45:40.954649Z

the behavior and style are based on existing test frameworks in other languages with mutable variables such as javascript, ruby, python etc

seancorfield 2025-11-07T00:46:27.148659Z

He's talking about describe vs it and when it runs

2025-11-07T00:47:06.279079Z

right, i got that. the idea is that you only do set up/tear down in context functions or within test cases (it blocks)

👍 1
seancorfield 2025-11-07T00:48:07.305869Z

More examples in the readme would help:grinning:

👍 1
2025-11-07T00:48:40.269639Z

i'll spend some time on it tomorrow

andyhorng 2025-11-07T00:53:21.602699Z

Thanks! This is very helpful!