Table of Contents

How to generate sealed class hierarchies

Annotate an abstract base class and each concrete subtype with [Arbitrary] to get a Strategy.OneOf strategy that picks uniformly among all subtypes.

Requirements

  • The abstract base must be abstract, partial, and annotated with [Arbitrary]
  • Each subtype you want included must be partial and annotated with [Arbitrary]
  • All types must be in the same compilation (external assembly subtypes are not detected)

Steps

1. Mark the abstract base

using Conjecture.Core;

[Arbitrary]
public abstract partial class Shape { }

2. Mark each concrete subtype

[Arbitrary]
public partial class Circle : Shape
{
    public Circle(double radius) { }
}

[Arbitrary]
public partial class Rectangle : Shape
{
    public Rectangle(double width, double height) { }
}

The generator emits ShapeArbitrary that picks uniformly across all decorated subtypes:

// Auto-generated
public sealed class ShapeArbitrary : IStrategyProvider<Shape>
{
    public Strategy<Shape> Create() =>
        Strategy.OneOf(
            new CircleArbitrary().Create().Select(static x => (Shape)x),
            new RectangleArbitrary().Create().Select(static x => (Shape)x)
        );
}

3. Use it in a property test

[Property]
public bool Shape_area_is_non_negative([From<ShapeArbitrary>] Shape shape)
{
    return shape.Area() >= 0;
}

Undecorated subtypes

If a concrete subtype exists but is not annotated with [Arbitrary], Conjecture emits a CON205 warning at the subtype declaration site:

[Arbitrary]
public abstract partial class Shape { }

public partial class Triangle : Shape { }  // CON205: excluded from ShapeArbitrary

Triangle will not appear in the generated OneOf. Add [Arbitrary] to include it, or suppress CON205 if exclusion is intentional.

See also