fscheck / FsCheck

Random Testing for .NET
https://fscheck.github.io/FsCheck/
BSD 3-Clause "New" or "Revised" License
1.17k stars 156 forks source link

How to generate instances of more complicated data structures #641

Closed edyoung closed 7 months ago

edyoung commented 1 year ago

Hi,

I'm new to FsCheck (awesome project!). This is a request for guidance rather than a bug report. I would like to test a C# API which takes as input objects with many fields, some of which are other nested objects, and use FsCheck to generate different inputs. This is using 3.0 RC1.

I have this so far, which appears to work but is a bit cumbersome, and would be worse with more properties. How can this be written more cleanly?

namespace PropTests
{
    public class T
    {
        public int X { get; set; }
        public string Y { get; set; }
        public T? Next { get; set; }
    }

    [TestClass]
    public class UnitTest1
    {        
        [TestMethod]
        public void TestMethod2()
        {
            ArbMap.Default.Merge<MyArbitraries>();
            var samples = Gen.Sample<T>(new ArbitraryT().Generator,100,100);                           
        }
    }

    public class ArbitraryT : Arbitrary<T>
    {
        public static Gen<T> SafeTHelper(int size)
        {
            var pairGen = ArbMap.Default.GeneratorFor<int>().Zip(ArbMap.Default.GeneratorFor<string>());
            if (size == 0)
            {
                return pairGen.Select(i => new T() { X = i.Item1, Y = i.Item2 });
            }
            else
            {
                var subtree = SafeTHelper(size / 2);
                return Gen.OneOf(
                    pairGen.Select(i => new T() { X = i.Item1, Y = i.Item2 }),
                    pairGen.Zip(subtree).Select(i => new T() { X = i.Item1.Item1, Y = i.Item1.Item2, Next = i.Item2 }));                    
            }
        }

        public override Gen<T> Generator {
            get
            {
                return Gen.Sized(SafeTHelper);
            }
        }
    }

    public class MyArbitraries
    {
        public static Arbitrary<T> T { get; } = new ArbitraryT();
    }
}
kurtschelfthout commented 1 year ago

That's a very broad question, but a few tips:

bartelink commented 7 months ago

@edyoung can you share where you landed on this and/or close it please? Kurt's advice is excellent, but I'm not sure this is an actionable issue as it stands, and such issues are a both a tax on the maintainer, and other users.

edyoung commented 7 months ago

I intended it as a discussion rather than an issue