Does run-events! support event data?
(defn H-initialize [_env event-payload]
(println :H-initialize)
(println event-payload)
[(ops/assign :foo (get-in event-payload
[:_event :data :foo]))])
(deftest demonstrate-correct-event-data-testing
(testing "A focused example of how to test a handler that updates the data model"
(let [chart-def (statechart {}
(data-model {:expr {:foo nil}})
(state {:id :state/initial}
(transition {:event :event/initializing
:target :state/initialized}
(script {:expr H-initialize})))
(state {:id :state/initialized}))
config {:statechart chart-def}
overrides {}
env (testing/new-testing-env config overrides)]
(println 800000)
(testing/start! env)
(testing/in? env :state/initial)
(testing/run-events! env {:event :event/initializing
:data {:foo :bar}})
(testing/in? env :state/initialized)
(is (= (testing/data env) {:foo :bar})))))
; FAIL in (demonstrate-correct-event-data-testing) (NO_SOURCE_FILE:33)
; A focused example of how to test a handler that updates the data model
; expected: (= (testing/data env) {:foo :bar})
; actual: (not (= {:foo nil, :_event nil} {:foo :bar}))
; - {:foo nil, :_event nil}
; + {:foo :bar}It looks like the script doesn't get hit at all. Am I missing something?
You mean this one in :state/initial?
(script {:expr H-initialize}yep
I am trying to find the post where tony mentioned a transition can execute, give me a few.
Ah, maybe you mean this one? https://clojurians.slack.com/archives/C68M60S4F/p1733057517439029?thread_ts=1732929918.907499&cid=C68M60S4F
it links to a test in the source where it uses simple/send! to send an event with data inside a test.
I think your transition looks right to me, not sure why run-events! is not running it. I just searched my own statecharts and I am not using a single transition with a script, all my executable content is in on-entry and on-exit states.
I also use simple for all my stuff so I use simple/send! for events.
I can try that to see if it'll work with testing utils. Not a huge difference if it does work.
You’re missing something
The testing environment stubs out everything
See the comments about mocking. You specify a map of functions and what they should give as values (let [env (testing/new-testing-env (config) {is-tuesday? true})]
So it's not patching just certain parts of your statechart, you have to provide definitions of all the expressions in your chart and use {a a} sorta thing where you don't want a mock?
at the moment yes, but feel free to copy out the current utils and change how they work.
Still not sure what I'm missing here...
(defn H-initialize [_env event-payload]
(println :H-initialize)
; (println event-payload)
[(ops/assign :foo :xx)])
(defn my-cond [_ _]
(println :my-cond)
true)
(deftest demonstrate-correct-event-data-testing
(testing "A focused example of how to test a handler that updates the data model"
(let [chart-def (statechart {}
(data-model {:expr {:foo nil}})
(state {:id :state/initial}
(transition {:event :event/initializing
:target :state/initialized}
(script
{:expr H-initialize})))
(state {:id :state/initialized}))
config {:statechart chart-def}
overrides {H-initialize (fn [env & _]
(println 99999)
[(ops/assign :foo 999)])}
env (testing/new-testing-env config overrides)]
(println 800000)
(testing/start! env)
(is (testing/in? env :state/initial) "Not in :state/initial")
(testing/run-events! env {:event :event/initializing
:data {:foo :bar}})
(is (testing/in? env :state/initialized)))))I agree it’s not the best design…in my original thinking what I was thinking was that you’d start the chart in some specific spot, say what those expressions would do, and then run it to show what would happen.
it never prints 99999 or gets into :state/initialized
That way you could protect your expectations in tests without having to deal with the side effects/setup of whatever was behind the scenes
Hm. I don’t see the problem…
as in I’m not sure why it doesn’t
Swapping the run-events! call with (testing/run-events! env :event/initializing) fixes the test, but then I can't test the data
facepalm :event -> :name is the fix:
(testing/run-events! env {:name :event/initializing
:data {:foo :bar}})I find these wrong key errors always consume so much time when I make them.
new-event shows it clearly:
(>defn new-event
"Generate a new event containing `data`. It is recommended that `data` be
easily serializable (plain EDN without code) if you wish to use it
in a distributed or durable environment.
If using a map:
`name` - the event name
`data` - Extra data to send with the event
`type` - :internal, :external, or :platform. Defaults to :external
but I was looking at usages of simple/send! which usually take :event kw like:
(simple/send! env {:target :main/child
:event :event/exit})Should probably add guardrails to those, and then turn it on 😄
does run-events! have GR?
that would have told you right away you were doing it wrong
Nope, it doesn't. I've been meaning to ask you, do you dev with Guardrails on most of the time?
Lately I haven’t been writing much, but yes, I try to write most things with it. I’ve added optimizations that make that work out well, and it is definitely part of my testing process that I feel is critical to doing a good job.
These days I use it with Malli though instead of spec
In dev mode I have GR warn but not throw, but in test mode I have GR throw to make tests fail
I turn it off when doing a lot of processing because of the performance cost, but with the new-ish rate limiting settings I can probably try turning it on more.
not just rate limiting
You can filter out entire nses
Why Malli over Spec?
and there is support for libraries to list nses where it should be off as well…which statecharts leverages
Malli over spec: 1. Malli is clearer 2. Malli can be faster (I still need to optmize GR for that). 3. Malli specs can be just data, and the integration of things like custom error messages is easier
probably more…but those come to mind
https://github.com/fulcrologic/guardrails?tab=readme-ov-file#new-in-12 The dynamic exclusions let’s you just turn off checking on swaths of code you know are not needed checks. E.g. the internals of statecharts need checks when I’m working on the lib, but there should be no way for user code to cause internals to fail those checks.
So the library exclusions let the lib put things at the “top” of the classpath that can be found on load where they can auto-exclude things…meaning that you can write libraries that don’t infect projects with performance problems.
THe throttling also helps, but I don’t recommend it when testing
Alright I guess this is the motivation I need to get back on the Guardrails diet. Thanks for your time helping me sort this one out guys.