Skip to content
Andrew Best edited this page Jan 21, 2020 · 6 revisions

Type conventions are Conventional's bread and butter. They allow you to enforce the style, composition and usage of types within your codebase.

All type conventions derive from ConventionSpecification

Type conventions that relate to members of a type only inspect declared members of the type - this is a deliberate design choice due to how reflection works in inheritance scenarios, as base class members cannot be effectively interrogated via derived types. If you wish to enforce conventions across base and derived types, you must ensure both the base and derived types are included in the set of types you are enforcing conventions across.

Sample Usage

Standard Syntax

new[] { typeof(MyType), typeof(MyOtherType) }
    .MustConformTo(
        Convention.PropertiesMustHavePublicGetters.And(
        Convention.PropertiesMustHavePublicSetters))
    .WithFailureAssertion(Assert.Fail);

Fluent Syntax

new[] { typeof(MyType), typeof(MyOtherType) }
    .MustConformTo(Convention.PropertiesMustHavePublicGetters)
    .AndMustConformTo(Convention.PropertiesMustHavePublicSetters)
    .WithFailureAssertion(Assert.Fail);

Or Mix It Up!

new[] { typeof(MyType), typeof(MyOtherType) }
    .MustConformTo(
        Convention.PropertiesMustHavePublicGetters.And(
        Convention.PropertiesMustHavePublicSetters))
    .AndMustConformTo(Convention.MustHaveADefaultConstructor)
    .WithFailureAssertion(Assert.Fail);

Supplied Type Conventions

  • Properties must be public
  • Properties must have public getters
  • Properties must have public setters
  • Properties must have protected setters
  • Properties must have private setters
  • Name must start with
  • Name must end with
  • Must live in namespace
  • Must have a default constructor
  • Must have a default non-public constructor
  • Must have appropriate constructors
  • Must have attribute
  • Must have corresponding Enum
  • Properties of type must have attribute
  • Must not take a dependency on
  • Must not have a property of type
  • Requires a corresponding implementation of (T)
  • Enumerable properties must be eager loaded
  • Collection properties must not have setters
  • Must not resolve current time via DateTime
  • Must not use DateTimeOffset.Now
  • Exceptions thrown must be derived from specified type
  • Must instantiate properties of specified type in default constructor
  • All properties must be instantiated during construction
  • Must have matching embedded resource
  • Void methods must not be async
  • Async methods must have 'Async' suffix
  • Library code should call Task.ConfigureAwait(false) to prevent deadlocks

Known offenders and Doomsday tests

If you are in a scenario where you want to acknowledge previous types that break a convention you are introducing - but prevent more from being introduced, WithKnownOffenders can be supplied

new[] { typeof(OldType), typeof(NewType) }
    .WithKnownOffenders(1)
    .MustConformTo(Convention.NameMustStartWith("New"));

Taking this further, if you want to enforce that the existing offenders be compliant by a given date, you can supply ByDoomsday, along with an optional WithWarningWithin to warn as the date approaches, and WithMessage to supply extra context to the output

new[] { typeof(OldType), typeof(NewType) }
    .WithKnownOffenders(1)
    .ByDoomsday(new DateTime(2015,11,18))
    .WithWarningWithin(TimeSpan.FromDays(14))
    .WithMessage("Names must start with 'New'")
    .MustConformTo(Convention.NameMustStartWith("New"));

When using Known Offenders or Doomsday tests, you must make sure to configure the default failure and warning assertions using Conventional's configuration