Repository navigation
Add Hedgehog.MSTest: Property attributes for MSTest 4 - #487
Merged
Merged
Conversation
xperiandri
added a commit
to fsprojects/FSharp.Azure.Cosmos
that referenced
this pull request
Oct 10, 2026
The Hedgehog MSTest adapter of #43 is proposed upstream as hedgehogqa/fsharp-hedgehog#487. The record notes how the proposal differs from the in-repository copy (net8.0 and F# 8, upstream's documentation in the shared files, VSTest), that MSTest 4.2 is the adapter's lowest version, and that one test of #43 stops compiling on MSTest 4.5.1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Terminal MSTest outcomes are misclassified, and bounded odd/even generators can emit values outside their requested ranges.
2 open findings
What changed in this PR
Adds a Hedgehog adapter for MSTest 4, including property attributes, generators, lifecycle integration, documentation, and comprehensive F# tests.
Changes:
- Adds MSTest property execution, configuration, shrinking, replay, data-row, and cancellation support.
- Adds built-in generator attributes and adapter test coverage.
- Integrates the projects into the solution and documentation.
| File | Description |
|---|---|
src/Hedgehog.MSTest/AutoGenConfig.fs |
Instantiates custom generator configurations. |
src/Hedgehog.MSTest/GenAttribute.fs |
Defines generator attribute abstractions. |
src/Hedgehog.MSTest/GenAttribute.Prelude.fs |
Adds built-in generator attributes. |
src/Hedgehog.MSTest/Hedgehog.MSTest.fsproj |
Configures the adapter package. |
src/Hedgehog.MSTest/InternalLogic.fs |
Implements property execution and result reporting. |
src/Hedgehog.MSTest/IPropertyAttribute.fs |
Defines shared property settings. |
src/Hedgehog.MSTest/Prelude.fs |
Adds internal utility helpers. |
src/Hedgehog.MSTest/PropertiesAttribute.fs |
Adds class-level property defaults. |
src/Hedgehog.MSTest/PropertyAttribute.fs |
Adds the MSTest property attribute. |
src/Hedgehog.MSTest/PropertyContext.fs |
Resolves effective property configuration. |
src/Hedgehog.MSTest/RecheckAttribute.fs |
Adds failure replay metadata. |
src/Hedgehog.MSTest/ReflectionHelpers.fs |
Adds return-type reflection helpers. |
tests/Hedgehog.MSTest.Tests.FSharp/Common.fs |
Provides shared test fixtures. |
tests/Hedgehog.MSTest.Tests.FSharp/GenAttributePreludeTests.fs |
Tests built-in generators. |
tests/Hedgehog.MSTest.Tests.FSharp/Harness.fs |
Provides adapter-level test execution. |
tests/Hedgehog.MSTest.Tests.FSharp/Hedgehog.MSTest.Tests.FSharp.fsproj |
Configures the MSTest suite. |
tests/Hedgehog.MSTest.Tests.FSharp/MSTestTests.fs |
Tests MSTest-specific behavior. |
tests/Hedgehog.MSTest.Tests.FSharp/PropertyTests.fs |
Tests property configuration and shrinking. |
docs/articles/getting-started.md |
Documents MSTest usage. |
Hedgehog.sln |
Registers the new projects. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
xperiandri
added a commit
to fsprojects/FSharp.Azure.Cosmos
that referenced
this pull request
Oct 10, 2026
The adapter was ported in upstream's style, because the same code was to go into the upstream pull request. That pull request now exists, hedgehogqa/fsharp-hedgehog#487, with its own copy in upstream's style, so the copy in this repository follows the coding guidelines of this repository, as the review of #43 asks: - A setting that is not given is a voption instead of an option: the members of IPropertyAttribute, the fields of PropertyContext, the private constructors and fields of PropertyAttribute and PropertiesAttribute, the generic arguments that AutoGenConfig infers and the generator that a GenAttribute gives. - The project compiles src/Shared/ValueCollections.fs again, like every other F# project, and uses its Seq.tryHead, Seq.tryFind, Seq.tryExactlyOne and Seq.zip, which return a voption or struct tuples. Prelude loses Option.requireSome and Seq.seqTryExactlyOne. - The generated arguments are an array instead of a list. Hedgehog 2.0.4 has Gen.sequence for sequences and Gen.sequenceList for lists, both over one traversal, and nothing for arrays, so Prelude adds Gen.sequenceArray over Gen.sequence; the values shrink as they did through Gen.sequenceList. Type.getAllAttributes returns an array, and the two parts of a NonZeroInt range are an array. - Collections are joined in array and sequence expressions instead of Array.append and Seq.append: the arguments of an invocation, the values of the journal and the attributes that PropertyContext folds. - The types that the adapter compares other types with are in one module, KnownTypes: Void, Task, ValueTask, IAutoGenConfig and the generic definitions of ValueTask<'T>, Async<'T> and Result<'T, 'E>. typeof is one ldtoken, but typedefof calls GetGenericTypeDefinition on every use (read in the compiled IL), so each type is resolved once, and every comparison goes through the operators of System.Type instead of generic equality. - withTests, withShrinks and withSeed are functions with named parameters instead of values that hold a lambda. - Attributes are found through GetCustomAttributes<'T> and OfType<'T> instead of a type test and a cast per attribute. Two things stay upstream's, so that the two copies remain comparable: the formatting, which is why .fantomasignore keeps the project, and nullness checking, which is off as in every upstream project. The header of every changed file records the change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
4 of 6 tasks
xperiandri
force-pushed
the
mstest-adapter
branch
from
October 10, 2026 12:30
4b01490 to
3f98d5a
Compare
xperiandri
added a commit
to fsprojects/FSharp.Azure.Cosmos
that referenced
this pull request
Oct 10, 2026
The adapter was ported in upstream's style, because the same code was to go into the upstream pull request. That pull request now exists, hedgehogqa/fsharp-hedgehog#487, with its own copy in upstream's style, so the copy in this repository follows the coding guidelines of this repository, as the review of #43 asks: - A setting that is not given is a voption instead of an option: the members of IPropertyAttribute, the fields of PropertyContext, the private constructors and fields of PropertyAttribute and PropertiesAttribute, the generic arguments that AutoGenConfig infers and the generator that a GenAttribute gives. - The project compiles src/Shared/ValueCollections.fs again, like every other F# project, and uses its Seq.tryHead, Seq.tryFind, Seq.tryExactlyOne and Seq.zip, which return a voption or struct tuples. Prelude loses Option.requireSome and Seq.seqTryExactlyOne. - The generated arguments are an array instead of a list. Hedgehog 2.0.4 has Gen.sequence for sequences and Gen.sequenceList for lists, both over one traversal, and nothing for arrays, so Prelude adds Gen.sequenceArray over Gen.sequence; the values shrink as they did through Gen.sequenceList. Type.getAllAttributes returns an array, and the two parts of a NonZeroInt range are an array. - Collections are joined in array and sequence expressions instead of Array.append and Seq.append: the arguments of an invocation, the values of the journal and the attributes that PropertyContext folds. - The types that the adapter compares other types with are in one module, KnownTypes: Void, Task, ValueTask, IAutoGenConfig and the generic definitions of ValueTask<'T>, Async<'T> and Result<'T, 'E>. typeof is one ldtoken, but typedefof calls GetGenericTypeDefinition on every use (read in the compiled IL), so each type is resolved once, and every comparison goes through the operators of System.Type instead of generic equality. - withTests, withShrinks and withSeed are functions with named parameters instead of values that hold a lambda. - Attributes are found through GetCustomAttributes<'T> and OfType<'T> instead of a type test and a cast per attribute. Two things stay upstream's, so that the two copies remain comparable: the formatting, which is why .fantomasignore keeps the project, and nullness checking, which is off as in every upstream project. The header of every changed file records the change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
xperiandri
added a commit
to fsprojects/FSharp.Azure.Cosmos
that referenced
this pull request
Oct 10, 2026
The adapter was ported in upstream's style, because the same code was to go into the upstream pull request. That pull request now exists, hedgehogqa/fsharp-hedgehog#487, with its own copy in upstream's style, so the copy in this repository follows the coding guidelines of this repository, as the review of #43 asks: - A setting that is not given is a voption instead of an option: the members of IPropertyAttribute, the fields of PropertyContext, the private constructors and fields of PropertyAttribute and PropertiesAttribute, the generic arguments that AutoGenConfig infers and the generator that a GenAttribute gives. - The project compiles src/Shared/ValueCollections.fs again, like every other F# project, and uses its Seq.tryHead, Seq.tryFind, Seq.tryExactlyOne and Seq.zip, which return a voption or struct tuples. Prelude loses Option.requireSome and Seq.seqTryExactlyOne. - The generated arguments are an array instead of a list. Hedgehog 2.0.4 has Gen.sequence for sequences and Gen.sequenceList for lists, both over one traversal, and nothing for arrays, so Prelude adds Gen.sequenceArray over Gen.sequence; the values shrink as they did through Gen.sequenceList. Type.getAllAttributes returns an array, and the two parts of a NonZeroInt range are an array. - Collections are joined in array and sequence expressions instead of Array.append and Seq.append: the arguments of an invocation, the values of the journal and the attributes that PropertyContext folds. - The types that the adapter compares other types with are in one module, KnownTypes: Void, Task, ValueTask, IAutoGenConfig and the generic definitions of ValueTask<'T>, Async<'T> and Result<'T, 'E>. typeof is one ldtoken, but typedefof calls GetGenericTypeDefinition on every use (read in the compiled IL), so each type is resolved once, and every comparison goes through the operators of System.Type instead of generic equality. - withTests, withShrinks and withSeed are functions with named parameters instead of values that hold a lambda. - Attributes are found through GetCustomAttributes<'T> and OfType<'T> instead of a type test and a cast per attribute. Two things stay upstream's, so that the two copies remain comparable: the formatting, which is why .fantomasignore keeps the project, and nullness checking, which is off as in every upstream project. The header of every changed file records the change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
xperiandri
added a commit
to fsprojects/FSharp.Azure.Cosmos
that referenced
this pull request
Oct 10, 2026
The adapter was ported in upstream's style, because the same code was to go into the upstream pull request. That pull request now exists, hedgehogqa/fsharp-hedgehog#487, with its own copy in upstream's style, so the copy in this repository follows the coding guidelines of this repository, as the review of #43 asks: - A setting that is not given is a voption instead of an option: the members of IPropertyAttribute, the fields of PropertyContext, the private constructors and fields of PropertyAttribute and PropertiesAttribute, the generic arguments that AutoGenConfig infers and the generator that a GenAttribute gives. - The project compiles src/Shared/ValueCollections.fs again, like every other F# project, and uses its Seq.tryHead, Seq.tryFind, Seq.tryExactlyOne and Seq.zip, which return a voption or struct tuples. Prelude loses Option.requireSome and Seq.seqTryExactlyOne. - The generated arguments are an array instead of a list. Hedgehog 2.0.4 has Gen.sequence for sequences and Gen.sequenceList for lists, both over one traversal, and nothing for arrays, so Prelude adds Gen.sequenceArray over Gen.sequence; the values shrink as they did through Gen.sequenceList. Type.getAllAttributes returns an array, and the two parts of a NonZeroInt range are an array. - Collections are joined in array and sequence expressions instead of Array.append and Seq.append: the arguments of an invocation, the values of the journal and the attributes that PropertyContext folds. - The types that the adapter compares other types with are in one module, KnownTypes: Void, Task, ValueTask, IAutoGenConfig and the generic definitions of ValueTask<'T>, Async<'T> and Result<'T, 'E>. typeof is one ldtoken, but typedefof calls GetGenericTypeDefinition on every use (read in the compiled IL), so each type is resolved once, and every comparison goes through the operators of System.Type instead of generic equality. - withTests, withShrinks and withSeed are functions with named parameters instead of values that hold a lambda. - Attributes are found through GetCustomAttributes<'T> and OfType<'T> instead of a type test and a cast per attribute. - The error for an AutoGenConfig type without exactly one config member says "member" where upstream says "property": a method that takes AutoGenConfigArgs is accepted as well (automatic review of #50). Two things stay upstream's, so that the two copies remain comparable: the formatting, which is why .fantomasignore keeps the project, and nullness checking, which is off as in every upstream project. The header of every changed file records the change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The sources that do not depend on the test framework are copied from Hedgehog.NUnit with only the namespace renamed, so that the following commits show what MSTest changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PropertyAttribute derives from TestMethodAttribute and overrides ExecuteAsync, which MSTest calls instead of invoking the test method once. Hedgehog generates the arguments, every case and every shrink step is one ITestMethod.InvokeAsync call, and the run is folded into a single TestResult whose message is Hedgehog's report, followed by a Recheck attribute that replays the failure. What MSTest makes different from Hedgehog.NUnit: - MSTest runs only test methods that return void, Task or ValueTask, so the handling of bool, Property, Result, Async and Task<'T> results is gone and a property fails by throwing. A generic method gets an error result instead of arguments that are all null. - The values of a DataRow or DynamicData row are the leading arguments, and only the parameters after them are generated. - MSTest creates the test class and runs TestInitialize and TestCleanup for every invocation. Each node of the shrink tree is therefore invoked once: the property runner evaluates an asynchronous result again whenever it unwraps it, which would run the counterexample twice. - An invocation that calls Assert.Inconclusive is discarded. - The run stops without shrinking once the cancellation token of the TestContext is cancelled, by a Timeout or by the test run. The token is checked before every invocation and after it, whatever the outcome: MSTest reports a test method that returns without observing its cancelled token as passed. - An invocation whose outcome is none of Passed, Failed, Inconclusive and Timeout, such as Error, was not run as a test by MSTest. The run stops at it without shrinking, and the property reports the outcome and the exception of that invocation instead of a counterexample. - MSTest runs a property inherited from a base class once per derived test class, so the class-level settings come from the reflected type of the method, and those of a derived class win over its base's. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The generator of a parameter was found only when the attribute derived from GenAttribute<'a> directly: an attribute derived from another generator attribute, such as one derived from IntAttribute, was overlooked and the parameter was auto-generated instead. A non-generic base GenAttribute declares Box, so the attribute is found by a type test wherever it sits in the inheritance chain. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A seed on the attribute, passed on through PropertyConfig.withSeed, makes every run of a property generate the same cases, for example one seed per suite on its Properties attribute. Without it every run draws a random seed, as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
NonZeroInt splits a range that contains 0 into the part below it and the part above it. With 0 as the lowest or the highest value, one of the parts is empty, and its inverted range still generated values: NonZeroInt(0, 5) gave -1 and 0, NonZeroInt(-5, 0) gave 0 and 1. Such a range now skips the 0 at its end, and the range from 0 to 0, which holds no non-zero value, is rejected. Hedgehog.NUnit and Hedgehog.Xunit have the same code. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…eters The type arguments of a generic config method were the types of the AutoGenConfigArgs whose parameter type is itself a generic parameter, one per such parameter. That fails when one generic parameter types two parameters, and when it is nested in a parameter type such as 'a[]. The types of the arguments are now unified with the parameter types, through arrays and generic types, and a generic parameter that gets two different types, or none, is reported by name. Hedgehog.NUnit and Hedgehog.Xunit have the same code. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The suites of Hedgehog.Xunit and Hedgehog.NUnit rewritten for MSTest, without the cases MSTest cannot run (properties that return bool, Result, Async, Task<'T> or a Property, module-level and generic properties), plus tests of what the MSTest adapter adds or does differently: one result per property, the lifecycle of every case, failure reports and their replay, Seed, give-ups, Assert.Inconclusive, rejected methods, data rows, settings of inherited properties, generator attributes in an inheritance chain, memoised invocations, cancellation, timeouts and invocations that MSTest could not run. A property that fails by design lives in a class without TestClass and runs through the adapter's entry point with an invoker of the suite's own, so that its result is asserted instead of failing the suite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An MSTest section next to xUnit and NUnit, with a note on the Property attribute of Hedgehog.MSTest. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DateOnlyAttribute and TimeOnlyAttribute set the generator of one parameter from a minimum to a maximum, both included. Without arguments DateOnly generates within the 3650 days from 2000-01-01, the dates of DateTimeAttribute's default range, and TimeOnly any time of day. An attribute argument must be a constant, so it cannot be a DateOnly or a TimeOnly. Both attributes therefore also take their range as integers, [<DateOnly(2024, 1, 1, 2024, 12, 31)>] and [<TimeOnly(9, 0, 17, 0)>]; the constructor that takes the two values serves attributes that derive from these. Gen.auto cannot generate these two types: it passes arbitrary integers to their constructors, which throw ArgumentOutOfRangeException. The attributes therefore generate over the day number and the ticks of the value themselves, so that they do not depend on a change of the core. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Odd sets the lowest bit of a value generated from min to max, and Even clears it, so an even upper bound of Odd is exceeded by one and an odd lower bound of Even is undercut by one: OddAttribute(-1, 0) can give 1, and EvenAttribute(1, 2) can give 0. The range is now narrowed to its first and last odd or even value before a value is generated, in 64-bit arithmetic so that the neighbour of Int32.MinValue or Int32.MaxValue does not overflow, and a range without such a value, such as OddAttribute(0, 0), is rejected. Hedgehog.NUnit and Hedgehog.Xunit have the same code. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
xperiandri
force-pushed
the
mstest-adapter
branch
from
October 10, 2026 14:18
c4ee44e to
a01f7b6
Compare
xperiandri
added a commit
to fsprojects/FSharp.Azure.Cosmos
that referenced
this pull request
Oct 10, 2026
The adapter was ported in upstream's style, because the same code was to go into the upstream pull request. That pull request now exists, hedgehogqa/fsharp-hedgehog#487, with its own copy in upstream's style, so the copy in this repository follows the coding guidelines of this repository, as the review of #43 asks: - A setting that is not given is a voption instead of an option: the members of IPropertyAttribute, the fields of PropertyContext, the private constructors and fields of PropertyAttribute and PropertiesAttribute, the generic arguments that AutoGenConfig infers and the generator that a GenAttribute gives. - The project compiles src/Shared/ValueCollections.fs again, like every other F# project, and uses its Seq.tryHead, Seq.tryFind, Seq.tryExactlyOne and Seq.zip, which return a voption or struct tuples. Prelude loses Option.requireSome and Seq.seqTryExactlyOne. - The generated arguments are an array instead of a list. Hedgehog 2.0.4 has Gen.sequence for sequences and Gen.sequenceList for lists, both over one traversal, and nothing for arrays, so Prelude adds Gen.sequenceArray over Gen.sequence; the values shrink as they did through Gen.sequenceList. Type.getAllAttributes returns an array, and the two parts of a NonZeroInt range are an array. - Collections are joined in array and sequence expressions instead of Array.append and Seq.append: the arguments of an invocation, the values of the journal and the attributes that PropertyContext folds. - The types that the adapter compares other types with are in one module, KnownTypes: Void, Task, ValueTask, IAutoGenConfig and the generic definitions of ValueTask<'T>, Async<'T> and Result<'T, 'E>. typeof is one ldtoken, but typedefof calls GetGenericTypeDefinition on every use (read in the compiled IL), so each type is resolved once, and every comparison goes through the operators of System.Type instead of generic equality. - withTests, withShrinks and withSeed are functions with named parameters instead of values that hold a lambda. - Attributes are found through GetCustomAttributes<'T> and OfType<'T> instead of a type test and a cast per attribute. - The error for an AutoGenConfig type without exactly one config member says "member" where upstream says "property": a method that takes AutoGenConfigArgs is accepted as well (automatic review of #50). Two things stay upstream's, so that the two copies remain comparable: the formatting, which is why .fantomasignore keeps the project, and nullness checking, which is off as in every upstream project. The header of every changed file records the change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
xperiandri
added a commit
to fsprojects/FSharp.Azure.Cosmos
that referenced
this pull request
Oct 10, 2026
The Hedgehog MSTest adapter of #43 is proposed upstream as hedgehogqa/fsharp-hedgehog#487. The record notes how the proposal differs from the in-repository copy (net8.0 and F# 8, upstream's documentation in the shared files, VSTest), that MSTest 4.2 is the adapter's lowest version, and that one test of #43 stops compiling on MSTest 4.5.1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
xperiandri
added a commit
to fsprojects/FSharp.Azure.Cosmos
that referenced
this pull request
Oct 10, 2026
The adapter was ported in upstream's style, because the same code was to go into the upstream pull request. That pull request now exists, hedgehogqa/fsharp-hedgehog#487, with its own copy in upstream's style, so the copy in this repository follows the coding guidelines of this repository, its formatting and nullness checking included, as the review of #43 asks: - A setting that is not given is a voption instead of an option: the members of IPropertyAttribute, the fields of PropertyContext, the private constructors and fields of PropertyAttribute and PropertiesAttribute, the generic arguments that AutoGenConfig infers and the generator that a GenAttribute gives. - The project compiles src/Shared/ValueCollections.fs again, like every other F# project, and uses its Seq.tryHead, Seq.tryFind, Seq.tryExactlyOne and Seq.zip, which return a voption or struct tuples. The three files that use them open FSharp.Azure.Cosmos, the namespace of these functions. Prelude loses Option.requireSome and Seq.seqTryExactlyOne. - The generated arguments are an array instead of a list. Hedgehog 2.0.4 has Gen.sequence for sequences and Gen.sequenceList for lists, both over one traversal, and nothing for arrays, so Prelude adds Gen.sequenceArray over Gen.sequence; the values shrink as they did through Gen.sequenceList. Type.getAllAttributes returns an array, and the two parts of a NonZeroInt range are an array. - Collections are joined in array and sequence expressions instead of Array.append and Seq.append: the arguments of an invocation, the values of the journal and the attributes that PropertyContext folds. - The types that the adapter compares other types with are in one module, KnownTypes: Void, Task, ValueTask, IAutoGenConfig and the generic definitions of ValueTask<'T>, Async<'T> and Result<'T, 'E>. typeof is one ldtoken, but typedefof calls GetGenericTypeDefinition on every use (read in the compiled IL), so each type is resolved once, and every comparison goes through the operators of System.Type instead of generic equality. - withTests, withShrinks and withSeed are functions with named parameters instead of values that hold a lambda. - Attributes are found through GetCustomAttributes<'T> and OfType<'T> instead of a type test and a cast per attribute. - The error for an AutoGenConfig type without exactly one config member says "member" where upstream says "property": a method that takes AutoGenConfigArgs is accepted as well (automatic review of #50). - The project is formatted with Fantomas: .fantomasignore no longer lists it, so the Fantomas run of the build covers it. - Nullness checking is on, as everywhere in the repository. A data row and the arguments of an invocation or of a config method may hold null, so they are arrays of objnull. A null array is checked for where it enters: in the setters of AutoGenConfigArgs, which take it as no arguments, and at the entry point that receives the data row from MSTest. Nested exceptions and base types are matched on null instead of tested with isNull, reflection lookups that cannot fail are asserted with nonNull, and a config method that returns no IAutoGenConfig is an error. One call keeps a scoped #nowarn "3261", in a helper of its own: ITestMethod.InvokeAsync declares the elements of its arguments as non-null, although MSTest itself passes it ITestMethod.Arguments, whose elements it declares as nullable. - The pipeline that builds the journal ends in Seq.toArray, so that it stays in one module. The header of every changed file records the change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
3 of 6 tasks
xperiandri
added a commit
to fsprojects/FSharp.Azure.Cosmos
that referenced
this pull request
Oct 10, 2026
The adapter was ported in upstream's style, because the same code was to go into the upstream pull request. That pull request now exists, hedgehogqa/fsharp-hedgehog#487, with its own copy in upstream's style, so the copy in this repository follows the coding guidelines of this repository, its formatting and nullness checking included, as the review of #43 asks: - A setting that is not given is a voption instead of an option: the members of IPropertyAttribute, the fields of PropertyContext, the private constructors and fields of PropertyAttribute and PropertiesAttribute, the generic arguments that AutoGenConfig infers and the generator that a GenAttribute gives. - The project compiles src/Shared/ValueCollections.fs again, like every other F# project, and uses its Seq.tryHead, Seq.tryFind, Seq.tryExactlyOne and Seq.zip, which return a voption or struct tuples. The three files that use them open FSharp.Azure.Cosmos, the namespace of these functions. Prelude loses Option.requireSome and Seq.seqTryExactlyOne. - The generated arguments are an array instead of a list. Hedgehog 2.0.4 has Gen.sequence for sequences and Gen.sequenceList for lists, both over one traversal, and nothing for arrays, so Prelude adds Gen.sequenceArray over Gen.sequence; the values shrink as they did through Gen.sequenceList. Type.getAllAttributes returns an array, and the two parts of a NonZeroInt range are an array. - Collections are joined in array and sequence expressions instead of Array.append and Seq.append: the arguments of an invocation, the values of the journal and the attributes that PropertyContext folds. - The types that the adapter compares other types with are in one module, KnownTypes: Void, Task, ValueTask, IAutoGenConfig and the generic definitions of ValueTask<'T>, Async<'T> and Result<'T, 'E>. typeof is one ldtoken, but typedefof calls GetGenericTypeDefinition on every use (read in the compiled IL), so each type is resolved once, and every comparison goes through the operators of System.Type instead of generic equality. - withTests, withShrinks and withSeed are functions with named parameters instead of values that hold a lambda. - Attributes are found through GetCustomAttributes<'T> and OfType<'T> instead of a type test and a cast per attribute. - The error for an AutoGenConfig type without exactly one config member says "member" where upstream says "property": a method that takes AutoGenConfigArgs is accepted as well (automatic review of #50). - The project is formatted with Fantomas: .fantomasignore no longer lists it, so the Fantomas run of the build covers it. - Nullness checking is on, as everywhere in the repository. A data row and the arguments of an invocation or of a config method may hold null, so they are arrays of objnull. A null array is checked for where it enters: in the setters of AutoGenConfigArgs, which take it as no arguments, and at the entry point that receives the data row from MSTest. Nested exceptions and base types are matched on null instead of tested with isNull, reflection lookups that cannot fail are asserted with nonNull, and a config method that returns no IAutoGenConfig is an error. One call keeps a scoped #nowarn "3261", in a helper of its own: ITestMethod.InvokeAsync declares the elements of its arguments as non-null, although MSTest itself passes it ITestMethod.Arguments, whose elements it declares as nullable. - The pipeline that builds the journal ends in Seq.toArray, so that it stays in one module. The header of every changed file records the change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
xperiandri
added a commit
to fsprojects/FSharp.Azure.Cosmos
that referenced
this pull request
Oct 10, 2026
The Hedgehog MSTest adapter of #43 is proposed upstream as hedgehogqa/fsharp-hedgehog#487. The record notes how the proposal differs from the in-repository copy (net8.0 and F# 8, upstream's documentation in the shared files, VSTest), that MSTest 4.2 is the adapter's lowest version, and that one test of #43 stops compiling on MSTest 4.5.1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
xperiandri
added a commit
to fsprojects/FSharp.Azure.Cosmos
that referenced
this pull request
Oct 11, 2026
The adapter was ported in upstream's style, because the same code was to go into the upstream pull request. That pull request now exists, hedgehogqa/fsharp-hedgehog#487, with its own copy in upstream's style, so the copy in this repository follows the coding guidelines of this repository, its formatting and nullness checking included, as the review of #43 asks: - A setting that is not given is a voption instead of an option: the members of IPropertyAttribute, the fields of PropertyContext, the private constructors and fields of PropertyAttribute and PropertiesAttribute, the generic arguments that AutoGenConfig infers and the generator that a GenAttribute gives. - The project compiles src/Shared/ValueCollections.fs again, like every other F# project, and uses its Seq.tryHead, Seq.tryFind, Seq.tryExactlyOne and Seq.zip, which return a voption or struct tuples. The three files that use them open FSharp.Azure.Cosmos, the namespace of these functions. Prelude loses Option.requireSome and Seq.seqTryExactlyOne. - The generated arguments are an array instead of a list. Hedgehog 2.0.4 has Gen.sequence for sequences and Gen.sequenceList for lists, both over one traversal, and nothing for arrays, so Prelude adds Gen.sequenceArray over Gen.sequence; the values shrink as they did through Gen.sequenceList. Type.getAllAttributes returns an array, and the two parts of a NonZeroInt range are an array. - Collections are joined in array and sequence expressions instead of Array.append and Seq.append: the arguments of an invocation, the values of the journal and the attributes that PropertyContext folds. - The types that the adapter compares other types with are in one module, KnownTypes: Void, Task, ValueTask, IAutoGenConfig and the generic definitions of ValueTask<'T>, Async<'T> and Result<'T, 'E>. typeof is one ldtoken, but typedefof calls GetGenericTypeDefinition on every use (read in the compiled IL), so each type is resolved once, and every comparison goes through the operators of System.Type instead of generic equality. - withTests, withShrinks and withSeed are functions with named parameters instead of values that hold a lambda. - Attributes are found through GetCustomAttributes<'T> and OfType<'T> instead of a type test and a cast per attribute. - The error for an AutoGenConfig type without exactly one config member says "member" where upstream says "property": a method that takes AutoGenConfigArgs is accepted as well (automatic review of #50). - The project is formatted with Fantomas: .fantomasignore no longer lists it, so the Fantomas run of the build covers it. - Nullness checking is on, as everywhere in the repository. A data row and the arguments of an invocation or of a config method may hold null, so they are arrays of objnull. A null array is checked for where it enters: in the setters of AutoGenConfigArgs, which take it as no arguments, and at the entry point that receives the data row from MSTest. Nested exceptions and base types are matched on null instead of tested with isNull, reflection lookups that cannot fail are asserted with nonNull, and a config method that returns no IAutoGenConfig is an error. One call keeps a scoped #nowarn "3261", in a helper of its own: ITestMethod.InvokeAsync declares the elements of its arguments as non-null, although MSTest itself passes it ITestMethod.Arguments, whose elements it declares as nullable. - The pipeline that builds the journal ends in Seq.toArray, so that it stays in one module. The header of every changed file records the change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
xperiandri
added a commit
to fsprojects/FSharp.Azure.Cosmos
that referenced
this pull request
Oct 11, 2026
The Hedgehog MSTest adapter of #43 is proposed upstream as hedgehogqa/fsharp-hedgehog#487. The record notes how the proposal differs from the in-repository copy (net8.0 and F# 8, upstream's documentation in the shared files, VSTest), that MSTest 4.2 is the adapter's lowest version, and that one test of #43 stops compiling on MSTest 4.5.1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The attributes are meant for C# as well: their settings have getters only so that C# accepts them as named arguments. The F# tests do not show that a C# test project compiles against them and that MSTest discovers and runs what it declares. Hedgehog.MSTest.Tests.CSharp does, after the model of Hedgehog.Xunit.Tests.CSharp: the generator attributes, the settings of Property and Properties as named arguments and as constructor arguments, an AutoGenConfig with and without arguments, a GenAttribute declared in C#, data rows before the generated arguments, Recheck, and properties that return Task and ValueTask. The number of cases that a setting asks for is counted and checked when the class is cleaned up. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
xperiandri
added a commit
to fsprojects/FSharp.Azure.Cosmos
that referenced
this pull request
Oct 11, 2026
The adapter was ported in upstream's style, because the same code was to go into the upstream pull request. That pull request now exists, hedgehogqa/fsharp-hedgehog#487, with its own copy in upstream's style, so the copy in this repository follows the coding guidelines of this repository, its formatting and nullness checking included, as the review of #43 asks: - A setting that is not given is a voption instead of an option: the members of IPropertyAttribute, the fields of PropertyContext, the private constructors and fields of PropertyAttribute and PropertiesAttribute, the generic arguments that AutoGenConfig infers and the generator that a GenAttribute gives. - The project compiles src/Shared/ValueCollections.fs again, like every other F# project, and uses its Seq.tryHead, Seq.tryFind, Seq.tryExactlyOne and Seq.zip, which return a voption or struct tuples. The three files that use them open FSharp.Azure.Cosmos, the namespace of these functions. Prelude loses Option.requireSome and Seq.seqTryExactlyOne. - The generated arguments are an array instead of a list. Hedgehog 2.0.4 has Gen.sequence for sequences and Gen.sequenceList for lists, both over one traversal, and nothing for arrays, so Prelude adds Gen.sequenceArray over Gen.sequence; the values shrink as they did through Gen.sequenceList. Type.getAllAttributes returns an array, and the two parts of a NonZeroInt range are an array. - Collections are joined in array and sequence expressions instead of Array.append and Seq.append: the arguments of an invocation, the values of the journal and the attributes that PropertyContext folds. - The types that the adapter compares other types with are in one module, KnownTypes: Void, Task, ValueTask, IAutoGenConfig and the generic definitions of ValueTask<'T>, Async<'T> and Result<'T, 'E>. typeof is one ldtoken, but typedefof calls GetGenericTypeDefinition on every use (read in the compiled IL), so each type is resolved once, and every comparison goes through the operators of System.Type instead of generic equality. - withTests, withShrinks and withSeed are functions with named parameters instead of values that hold a lambda. - Attributes are found through GetCustomAttributes<'T> and OfType<'T> instead of a type test and a cast per attribute. - The error for an AutoGenConfig type without exactly one config member says "member" where upstream says "property": a method that takes AutoGenConfigArgs is accepted as well (automatic review of #50). - The project is formatted with Fantomas: .fantomasignore no longer lists it, so the Fantomas run of the build covers it. - Nullness checking is on, as everywhere in the repository. A data row and the arguments of an invocation or of a config method may hold null, so they are arrays of objnull. A null array is checked for where it enters: in the setters of AutoGenConfigArgs, which take it as no arguments, and at the entry point that receives the data row from MSTest. Nested exceptions and base types are matched on null instead of tested with isNull, reflection lookups that cannot fail are asserted with nonNull, and a config method that returns no IAutoGenConfig is an error. One call keeps a scoped #nowarn "3261", in a helper of its own: ITestMethod.InvokeAsync declares the elements of its arguments as non-null, although MSTest itself passes it ITestMethod.Arguments, whose elements it declares as nullable. - The pipeline that builds the journal ends in Seq.toArray, so that it stays in one module. The header of every changed file records the change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
AlexeyRaga
approved these changes
Oct 12, 2026
xperiandri
added a commit
to fsprojects/FSharp.Azure.Cosmos
that referenced
this pull request
Oct 13, 2026
The adapter was ported in upstream's style, because the same code was to go into the upstream pull request. That pull request now exists, hedgehogqa/fsharp-hedgehog#487, with its own copy in upstream's style, so the copy in this repository follows the coding guidelines of this repository, its formatting and nullness checking included, as the review of #43 asks: - A setting that is not given is a voption instead of an option: the members of IPropertyAttribute, the fields of PropertyContext, the private constructors and fields of PropertyAttribute and PropertiesAttribute, the generic arguments that AutoGenConfig infers and the generator that a GenAttribute gives. - The project compiles src/Shared/ValueCollections.fs again, like every other F# project, and uses its Seq.tryHead, Seq.tryFind, Seq.tryExactlyOne and Seq.zip, which return a voption or struct tuples. The three files that use them open FSharp.Azure.Cosmos, the namespace of these functions. Prelude loses Option.requireSome and Seq.seqTryExactlyOne. - The generated arguments are an array instead of a list. Hedgehog 2.0.4 has Gen.sequence for sequences and Gen.sequenceList for lists, both over one traversal, and nothing for arrays, so Prelude adds Gen.sequenceArray over Gen.sequence; the values shrink as they did through Gen.sequenceList. Type.getAllAttributes returns an array, and the two parts of a NonZeroInt range are an array. - Collections are joined in array and sequence expressions instead of Array.append and Seq.append: the arguments of an invocation, the values of the journal and the attributes that PropertyContext folds. - The types that the adapter compares other types with are in one module, KnownTypes: Void, Task, ValueTask, IAutoGenConfig and the generic definitions of ValueTask<'T>, Async<'T> and Result<'T, 'E>. typeof is one ldtoken, but typedefof calls GetGenericTypeDefinition on every use (read in the compiled IL), so each type is resolved once, and every comparison goes through the operators of System.Type instead of generic equality. - withTests, withShrinks and withSeed are functions with named parameters instead of values that hold a lambda. - Attributes are found through GetCustomAttributes<'T> and OfType<'T> instead of a type test and a cast per attribute. - The error for an AutoGenConfig type without exactly one config member says "member" where upstream says "property": a method that takes AutoGenConfigArgs is accepted as well (automatic review of #50). - The project is formatted with Fantomas: .fantomasignore no longer lists it, so the Fantomas run of the build covers it. - Nullness checking is on, as everywhere in the repository. A data row and the arguments of an invocation or of a config method may hold null, so they are arrays of objnull. A null array is checked for where it enters: in the setters of AutoGenConfigArgs, which take it as no arguments, and at the entry point that receives the data row from MSTest. Nested exceptions and base types are matched on null instead of tested with isNull, reflection lookups that cannot fail are asserted with nonNull, and a config method that returns no IAutoGenConfig is an error. One call keeps a scoped #nowarn "3261", in a helper of its own: ITestMethod.InvokeAsync declares the elements of its arguments as non-null, although MSTest itself passes it ITestMethod.Arguments, whose elements it declares as nullable. - The pipeline that builds the journal ends in Seq.toArray, so that it stays in one module. The header of every changed file records the change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
xperiandri
added a commit
to fsprojects/FSharp.Azure.Cosmos
that referenced
this pull request
Oct 13, 2026
Hedgehog.MSTest 1.0.0, the MSTest adapter of Hedgehog (hedgehogqa/fsharp-hedgehog#487), is released, so a test project references the package. Hedgehog 2.0.4 is the version that it depends on. Both get a central version, and the coverage report leaves out their assemblies, which a property test loads. The agent instructions list the two packages and say how a property test is written: a method of a [<TestClass>] type marked [<Property>], with generated parameters, never Property.check inside a [<TestMethod>]; an instance method that returns unit, Task or ValueTask and fails by throwing, because MSTest 4 rejects static methods and other return types and one such method fails the discovery of the whole assembly, which --list-tests shows before a run; that MSTest runs the test class and its [<TestInitialize>] and [<TestCleanup>] for every generated case and every shrink step; and that a failure reports a [<Recheck>] that replays it. They also say that Hedgehog 2.0.4 cannot generate DateOnly and TimeOnly, which hedgehogqa/fsharp-hedgehog#488 has merged and not released, so a parameter of such a type needs a generator of its own. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
xperiandri
added a commit
to fsprojects/FSharp.Azure.Cosmos
that referenced
this pull request
Oct 13, 2026
hedgehogqa/fsharp-hedgehog#487 is merged and Hedgehog.MSTest 1.0.0 is on NuGet, so the repository does not carry a copy of the adapter: the pull request that ported it (#43) is closed, the two adapter projects are not added, and the test projects reference the package. The record says which commits of #43 live on in #50 and #54, what the property tests of the stack run on now, and that Hedgehog 2.0.4 still cannot generate DateOnly and TimeOnly. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
xperiandri
added a commit
to fsprojects/FSharp.Azure.Cosmos
that referenced
this pull request
Oct 14, 2026
hedgehogqa/fsharp-hedgehog#487 is merged and Hedgehog.MSTest 1.0.0 is on NuGet, so the repository does not carry a copy of the adapter: the pull request that ported it (#43) is closed, the two adapter projects are not added, and the test projects reference the package. The record says which commits of #43 live on in #50 and #54, what the property tests of the stack run on now, and that Hedgehog 2.0.4 still cannot generate DateOnly and TimeOnly. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
xperiandri
added a commit
to fsprojects/FSharp.Azure.Cosmos
that referenced
this pull request
Oct 14, 2026
hedgehogqa/fsharp-hedgehog#487 is merged and Hedgehog.MSTest 1.0.0 is on NuGet, so the repository does not carry a copy of the adapter: the pull request that ported it (#43) is closed, the two adapter projects are not added, and the test projects reference the package. The record says which commits of #43 live on in #50 and #54, what the property tests of the stack run on now, and that Hedgehog 2.0.4 still cannot generate DateOnly and TimeOnly. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
xperiandri
added a commit
to fsprojects/FSharp.Azure.Cosmos
that referenced
this pull request
Oct 14, 2026
hedgehogqa/fsharp-hedgehog#487 is merged and Hedgehog.MSTest 1.0.0 is on NuGet, so the repository does not carry a copy of the adapter: the pull request that ported it (#43) is closed, the two adapter projects are not added, and the test projects reference the package. The record says which commits of #43 live on in #50 and #54, what the property tests of the stack run on now, and that Hedgehog 2.0.4 still cannot generate DateOnly and TimeOnly. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
xperiandri
added a commit
to fsprojects/FSharp.Azure.Cosmos
that referenced
this pull request
Oct 14, 2026
hedgehogqa/fsharp-hedgehog#487 is merged and Hedgehog.MSTest 1.0.0 is on NuGet, so the repository does not carry a copy of the adapter: the pull request that ported it (#43) is closed, the two adapter projects are not added, and the test projects reference the package. The record says which commits of #43 live on in #50 and #54, what the property tests of the stack run on now, and that Hedgehog 2.0.4 still cannot generate DateOnly and TimeOnly. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
xperiandri
added a commit
to fsprojects/FSharp.Azure.Cosmos
that referenced
this pull request
Oct 17, 2026
The Hedgehog MSTest adapter of #43 is proposed upstream as hedgehogqa/fsharp-hedgehog#487. The record notes how the proposal differs from the in-repository copy (net8.0 and F# 8, upstream's documentation in the shared files, VSTest), that MSTest 4.2 is the adapter's lowest version, and that one test of #43 stops compiling on MSTest 4.5.1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
xperiandri
added a commit
to fsprojects/FSharp.Azure.Cosmos
that referenced
this pull request
Oct 17, 2026
hedgehogqa/fsharp-hedgehog#487 is merged and Hedgehog.MSTest 1.0.0 is on NuGet, so the repository does not carry a copy of the adapter: the pull request that ported it (#43) is closed, the two adapter projects are not added, and the test projects reference the package. The record says which commits of #43 live on in #50 and #54, what the property tests of the stack run on now, and that Hedgehog 2.0.4 still cannot generate DateOnly and TimeOnly. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
xperiandri
added a commit
to fsprojects/FSharp.Azure.Cosmos
that referenced
this pull request
Oct 17, 2026
The Hedgehog MSTest adapter of #43 is proposed upstream as hedgehogqa/fsharp-hedgehog#487. The record notes how the proposal differs from the in-repository copy (net8.0 and F# 8, upstream's documentation in the shared files, VSTest), that MSTest 4.2 is the adapter's lowest version, and that one test of #43 stops compiling on MSTest 4.5.1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
xperiandri
added a commit
to fsprojects/FSharp.Azure.Cosmos
that referenced
this pull request
Oct 17, 2026
hedgehogqa/fsharp-hedgehog#487 is merged and Hedgehog.MSTest 1.0.0 is on NuGet, so the repository does not carry a copy of the adapter: the pull request that ported it (#43) is closed, the two adapter projects are not added, and the test projects reference the package. The record says which commits of #43 live on in #50 and #54, what the property tests of the stack run on now, and that Hedgehog 2.0.4 still cannot generate DateOnly and TimeOnly. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
xperiandri
added a commit
to fsprojects/FSharp.Azure.Cosmos
that referenced
this pull request
Oct 18, 2026
The Hedgehog MSTest adapter of #43 is proposed upstream as hedgehogqa/fsharp-hedgehog#487. The record notes how the proposal differs from the in-repository copy (net8.0 and F# 8, upstream's documentation in the shared files, VSTest), that MSTest 4.2 is the adapter's lowest version, and that one test of #43 stops compiling on MSTest 4.5.1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
xperiandri
added a commit
to fsprojects/FSharp.Azure.Cosmos
that referenced
this pull request
Oct 18, 2026
hedgehogqa/fsharp-hedgehog#487 is merged and Hedgehog.MSTest 1.0.0 is on NuGet, so the repository does not carry a copy of the adapter: the pull request that ported it (#43) is closed, the two adapter projects are not added, and the test projects reference the package. The record says which commits of #43 live on in #50 and #54, what the property tests of the stack run on now, and that Hedgehog 2.0.4 still cannot generate DateOnly and TimeOnly. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
xperiandri
added a commit
to fsprojects/FSharp.Azure.Cosmos
that referenced
this pull request
Oct 18, 2026
The Hedgehog MSTest adapter of #43 is proposed upstream as hedgehogqa/fsharp-hedgehog#487. The record notes how the proposal differs from the in-repository copy (net8.0 and F# 8, upstream's documentation in the shared files, VSTest), that MSTest 4.2 is the adapter's lowest version, and that one test of #43 stops compiling on MSTest 4.5.1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
xperiandri
added a commit
to fsprojects/FSharp.Azure.Cosmos
that referenced
this pull request
Oct 18, 2026
hedgehogqa/fsharp-hedgehog#487 is merged and Hedgehog.MSTest 1.0.0 is on NuGet, so the repository does not carry a copy of the adapter: the pull request that ported it (#43) is closed, the two adapter projects are not added, and the test projects reference the package. The record says which commits of #43 live on in #50 and #54, what the property tests of the stack run on now, and that Hedgehog 2.0.4 still cannot generate DateOnly and TimeOnly. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
xperiandri
added a commit
to fsprojects/FSharp.Azure.Cosmos
that referenced
this pull request
Oct 18, 2026
The Hedgehog MSTest adapter of #43 is proposed upstream as hedgehogqa/fsharp-hedgehog#487. The record notes how the proposal differs from the in-repository copy (net8.0 and F# 8, upstream's documentation in the shared files, VSTest), that MSTest 4.2 is the adapter's lowest version, and that one test of #43 stops compiling on MSTest 4.5.1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
xperiandri
added a commit
to fsprojects/FSharp.Azure.Cosmos
that referenced
this pull request
Oct 18, 2026
hedgehogqa/fsharp-hedgehog#487 is merged and Hedgehog.MSTest 1.0.0 is on NuGet, so the repository does not carry a copy of the adapter: the pull request that ported it (#43) is closed, the two adapter projects are not added, and the test projects reference the package. The record says which commits of #43 live on in #50 and #54, what the property tests of the stack run on now, and that Hedgehog 2.0.4 still cannot generate DateOnly and TimeOnly. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
xperiandri
added a commit
to fsprojects/FSharp.Azure.Cosmos
that referenced
this pull request
Oct 18, 2026
The Hedgehog MSTest adapter of #43 is proposed upstream as hedgehogqa/fsharp-hedgehog#487. The record notes how the proposal differs from the in-repository copy (net8.0 and F# 8, upstream's documentation in the shared files, VSTest), that MSTest 4.2 is the adapter's lowest version, and that one test of #43 stops compiling on MSTest 4.5.1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
xperiandri
added a commit
to fsprojects/FSharp.Azure.Cosmos
that referenced
this pull request
Oct 18, 2026
hedgehogqa/fsharp-hedgehog#487 is merged and Hedgehog.MSTest 1.0.0 is on NuGet, so the repository does not carry a copy of the adapter: the pull request that ported it (#43) is closed, the two adapter projects are not added, and the test projects reference the package. The record says which commits of #43 live on in #50 and #54, what the property tests of the stack run on now, and that Hedgehog 2.0.4 still cannot generate DateOnly and TimeOnly. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
xperiandri
added a commit
to fsprojects/FSharp.Azure.Cosmos
that referenced
this pull request
Oct 18, 2026
The Hedgehog MSTest adapter of #43 is proposed upstream as hedgehogqa/fsharp-hedgehog#487. The record notes how the proposal differs from the in-repository copy (net8.0 and F# 8, upstream's documentation in the shared files, VSTest), that MSTest 4.2 is the adapter's lowest version, and that one test of #43 stops compiling on MSTest 4.5.1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
xperiandri
added a commit
to fsprojects/FSharp.Azure.Cosmos
that referenced
this pull request
Oct 18, 2026
hedgehogqa/fsharp-hedgehog#487 is merged and Hedgehog.MSTest 1.0.0 is on NuGet, so the repository does not carry a copy of the adapter: the pull request that ported it (#43) is closed, the two adapter projects are not added, and the test projects reference the package. The record says which commits of #43 live on in #50 and #54, what the property tests of the stack run on now, and that Hedgehog 2.0.4 still cannot generate DateOnly and TimeOnly. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
xperiandri
added a commit
to fsprojects/FSharp.Azure.Cosmos
that referenced
this pull request
Oct 18, 2026
The Hedgehog MSTest adapter of #43 is proposed upstream as hedgehogqa/fsharp-hedgehog#487. The record notes how the proposal differs from the in-repository copy (net8.0 and F# 8, upstream's documentation in the shared files, VSTest), that MSTest 4.2 is the adapter's lowest version, and that one test of #43 stops compiling on MSTest 4.5.1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
xperiandri
added a commit
to fsprojects/FSharp.Azure.Cosmos
that referenced
this pull request
Oct 18, 2026
hedgehogqa/fsharp-hedgehog#487 is merged and Hedgehog.MSTest 1.0.0 is on NuGet, so the repository does not carry a copy of the adapter: the pull request that ported it (#43) is closed, the two adapter projects are not added, and the test projects reference the package. The record says which commits of #43 live on in #50 and #54, what the property tests of the stack run on now, and that Hedgehog 2.0.4 still cannot generate DateOnly and TimeOnly. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
xperiandri
added a commit
to fsprojects/FSharp.Azure.Cosmos
that referenced
this pull request
Oct 18, 2026
The Hedgehog MSTest adapter of #43 is proposed upstream as hedgehogqa/fsharp-hedgehog#487. The record notes how the proposal differs from the in-repository copy (net8.0 and F# 8, upstream's documentation in the shared files, VSTest), that MSTest 4.2 is the adapter's lowest version, and that one test of #43 stops compiling on MSTest 4.5.1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
xperiandri
added a commit
to fsprojects/FSharp.Azure.Cosmos
that referenced
this pull request
Oct 18, 2026
hedgehogqa/fsharp-hedgehog#487 is merged and Hedgehog.MSTest 1.0.0 is on NuGet, so the repository does not carry a copy of the adapter: the pull request that ported it (#43) is closed, the two adapter projects are not added, and the test projects reference the package. The record says which commits of #43 live on in #50 and #54, what the property tests of the stack run on now, and that Hedgehog 2.0.4 still cannot generate DateOnly and TimeOnly. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

This adds
Hedgehog.MSTest, a third adapter next toHedgehog.NUnitandHedgehog.Xunit. It has the same attributes –[<Property>],[<Properties>],[<Recheck>],GenAttribute<'a>and the built-in generator attributes such asInt,PositiveIntandEmail– and the same settings:AutoGenConfig,AutoGenConfigArgs,Tests,ShrinksandSize.CONTRIBUTING asks for a discussion before a significant change. I am opening this as a pull request so that the discussion has code to look at; I am happy to move it to a discussion, or to cut it down.
How it works
PropertyAttributederives from MSTest'sTestMethodAttributeand overridesExecuteAsync, which MSTest calls instead of invoking the test method once. Hedgehog generates the arguments, every case and every shrink step is oneITestMethod.InvokeAsynccall, and the whole run is reported as a singleTestResult. A failure carries Hedgehog's report, the exception and the output of the counterexample, and a[<Recheck("…")>]line to paste.The commits are meant to be read in order. The first one copies the framework-neutral files of
Hedgehog.NUnitwith only the namespace renamed, so the later ones show exactly what differs.What MSTest forces to be different
void,TaskorValueTask; one method that returns anything else fails the discovery of the whole test assembly (UTA007). So a property fails by throwing, and the handling ofbool,Property<_>,Result,AsyncandTask<'T>results is gone. For the same reason properties are instance members of[<TestClass>]types: MSTest has no module-level tests.obj, as the other two adapters do, makes every generated valuenull.InvokeAsync, so MSTest creates the test class and runsTestInitializeandTestCleanupfor every case and every shrink step, and a[<Timeout>]applies to each invocation. Because of that, each node of the shrink tree is invoked exactly once: the property runner evaluates an asynchronous result again whenever it unwraps it, which would otherwise run the counterexample, with its setup and cleanup, twice.[<Properties>]are therefore read from the class that MSTest runs (MethodInfo.ReflectedType), and those of a derived class win over its base's.TestContextis cancelled, by a timeout or by the test run, the property stops without shrinking. The token is checked before every invocation and after it, whatever its outcome, because MSTest reports a test method that returns without observing its cancelled token as passed. An attribute reaches the token only throughTestContext.Current, which MSTest 4.1 does not have yet and which is marked experimental (MSTESTEXP).InvokeAsynccan end with an outcome that is not the test method's own, such asErrororNotFound, or throw. That is not a counterexample: the property stops at it without shrinking and reports the outcome and the exception of that invocation as they are.What it adds to the other two adapters
Each of these can be ported to
Hedgehog.NUnitandHedgehog.Xunit, or dropped here if you prefer the three adapters to match:SeedonPropertyandProperties, passed on throughPropertyConfig.withSeed, so that a suite can fix its seed.[<DataRow>]or[<DynamicData>]row are the leading arguments, and only the parameters after them are generated.Assert.Inconclusivein a case discards the case, like a precondition that does not hold.GenAttributeis found through a non-generic base type. The other two adapters check only the direct base type, so an attribute derived from, say,IntAttributeis overlooked and the parameter is auto-generated.[<DateOnly>]and[<TimeOnly>], the two attributes that Generate DateOnly and TimeOnly: Gen functions, auto-generation and attributes #488 adds toHedgehog.XunitandHedgehog.NUnit. Here they generate over the day number and the ticks themselves, so that this pull request does not depend on Generate DateOnly and TimeOnly: Gen functions, auto-generation and attributes #488.Three fixes in the copied files
Each is a separate commit, and
Hedgehog.NUnitandHedgehog.Xunithave the same code. I can send the fixes for those two as a follow-up, or take them out of this pull request.NonZeroIntwith 0 as a bound of its range generates 0 and a value outside the range:NonZeroInt(0, 5)gives -1 and 0,NonZeroInt(-5, 0)gives 0 and 1.AutoGenConfigmethod does not get its type arguments fromAutoGenConfigArgswhen one generic parameter types two parameters, or when it is nested in a parameter type such as'a[].OddandEvencan leave their range by one:Odd(-1, 0)gives 1,Even(1, 2)gives 0. They now generate from the odd or even values inside the range and reject a range without one.Testing
tests/Hedgehog.MSTest.Tests.FSharp, 142 tests: the xUnit and NUnit suites rewritten for MSTest, plus tests of everything listed above. A property that fails by design is driven through the adapter's entry point and its result is asserted, so it does not fail the suite.tests/Hedgehog.MSTest.Tests.FSharp.MinimumMSTestcompiles the same test files against MSTest 4.2.3, so the solution runs the suite on the oldest supported MSTest and on the current one.tests/Hedgehog.MSTest.Tests.CSharp, 36 tests, after the model ofHedgehog.Xunit.Tests.CSharp: the generator attributes, the settings as named arguments and as constructor arguments, anAutoGenConfigtype, aGenAttributedeclared in C#, data rows,Recheck, and properties that returnTaskandValueTask.MSTest.TestFramework4.2.3, the lowest version I tested that hasTestContext.Current; the test projects reference 4.5.1, except the one named above.dotnet buildanddotnet testof the whole solution pass locally.Things to know before merging
master.ymlpacks and pushes every package, so a merge publishesHedgehog.MSTest1.0.0. The id is not taken on nuget.org.Authorsnames me next to Alexey Raga, whose code most of the shared files are. Change it as you see fit.Property.checkBool; the xUnit and NUnit examples next to it callProperty.checkon aProperty<bool>, which does not compile.dotnet fsi gen-docfx.fsx Releasefails for me before it reaches anything this pull request touches, while it loadsHedgehog.dll: the script does not pinFSharp.Formatting, and its current release needs a newerFSharp.Compiler.Servicethan the pinned 43.8.400. CI may hit the same.The adapter was written for fsprojects/FSharp.Azure.Cosmos, where it lives as a test-only project until there is a package.
🤖 Generated with Claude Code