-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Property Method Attribute Generators #16
Property Method Attribute Generators #16
Conversation
Can you share a complete example here of a test as it would be written without this PR and then how it would be written with this PR? |
In reply to @TysonMN. As I'm primarily a C# developer and I envisaged the usage of these attributes in C#, I have added the examples in C#, the code would be more succinct in F# but it think the same points would still apply.
I think that in both the attribute examples the code is more succinct. I also think that in an IDE it easier to find the generator by navigating to it's definition, in the first example you need to navigate to the Generators type and then definition then parse the type looking for the generator responsible for creating the type, in addition the same wrapper type could be defined in different Generator types with different definitions (In F# these would probably be generator function not associated with a type so this would not be an issue). |
C# examples are fine. Can you share the two versions of the test in a comment on this PR? |
@TysonMN Here you. Current Methodologypublic record PositiveInt(int Value);
public record NegativeInt( int Value );
public class Generators
{
public static Gen<PositiveInt> GenPositiveInt =>
from x in Gen.Int32(Range.Constant(1, Int32.MaxValue))
select new PositiveInt(x);
public static Gen<NegativeInt> GenNegativeInt =>
from x in Gen.Int32(Range.Constant(Int32.MinValue, -1))
select new NegativeInt(x);
public static AutoGenConfig _ => GenX.defaults
.WithGenerator(GenPositiveInt)
.WithGenerator(GenNegativeInt);
}
public class PositiveAndNegativeGeneratorContainerTypes
{
[Property(typeof(Generators))]
public bool ResultOfAddingPositiveAndNegativeLessThanPositive(
PositiveInt positive,
NegativeInt negative)
=> positive.Value + negative.Value < positive.Value;
} Simple Attribute, non parameterized attributepublic class Negative : ParameterGeneraterBaseType<int>
{
public override Gen<int> Generator => Gen.Int32(Range.Constant(Int32.MinValue, -1));
}
public class Positive : ParameterGeneraterBaseType<int>
{
public override Gen<int> Generator => Gen.Int32(Range.Constant(1, Int32.MaxValue));
}
public class PositiveAndNegativeUtilizingIntegerRangeAttribute
{
[Property]
public bool ResultOfAddingPositiveAndNegativeLessThanPositive(
[Positive] int positive,
[Negative] int negative)
=> positive + negative < positive;
} With Range Attributepublic class Int32Range : ParameterGeneraterBaseType<int>
{
private readonly int _min;
private readonly int _max;
public Int32Range(int min, int max)
{
_min = min;
_max = max;
}
public override Gen<int> Generator => Gen.Int32(Range.Constant(_min, _max));
}
public class PositiveAndNegativeWithAttributes
{
[Property]
public bool ResultOfAddingPositiveAndNegativeLessThanPositive(
[Int32Range(1, Int32.MaxValue)] int positive,
[Int32Range(Int32.MinValue, -1 )] int negative)
=> positive + negative < positive;
} |
My first impression is that I really like this since |
@dharmaturtle I thought so. Looking at my outstanding points
|
Incase there is any confusion JohnEfford and JohnEffo are the same person, JohnEfford is my work GitHub account. |
The examples look great. Thanks for sharing and creating this PR. |
Getting StartedInstall the Hedgehog.Xunit [package][nuget] from Visual Studio's Package Manager Console: PM> Install-Package Hedgehog.Xunit
F# ExampleSuppose you have a test that uses Hedgehog.Experimental and looks similar to the following:open Xunit
open Hedgehog
[<Fact>]
let ``Reversing a list twice yields the original list`` () =
property {
let! xs = GenX.auto<int list>
return List.rev (List.rev xs) = xs
} |> Property.check Then using Hedgehog.Xunit, you can simplify the above test to open Hedgehog.Xunit
[<Property>]
let ``Reversing a list twice yields the original list, with Hedgehog.Xunit`` (xs: int list) =
List.rev (List.rev xs) = xs C# ExampleSuppose you have a test that uses Hedgehog.Experimental and looks similar to the following: using Hedgehog;
using Hedgehog.Linq;
using Hedgehog.Xunit;
using Property = Hedgehog.Linq.Property;
public class DocumentationSamples
{
[Fact]
public void Reversing_a_list_twice_yields_the_original_list()
{
var gen = GenX.auto<List<int>>();
var prop = from data in Property.ForAll(gen)
let testList = Enumerable.Reverse(data).Reverse().ToList()
select Assert.Equivalent(data, testList, true);
prop.Check();
}
} Then using Hedgehog.Xunit, you can simplify the above test to [Property]
public void Reversing_a_list_twice_yields_the_original_list_with_xunit(List<int> xs)
{
var testList = Enumerable.Reverse(xs).ToList();
Assert.Equivalent(xs, testList, true);
} |
Hmm I want to look into if there's a way create an attribute that just wraps a
Hm, the readme is rather dense with many code blocks. It also uses F# specific segments like
I think it would be better to have a separate readme for C#, (depending on how far you're willing to go - if it's just the one |
I looked into trying to create a generic [Property]
public bool ResultOfAddingPositiveAndNegativeLessThanPositive(
[Positive] int positive,
[Negative] int negative)
=> positive + negative < positive; vs [Property]
public bool ResultOfAddingPositiveAndNegativeLessThanPositive(
[Int32Range(1, Int32.MaxValue)] int positive,
[Int32Range(Int32.MinValue, -1 )] int negative)
=> positive + negative < positive;
I'll start on a separate C# documentation file. and put a Go Here For C# Documentationsized link at the top of the current readme |
From my own readme:
😭 It looks like C#11 supports generic attributes, though F# doesn't. So someday we might be able to do
💯 |
using Gen = Hedgehog.Linq.Gen; | ||
using Range = Hedgehog.Linq.Range; | ||
|
||
namespace csharp_attribute_based_parameters_comparision; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this name be more general? Might be useful for other C# examples. Or heck, could even turn it into a real test suite. I should probably add it to CICD anyway - doesn't really make sense to have broken examples.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have updated this, I realised this when I came to do the documentation and wanted to put add the documentation examples.
A couple of things: I Cannot run an async property: Only the given contructors can be used for the attributes in C# this means that I can use autoGenConfigArgs this is not a problem for me as I'd always use, parameter based generators if I required to pass in argruments, but it does mean I can adjust the size via the property would you be happy for me to add: new(tests, shrinks, size) |
A few more things. I've committed the documentation, sans the Async docs |
Commit 48b88d0 allows me to run the async properties below which were not running previously, this has meant making a change to the matching logic in InternnalLogin.yieldAndCheckReturnValue all the tests pass but I'd feel happier if you guys could take a look. public Task Fast()
{
return Task.FromResult(0);
}
[Property]
public async Task Async_property_which_returns_task_can_run(
int i)
{
await Fast();
Assert.True(i == i);
}
[Property]
public async Task<bool> Async_property_which_returns_boolean_task_can_run(bool i)
{
await Fast();
return i || !i;
} |
28b83d2
to
1567e36
Compare
Pull Request Test Coverage Report for Build 5465920435
💛 - Coveralls |
1bec866
to
0b1197c
Compare
0b1197c
to
4869c93
Compare
4869c93
to
c93e7d2
Compare
This reverts commit c93e7d2.
036a381
to
f51725e
Compare
1c4d20a
to
cafec87
Compare
cafec87
to
9915cfd
Compare
@JohnEffo Okay I'm more or less done with my changes - would you like review them? My biggest logic change was to drop the I also made many changes to the readme. I noticed that you used |
I felt cheeky changing your readme in any case and your changes look good. Thank you for time you spent going through the C# examples. I tried getting something like the code you added below to work, so having a working example is really useful. [Property(AutoGenConfig = typeof(ConfigWithArgs), AutoGenConfigArgs = new object[] { "foo", 13 })]
public bool This_also_passes(string s, int i) =>
s == "foo" && i == 13; I'm happy for you to merge. |
Okay this is out with 0.6.0! |
What is this
The aim of this pull request is to allow the definition of Generators via attributes. This has a couple of advantages, currently all generators for
Properties
need to be registered on the AutoGenConfig as discussed in your documentation:This means that:
AutoGenConfigContainer
classes would need to be created.Allowing generators to be specified at the parameter level allows a greater level of freedom at point of consumption, as the attributes themselves can take parameters, for example:
I think this keeps the generator specification and tests closer together.
Work outstanding
This is a proof of concept, to see if you like the idea as such there is still work which need doing: