From 1c4d20a4ac56efc712d3185f57e23de19ac1fc32 Mon Sep 17 00:00:00 2001 From: AN Date: Mon, 3 Jul 2023 16:04:57 -0500 Subject: [PATCH] more or less done with Readme --- readme.md | 218 ++++++++---------- readmeCSharp.md | 10 +- .../PropertyTests.fs | 2 +- 3 files changed, 98 insertions(+), 132 deletions(-) diff --git a/readme.md b/readme.md index 66c17ef..ce7edb0 100644 --- a/readme.md +++ b/readme.md @@ -1,8 +1,5 @@ # Hedgehog.Xunit -> **Note** -> ## This readme is for F#. Go [here for C# documentation.](/readmeCSharp.md) - [![][nuget-shield]][nuget] [![][workflow-shield]][workflow] [![Coverage Status](https://coveralls.io/repos/github/dharmaturtle/fsharp-hedgehog-xunit/badge.svg?branch=main)](https://coveralls.io/github/dharmaturtle/fsharp-hedgehog-xunit?branch=main) [Hedgehog][hedgehog] with convenience attributes for [xUnit.net][xunit]. @@ -11,11 +8,15 @@ ## Features -- Test method arguments can be generated [`GenX.auto`](https://github.com/hedgehogqa/fsharp-hedgehog-experimental/#auto-generation). +- Test method arguments generated with a custom [`GenX.auto`](https://github.com/hedgehogqa/fsharp-hedgehog-experimental/#auto-generation)... +- ...or with a custom [Generator](https://hedgehogqa.github.io/fsharp-hedgehog/index.html#Generators). - `Property.check` called for each test. -- Attribute based generator definitions -## Getting Started +## Getting Started in C# + +This readme is for F#. Go [here for C# documentation.](/readmeCSharp.md) + +## Getting Started in F# Install the _Hedgehog.Xunit_ [package][nuget] from Visual Studio's Package Manager Console: @@ -48,18 +49,18 @@ let ``Reversing a list twice yields the original list, with Hedgehog.Xunit`` (xs ## Documentation `Hedgehog.Xunit` provides the following attributes: -* [Property](#properties-attribute) -Converts an XUnit `Fact` into a property, allowing you to easily configure property parameters. -* [Properties](#properties-attribute) -Allows the easy configuration of all properties in a test suite. -* [Recheck](#recheck-attribute) -Rerun a particular test case. -* [GenAttribute](#GenAttribute) -Control what generator is used on a parameter by parameter basis. - -### `Property` attribute ---- -Methods with the `Property` attribute have their arguments automatically generated by [`GenX.auto`](https://github.com/hedgehogqa/fsharp-hedgehog-experimental/#auto-generation). +* [`Property`](#-property-attribute) +Extends xUnit's `Fact` to call Hedgehog's `property`. +* [`Properties`](#-properties-attribute) +Configures all [`Property`](#-property-attribute) tagged tests in a module or class. +* [`GenAttribute`](#-genattribute) +Set a parameter's generator. +* [`Recheck`](#-recheck-attribute) +Run a test with a specific `Size` and `Seed`. + +### 🔖 `Property` attribute + +Methods with the `Property` attribute have their arguments generated by [`GenX.auto`](https://github.com/hedgehogqa/fsharp-hedgehog-experimental/#auto-generation). ```f# type ``class with a test`` (output: Xunit.Abstractions.ITestOutputHelper) = @@ -77,7 +78,7 @@ Test input: 404306656 Test input: 1550509078 ``` -`Property.check` is called automatically. +`Property.check` is called. ```f# [] @@ -135,47 +136,50 @@ System.Exception: *** Failed! Falsifiable (after 23 tests and 5 shrinks): 50 ``` -### `Property` Configuration ---- +#### ⚙️ `Property` attribute Configuration + The `Property` attribute's constructor may take several arguments: -* [`AutoGenConfig` and `AutoGenConfigArgs`](#autogenconfig-and-autogenconfigargs): Allow the manual control of generators -* [`Tests`](#tests-count): Specifies the number of tests to run -* [`Shrinks`](#shrinks-count): Specifies the number of shrinks +* [`AutoGenConfig` and `AutoGenConfigArgs`](#-autogenconfig-and-autogenconfigargs): Set an `AutoGenConfig` to use when generating arguments. +* [`Tests`](#-tests): Specifies the number of tests to be run. +* [`Shrinks`](#-shrinks): Specifies the maximal number of shrinks that may run. +* [`Size`](#-size): Sets the `Size` to a value for all runs. -The `Property` attribute extends `Xunit.FactAttribute`, so it may also take `DisplayName`, `Skip`, and `Timeout`. +The `Property` attribute extends `Xunit.FactAttribute`, so it may also take `DisplayName`, `Skip`, and `Timeout`. -#### `AutoGenConfig` and `AutoGenConfigArgs` ---- -The default generator used to create arguments is `GenX.defaults`. To specify different generators: -* Create a class with a single static property or method that returns an instance of `AutoGenConfig`. -* Provide the type of this class as an argument to the `Property` attribute. This works around the constraint that [`Attribute` parameters must be a constant.](https://stackoverflow.com/a/33007272) +#### 🧰 `AutoGenConfig` and `AutoGenConfigArgs` - ```f# - type AutoGenConfigContainer = - static member __ = - GenX.defaults |> AutoGenConfig.addGenerator (Gen.constant 13) +[`GenX.defaults`](https://github.com/hedgehogqa/fsharp-hedgehog-experimental/blob/6d8719aaa6568c51478a39e2ad76afbcd43d5b8e/src/Hedgehog.Experimental/Gen.fs#L356) is the [`AutoGenConfig`](https://github.com/hedgehogqa/fsharp-hedgehog-experimental/blob/6d8719aaa6568c51478a39e2ad76afbcd43d5b8e/src/Hedgehog.Experimental/Gen.fs#L17) used by default. - [)>] - let ``This test passes`` (i: int) = - i = 13 - ``` +Here's how to add your own generators: +1. Create a class with a single static property or method that returns an instance of `AutoGenConfig`. +2. Provide the type of this class as an argument to the `Property` attribute. (This works around the constraint that [`Attribute` parameters must be a constant.](https://stackoverflow.com/a/33007272)) -* If the method takes arguments, you must provide them using `AutoGenConfigArgs`. +```f# +type AutoGenConfigContainer = + static member __ = + GenX.defaults |> AutoGenConfig.addGenerator (Gen.constant 13) - ```f# - type ConfigWithArgs = - static member __ a b = - GenX.defaults - |> AutoGenConfig.addGenerator (Gen.constant a) - |> AutoGenConfig.addGenerator (Gen.constant b) +[)>] +let ``This test passes`` (i: int) = + i = 13 +``` - [, AutoGenConfigArgs = [|"foo"; 13|])>] - let ``This also passes`` s i = - s = "foo" && i = 13 - ``` +If the method takes arguments, you must provide them using `AutoGenConfigArgs`. + +```f# +type ConfigWithArgs = + static member __ a b = + GenX.defaults + |> AutoGenConfig.addGenerator (Gen.constant a) + |> AutoGenConfig.addGenerator (Gen.constant b) + +[, AutoGenConfigArgs = [|"foo"; 13|])>] +let ``This also passes`` s i = + s = "foo" && i = 13 +``` + +#### 🧰 `Tests` -#### `Tests` (count) ---- Specifies the number of tests to be run, though more or less may occur due to shrinking or early failure. ```f# @@ -184,8 +188,8 @@ let ``This runs 3 times`` () = () ``` -#### `Shrinks` (count) ---- +#### 🧰 `Shrinks` + Specifies the maximal number of shrinks that may run. ```f# @@ -194,8 +198,8 @@ let ``No shrinks occur`` i = if i > 50 then failwith "oops" ``` -##### `Size` ---- +#### 🧰 `Size` + Sets the `Size` to a value for all runs. ```f# @@ -204,9 +208,9 @@ let ``"i" mostly ranges between -1 and 1`` i = printfn "%i" i ``` -#### Properties attribute ---- -This optional attribute can decorate modules or classes. It sets default arguments for [`AutoGenConfig`, `AutoGenConfigArgs`](#autogenconfig-and-autogenconfigargs), [`Tests`](#tests-count), [`Shrinks`](#shrinks-count), and [`Size`](#size). These will be overridden by any arguments provided by the `Property` attribute. +### 🔖 `Properties` attribute + +This optional attribute can decorate modules or classes. It sets default arguments for [`AutoGenConfig`, `AutoGenConfigArgs`](#-autogenconfig-and-autogenconfigargs), [`Tests`](#-tests), [`Shrinks`](#-shrinks), and [`Size`](#-size). These will be overridden by any arguments provided by the `Property` attribute. ```f# type Int13 = static member __ = GenX.defaults |> AutoGenConfig.addGenerator (Gen.constant 13) @@ -219,95 +223,53 @@ module ``Module with tests`` = let ``this passes and runs once`` (i: int) = i = 13 - [, 2)>] + [, 2)>]🔖 let ``this passes and runs twice`` (i: int) = i = 2718 ``` -### Recheck attribute ---- -This optional method attribute invokes `Property.recheck` with the given `Size` and `Seed`, it must be used with `Property`. +### 🔖 `GenAttribute` -```f# -[] -[] -let ``this passes`` i = - i = 12345 -``` +To assign a generator to a test's parameter, extend `GenAttribute` and override `Generator`: -### GenAttribute ---- -This is the base type of an attribute that can applied to property function arguments. It allows you to provide a generator on a argument by argument basis. - -Lets look at the a property where we want to specify the generator to be used for two integer arguments. -Using a `Property` attribute on this turns out to be quite tricky as we require two different generators for the same type, we may end up with something like this, which wraps the integer value an a containing record. - -```F# -open Xunit -open Hedgehog - -let positiveInt() = Range.constant 0 System.Int32.MaxValue |> Gen.int32 -let negativeInt() = Range.constant System.Int32.MinValue 0 |> Gen.int32 - -type PositiveInt = {value : int} -type NegativeInt = {value : int} + ```f# + type Int5() = + inherit GenAttribute() + override _.Generator = Gen.constant 5 -let positiveInt() = positiveInt() |> Gen.map(fun x-> {PositiveInt.value=x}) -let negativeInt() = negativeInt() |> Gen.map(fun x -> {NegativeInt.value=x}) +[] +let ``can set parameter as 5`` ([] i) = + Assert.StrictEqual(5, i) +``` +Here's a more complex example of `GenAttribute` that takes a parameter and overrides `Property`'s `AutoGenConfig`: + +```f# type AutoGenConfigContainer = static member __ = - GenX.defaults - |> AutoGenConfig.addGenerator (positiveInt()) - |> AutoGenConfig.addGenerator (negativeInt()) - -[)>] -let ``Positive + Negative <= Positive`` (positive:PositiveInt) (negative:NegativeInt) = - positive.value + negative.value <= positive.value -``` - - Using the `GenAttribute` attribute is would look like as below: - - ```F# - type Posint() = - inherit GenAttribute() - override this.Generator = positiveInt() + GenX.defaults |> AutoGenConfig.addGenerator (Gen.constant 1) -type NegInt() = +type ConstInt(i: int)= inherit GenAttribute() - override this.Generator = negativeInt() - -[] -let ``Positive + Negative <= Positive xunit`` ([] positive) ([] negative) = - positive + negative <= positive + override _.Generator = Gen.constant i + +[)>] +let ``GenAttribute overrides Property's AutoGenConfig`` (one, [] two) = + Assert.StrictEqual(1, one) + Assert.StrictEqual(2, two) ``` -We can also supply parameters to the generator like so: - -```F# -//Using a parameterised attribute to configure the generators -//Using attributes to configure what generator the property should use -type IntRange(minimum:int32, maximum:int32) = - inherit GenAttribute() - override this.Generator = Range.constant minimum maximum |> Gen.int32 +### 🔖 `Recheck` attribute +This optional method attribute invokes `Property.recheck` with the given `Size` and `Seed`. It must be used with `Property`. +```f# [] -let ``Positive + Negative <= Positive attribute parameterised`` - ([] positive) - ([] negative) = - positive + negative <= positive +[] +let ``this passes`` i = + i = 12345 ``` -> The code above is in [Examples](/examples/fsharp-examples/attribute-based-parameter-comparison.fs) - -The attribute fulfils a very similar function to specifying the [`AutoGenConfig`](#autogenconfig-and-autogenconfigargs) parameter on the property. If all your tests use the same generators it may be more less code to use [`AutoGenConfig`](#autogenconfig-and-autogenconfigargs), which can be applied to the [`Properties`](#properties-attribute) attribute. -Parameter generator attributes allow you to easily configure the generator per argument, useful if: -* A single property has multiple arguments of the same type which require different generators. -* You want to easily configure your generators a per property basis. -* Different properties require different generators. -* You want to be able to see per argument what generator was used. - ## Tips Use named arguments to select the desired constructor overload. diff --git a/readmeCSharp.md b/readmeCSharp.md index f316102..564b893 100644 --- a/readmeCSharp.md +++ b/readmeCSharp.md @@ -1,4 +1,4 @@ -# fsharp-hedgehog-xunit +# Hedgehog.Xunit [![][nuget-shield]][nuget] [![][workflow-shield]][workflow] [![Coverage Status](https://coveralls.io/repos/github/dharmaturtle/fsharp-hedgehog-xunit/badge.svg?branch=main)](https://coveralls.io/github/dharmaturtle/fsharp-hedgehog-xunit?branch=main) @@ -8,11 +8,15 @@ ## Features -- Test method arguments generated by the customizable [`GenX.auto`](https://github.com/hedgehogqa/fsharp-hedgehog-experimental/#auto-generation). +- Test method arguments generated with a custom [`GenX.auto`](https://github.com/hedgehogqa/fsharp-hedgehog-experimental/#auto-generation)... +- ...or with a custom [Generator](https://hedgehogqa.github.io/fsharp-hedgehog/index.html#Generators). - `Property.check` called for each test. +## Getting Started in F# -## Getting Started +This readme is for C#. Go [here for F# documentation.](/readme.md) + +## Getting Started in C# Install the _Hedgehog.Xunit_ [package][nuget] from Visual Studio's Package Manager Console: diff --git a/tests/Hedgehog.Xunit.Tests.FSharp/PropertyTests.fs b/tests/Hedgehog.Xunit.Tests.FSharp/PropertyTests.fs index 7ad469f..e0d223c 100644 --- a/tests/Hedgehog.Xunit.Tests.FSharp/PropertyTests.fs +++ b/tests/Hedgehog.Xunit.Tests.FSharp/PropertyTests.fs @@ -750,7 +750,7 @@ module ``returning a property runs it`` = module ``GenAttribute Tests`` = [] - let ``can define parameter as 5`` ([] i) = + let ``can set parameter as 5`` ([] i) = Assert.StrictEqual(5, i) [)>]