Skip to content

Conversation

@AlexeyRaga
Copy link
Collaborator

What

Separate concerns of writing a journal and formatting the report.

This also simplifies report building (and unifies between hedgehog and hedgehog.xunit), and simplifies exceptions handling for repors.

How

We do it by allowing "tagged" journal entries, so that:

  • adding to the journal is not concerned about transforming values to strings
  • report formatter has a bit more information about the intent and the meaning of each of the report entries

The essence of the change

Instead of

[<Struct>]
type Journal =
    | Journal of seq<unit -> string>

We now have

type JournalLine =
    | TestParameter of name: string * value: obj     // Individual test method parameter
    | GeneratedValue of value: obj                   // forAll generated values (no name)
    | Counterexample of message: string              // Property.counterexample user messages
    | Exception of exn: exn                          // Original exception, unwrap at render
    | Cancellation of message: string                // OperationCanceledException messages
    | Text of message: string                        // Plane text messages (info, etc.)

[<Struct>]
type Journal =
    | Journal of seq<unit -> JournalLine>

Now the code that writes the journal is not concerned about casting to strings, or curating exceptions, etc.
Report generator can do it.

Generated report example

Even if I have a property definition that combines both xUnit and explicit techniques, I can have a good report:

// we are using Property attribuye and provided test method parameters
[Property]
public Property<bool> Foo(List<byte> myBytes, byte myInt) =>
    // and we also use explicit hedgehog-style generators in the same property
    from x in Gen.Alpha.String(Range.Constant(1, 20)).ForAll()
    from y in Gen.Bool.ForAll()
    from _ in Property.CounterExample(() => $"{x} is longer than {myInt}")
    select TestFail(myBytes.Count);
Hedgehog.Xunit.PropertyFailedException
  *** Failed! Falsifiable (after 1 test and 6 shrinks):

You can reproduce this failure with the following Recheck Seed:
  "0_691509993339586027_438402981521466921_0-0-0-0-0-0"

Test parameters:
  myBytes = []
  myInt = 0uy

Generated values:
  "a"
  false

Counterexamples:
  a is longer than 0

Actual exception:
System.InvalidOperationException: I fail with 0
   at Hedgehog.Xunit.Tests.CSharp.Async.TestFail(Int32 x) in /Users/alexey/src/github/fsharp-hedgehog/tests/Hedgehog.Xunit.Tests.CSharp/Async.cs:line 58
   at Hedgehog.Xunit.Tests.CSharp.Async.<>c__DisplayClass7_0.<Foo>b__3(<>f__AnonymousType0`2 <>h__TransparentIdentifier0, Unit _) in /Users/alexey/src/github/fsharp-hedgehog/tests/Hedgehog.Xunit.Tests.CSharp/Async.cs:line 55
   at <StartupCode$Hedgehog>[email protected](TCollection b) in /Users/alexey/src/github/fsharp-hedgehog/src/Hedgehog/Linq/Property.fs:line 424
   at [email protected](a x)
   at Hedgehog.Outcome.cata[a,b](Outcome`1 outcome, FSharpFunc`2 failure, FSharpFunc`2 discard, FSharpFunc`2 success) in /Users/alexey/src/github/fsharp-hedgehog/src/Hedgehog/Outcome.fs:line 21
   at Hedgehog.Outcome.Map[a,b](FSharpFunc`2 f, Outcome`1 result) in /Users/alexey/src/github/fsharp-hedgehog/src/Hedgehog/Outcome.fs:line 25
   at [email protected](Tuple`2 tupledArg)

<xUnit stack trace hidden>

@AlexeyRaga AlexeyRaga force-pushed the tagged-journal branch 3 times, most recently from 8aa4698 to 0309fc3 Compare December 4, 2025 03:56
@moodmosaic
Copy link
Member

Thank you! I'm looking into it. I'll get back with feedback.

Copy link
Member

@moodmosaic moodmosaic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is strictly better UX. Thank you! Since the Journal type signature changed it should require a major version bump.

@AlexeyRaga AlexeyRaga merged commit 87e418b into master Dec 7, 2025
2 checks passed
@AlexeyRaga AlexeyRaga deleted the tagged-journal branch December 7, 2025 22:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants