-
Notifications
You must be signed in to change notification settings - Fork 29
Type Conventions
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.
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);
- 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
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