cursive

cap10morgan 2025-06-10T16:57:52.078619Z

When working on babashka projects with a whole src/ hierarchy of files, is there a way to tell Cursive "everything in this project will be a Babashka script" instead of needing to mark each new file?

cfleming 2025-06-10T21:02:58.250379Z

There isn't, but that's a good idea, I'll see about being able to mark the source folder itself.

šŸ‘ 1
seancorfield 2025-06-10T21:10:13.017699Z

I have a question about running tests: how exactly does Cursive hook into clojure.test from both a test running perspective and a test reporting perspective? Background: I spent some time chatting with @cfleming back in the day about getting Expectations (`clojure.test` edition) properly integrated with Cursive's test running and reporting. But I'm migrating away from Expectations -- because of the underlying limitations of clojure.test -- and moving to LazyTest instead. It's "better" because it has separates test running and test reporting but that also means that how you run LazyTest and how you report the results is different. I'd like to see if we can collectively get LazyTest integrated into whatever Cursive does with tests.

msolli 2025-06-10T22:14:00.343129Z

I would absolutely adore to have LazyTest integration with Cursive. I’m converting our whole codebase over, and it’s fantastic, except Cursive doesn’t support it. I run my tests using a couple of REPL commands now, but obviously there’s no in-editor reporting or anything like that. If there’s anything I, as a regular user, could do to help, I’m all in.

raspasov 2025-06-11T00:23:23.745769Z

I am not sure about the internals (obviously) but that’s how it looks from end-user perspective (config and output):

raspasov 2025-06-11T00:23:57.111799Z

And here’s the file that cursive ā€œcompilesā€ to run the tests…

šŸ‘€ 1
raspasov 2025-06-11T00:29:36.629609Z

Assembles a classpath, and runs the file above.

seancorfield 2025-06-11T00:34:34.718299Z

Ah, makes sense that it basically wraps clojure.test directly. I suspected that might be the case. And it assumes clojure.test reporting machinery as well.

raspasov 2025-06-11T00:36:13.846979Z

It’s nice, but in reality it’s just a glorified terminal output with a list of tests… Jumping to source of a failed test is nice, but not sure if that’s ground breaking. (as in, you can probably just run any test framework in the terminal and get 90% of the benefit). Maybe there are features that I am not using/realizing šŸ˜… I think if something fails, it might show a diff of ā€œexpectedā€ vs ā€œcurrentā€ data… (don’t remember)

raspasov 2025-06-11T00:37:32.816099Z

Splitting the stdout/err output by test is nice

raspasov 2025-06-11T00:37:42.729759Z

So it’s not just one huge blob of text.

raspasov 2025-06-11T00:38:43.792919Z

Diff of expected/actual (useful)

raspasov 2025-06-11T00:39:40.936919Z

(not a great example, but if there’s a subtle departure of expected/actual data, it can help a ton)

cfleming 2025-06-11T03:48:45.474659Z

There are two integrations that Cursive has - what Rangel's showing there is the test runner, but you can also run tests interactively in the REPL, like this: https://cursive-ide.com/userguide/testing.html, which I think is what Martin is talking about. But they're definitely both tied to clojure.test very heavily right now. I would love an excuse to change that though! In particular, a solution which permits CLJS testing would be great. Is there any doc about how lazytest works? I assume it's not built on clojure.test, having read about it fairly briefly?

1
2025-06-11T04:12:19.680199Z

hi, i made lazytest. https://github.com/NoahTheDuke/lazytest#intellij is what i figured out for making cursive commands to run lazytest tests.

2025-06-11T04:14:25.556519Z

It's not built on clojure.test. what kind of information do you need about how it works? how can I help?

raspasov 2025-06-11T04:17:15.114059Z

Ah, didn’t realize it can run tests interactively in the REPL… I was just eval-ing part of the test code to do that, should try the built-in option šŸ™‚

cfleming 2025-06-11T05:54:47.958829Z

> Ah, didn’t realize it can run tests interactively in the REPL… This is the sort of moment that makes me realise I really need to get on to making short videos of Cursive features šŸ™‚

cfleming 2025-06-11T06:01:11.218219Z

@nbtheduke Those commands look good for a semi-interactive integration, and are probably the best you can do in user space right now. There are two parts to the integration: one is running the tests, which is easy enough - I have commands for "run all tests in this ns" and "run this test under the caret" - basically what you have in your commands. The trickier bit is the reporting - there isn't a good way to get bidirectional communication with the REPL out of the box, so at the moment the integration binds the report var and just writes specially formatted messages to stdout, and the REPL integration parses those and annotates the results. What does lazytest provide in terms of test result events?

raspasov 2025-06-11T07:59:46.615779Z

> This is the sort of moment that makes me realise I really need to get on to making short videos of Cursive features Oh yes…

raspasov 2025-06-11T08:01:36.609599Z

gif ā€œvideoā€ works great for stuff like that, I think. Example: https://github.com/apple/container

raspasov 2025-06-11T08:02:03.187349Z

2025-06-11T14:00:53.954189Z

@cfleming I'd point you to the documentation but i still haven't written it yet. šŸ˜… lazytest supports arbitrary custom reporters, which can be combined to run in the order provided. Each reporter is a function that is called with each suite before and after, and with each test case before and after, as well as each test case result. Typically, the reporter is a multi method that dispatches on :type but lazytest does not make assumptions. I can go into details about the various types if desired, and I will make some time to write proper documentation about this in general later today.

cfleming 2025-06-11T19:38:32.174509Z

@nbtheduke Ok, that sounds like it should be relatively easy to integrate with. Do you provide reports for individual assertions? i.e. expect or it blocks?

cfleming 2025-06-11T19:43:50.676719Z

Also, something else useful is that in clojure.test I get the expected and actual values when an = test fails. It's not ideal in that I have to fish it out of the macro expanded form, but it's there and essential for showing diffs.

cfleming 2025-06-11T19:48:16.779789Z

One other good thing to cover in the doc would be how the reporting works for generative testing, if there's any relevant detail there.

2025-06-11T19:51:15.701369Z

Lazytest doesn't report individual assertions because it deals in "test cases". A test case runs the given body, and if it doesn't throw an exception, it's counted as a success. This means that if you want to have granular reporting on each assertion, you should split them into separate test cases. for example:

(describe "some outer context"
  (it "is a test case"
    (expect (= 1 1))
    (expect (true? false))
    (expect (delete-the-database))))
the above test case will run and report a failure on the second expect because it throws, and the third expect won't be executed at all.

2025-06-11T19:53:22.116029Z

it has a similar expected vs actual tracking that clojure.test/is has, which is passed to the reporters

2025-06-11T19:55:09.374709Z

I don't know much about generative testing so I can't speak much to that, but test.check's defspec is just a thin wrapper over test.check's quick-check passed to clojure.test/is, so i think doing the same in Lazytest should be... easy? i hesitate to say it because that always blows up in my face lol

cfleming 2025-06-11T21:43:32.619979Z

Yeah, the reporting in test.check is tricky IIRC.

cfleming 2025-06-11T21:44:18.603429Z

That's a shame about the individual assertions. Is there any chance of reconsidering that? That allows things like the little pass/fail icons in Cursive's gutter area which are really nice.

seancorfield 2025-06-11T21:45:11.365559Z

Well, the pass/fail icons could go on the it (or expect-it) lines since those are the test cases?

seancorfield 2025-06-11T21:45:39.842789Z

(I mean, yeah, it would be nice to indicate the pass/fail/not-run on the expect lines too)

cfleming 2025-06-11T21:45:46.081219Z

Sure, they could, but that's not as nice as seeing each individual... right.

cfleming 2025-06-11T21:46:41.039309Z

It looks like this in Cursive, I (and most everyone else) like it a lot: https://cursive-ide.com/userguide/images/testing/test-results.webp

seancorfield 2025-06-11T21:48:32.359079Z

As a user of LazyTest, I really like the separation of test cases from assertions, TBH, and that a test case fails on the first assertion that fails. I've often wished clojure.test did that instead of ploughing on and producing additional failures.

cfleming 2025-06-11T21:54:02.599919Z

Thinking about this, I don't know how I would support what Cursive does. Test running is not just a pass/fail, in the case of a fail you want to see the diff between expected/actual. I can do that with clojure.test because I get individual assertion reports - with lazytest I would not be able to do that.

šŸ‘† 1
cfleming 2025-06-11T21:54:42.262019Z

If you just want pass/fail reporting, then @nbtheduke's existing REPL commands are probably good enough already.

cfleming 2025-06-11T21:56:14.920189Z

I guess if that detail is surfaced in the exception then I could potentially show it there, and use the exception stack trace to identify the failing assertion.

cfleming 2025-06-11T21:57:11.629909Z

That depends on that detail being surfaced in the reporter though....

seancorfield 2025-06-12T00:00:09.686959Z

LazyTest supports custom reporters. I think the way forward here is a custom reporter that provides the information Cursive needs.

šŸ‘ 1
2025-06-12T00:12:21.561919Z

lazytest uses very similar evaluation logic to clojure.test, where in the event of a failed assertion, it captures the input form, the evaluated result, and when given a "function call", the evaluated arguments as well. it doesn't capture passing assertions

2025-06-12T00:19:20.697269Z

if the logic for generating the test runner code is public, i can try writing a lazy test reporter for it

cfleming 2025-06-12T00:57:59.730809Z

@nbtheduke In a failure does it also capture the expected value?

2025-06-12T01:14:48.609589Z

sure? what's the expected value of (expect (zero? (foo ...)))? there's no built in handling of clojure.core/=, if that's what you mean.

2025-06-12T01:26:40.779249Z

i'm on my phone so my apologies up front. given (expect (= 5 (foo))), the resulting ex-data map will be

{:ns user
 :expected '(= 5 (foo))
 :evaluated (list #function[clojure.core/=] 5 :cool-keyword)
 :actual false
 :message nil}

cfleming 2025-06-12T01:29:16.754919Z

Right, so Cursive handles = specially, but I can do that in your example there by checking the form in :expected. In your example is false (from :actual) the result of (foo)?

šŸ‘ 1
2025-06-12T01:29:27.787139Z

and given (expect (or (foo) (bar)) "this better work!"), the resulting map will be

{:ns "user"
 :expected '(or (foo) (bar))
 :actual nil
 :message "this better work!"}

2025-06-12T01:30:09.482159Z

yeah, it's the result of the evaluated form

cfleming 2025-06-12T01:30:53.858869Z

Actually, wait - false is actually the value of (= 5 (foo)), right? Not (foo)?

cfleming 2025-06-12T01:31:46.108079Z

And it looks like :cool-keyword is (foo)?

2025-06-12T01:32:27.755359Z

yes, right

cfleming 2025-06-12T01:33:21.228079Z

Ok. Can I customise the way the reporting is done, so that on a failure I can create an exception object and store that, then I'll use the stack trace to figure out where the assertion is in the code?

cfleming 2025-06-12T01:33:38.692549Z

Then I can paint pretty icons on it.

2025-06-12T01:34:27.842639Z

yes. i didn't include that info in my example but the location data is all included

2025-06-12T01:34:49.723339Z

https://github.com/NoahTheDuke/lazytest/blob/77d9a01e87636fa2196d3d4db78e52a7219cbdf9/src/clojure/lazytest/reporters.clj#L149 here is existing code that pretty prints equality checks

cfleming 2025-06-12T01:34:59.234929Z

I guess it's also potentially in metadata on :expected too, right?

2025-06-12T01:36:51.034249Z

yeah, but i also include that directly in the data map

cfleming 2025-06-12T01:37:37.919909Z

Oh ok great.

cfleming 2025-06-12T01:38:30.434029Z

Ok, I'm working on something else right now but I'll see if I can get this in this release cycle, it'll be an interesting test of whether I can extend the test functionality I have easily or not.

2025-06-12T01:39:20.614849Z

for better or worse, i built a custom ExceptionInfo that doesn't generate stack traces because ex-info is really really slow, which is why i include the metadata directly.

2025-06-12T01:40:28.357509Z

this is a lot to ask of you so let me know if i can help

cfleming 2025-06-12T01:43:12.313709Z

I will do, thanks - it looks like lazytest is pretty well set up with the info I need, so thanks for that!

šŸ‘ 1
šŸŽ‰ 1