Skip to content
Cyrille DUPUYDAUBY edited this page Feb 6, 2020 · 16 revisions

Disclaimer

This page is dedicated to advanced functions available to write your own checks

General considerations

As shown in Extensibility the general structure of check logic is

public static ICheckLink<ICheck<string>> IsXXX(this ICheck<string> context)
{
  ExtensibilityHelper.BeginCheck(context).
    Rulexx(...).
    Rulexx(...).
    Rulexx(...).
    EndCheck();
  return ExtensibilityHelper.BuildChainLink(context);
}

Where each Rulexx(...) is an helper function that:

  • defines a rule to check
  • or provides details for error messages

Fail conditions

FailWhen

Describes a situation where the check should faild. Signature:

ICheckLogic<T> FailWhen(Func<T, bool> predicate, string errorMessage, MessageOptions option = MessageOptions.None);

// exemple: fail when  the sut is even.
 ...
 FailWhen( sut => sut % 2 == 0,  "The {checked} is even, whereas it should be odd.");

Where:

  • predicate: function that will received the sut and must returns true when the check must fail.
  • errorMessage: error message template, using {checked} and {expected}as placeholders for the sut and the provided expected value (if any).
  • option: flag enumeration that allows to specify options for error message structure. See below.

MessageOptions

Enumeration that is used to specify options for error message generation. Values are flags and can be combined:

  • MessageOptions.None: error message is generated with an error message, a standard description bloc for the checked value and a standard description bloc for the expected value, if one is provided
  • MessageOptions.NoCheckedBlock: prevent the creation of a description bloc for the checked value. Should be use when error message is explicit enough, for example when the checked value is null.
  • MessageOptions.NoExpectedBlock: prevent the creation of a description bloc for the expected value. Should be use when error message is explicit enough, for example when the expected and checked value are equal.
  • MessageOptions.ForceType: ensure that explicit type information is provided for checked and expected blocs.
  • MessageOptions.WithHash: ensure that hash values are provided for checked and expected blocs.

FailIfNull

Shorthand to take care of the case when the sut is null. Signature:

ICheckLogic<T> FailIfNull<T>(string error = "The {0} is null.");
...
// example
FailIfNull();

Fail

Ensure the check should always fail. This method is typically used within the Analyze method lambda. Signature:

ICheckLogic<T> Fail(string errorMessage, MessageOptions option = MessageOptions.None);

Where:

  • errorMessage: error message template, using {checked} and {expected}as placeholders for the sut and the provided expected value (if any).
  • option: flag enumeration that allows to specify options for error message structure. See above.
 ...
 Fail( "The {checked} is even, whereas it should be odd.");

When you need more complex analysis

CheckSutAttribute

Allow to perform tests on any sut's property or attribute. Signature:

ICheckLogic<TU> CheckSutAttributes<TU>(Func<T, TU> sutExtractor, string sutLabel);

where

  • sutExtractor is a lambda that extract the property or attribute you wish to check
  • sutLabel is the property's name as you want it to appear in error messages.

Example

CheckSutAttributes( s => s.Message, "error message").FailWhen( m => m != expectedValue);

Note that after a call to CheckSutAttributes, every check you perform will be against the extracted value. As of now, there is no method to switch back to the original sut.

Analyze

Allow to execute arbitrary lambda to implement complex checks. Methods presented here provide limited flexibility in error message generation: you can have a specific error message depending on the failure cause, but messages themselves are static. But it is often useful to generate error messages based on the sut, such as listing missing entries in an enumeration.

Signature:

ICheckLogic<T> Analyze(Action<T, ICheckLogic<T>> action);

where

  • action is a provided lambda that will be called with the sut and an ICheckLogic instance.

Example:

public static ICheckLink<ICheck<DateTime>> IsInSameMonthAs(this ICheck<DateTime> check, DateTime other)
{
        ExtensibilityHelper.BeginCheck(check).
        Analyze((sut, test) =>
        {
            if (sut.Month != other.Month)
            {
                test.Fail($"The {{0}} has a different month than the {{1}} (actual: {sut.Month}, expected: {other.Month})");
            }
        })
        ComparingTo(other, "same month", "different month")
        OnNegate("The {0} has the same month as the {1} whereas it must not.").
        EndCheck();
            
    return ExtensibilityHelper.BuildCheckLink(check);
}

Providing context for error messages

DefineExpectedValue

Specify the expected result of the check as a value; you can provide extra description text to make it more specific. This method should cover most use cases. Signature:

ICheckLogic<T> DefineExpectedValue<TU>(TU expected, string comparison = null, string negatedComparison = "different from");

where

  • expected is the expected value
  • comparison is an expression that will be added after the Expected block label
  • negatedComparison is an expression that will be added after the Expected block label when the check is Negated.

This method will add a description block to the error message. It follows this pattern:


The expected value: [comparison|negatedComparison]

expectedvalue [of type: [Type]] [with HashCode: [hashcode]]


DefineExpectedValues

Specify the expected result of the check as a list of values; you can provide extra description text to make it more specific. This method should be used when multiple values are expected, or a value within a list (of values). Signature:

ICheckLogic<T> DefineExpectedValues(IEnumerable values, long count, string expectedLabel = null, string negatedLabel = "different from");

where

  • values is the list of expected values
  • count is the number of expected values
  • comparison is an expression that will be added after the Expected block label
  • negatedComparison is an expression that will be added after the Expected block label when the check is Negated.

This method will add a description block to the error message. It follows this pattern:


The expected values: [comparison|negatedComparison]

values (count item[s]) [of type: [Type]] [with HashCode: [hashcode]]


SetValuesIndex

This is a companion method to DefineExpectedValues. Long enumerations are truncated to prevent having error messages that are too long. SetValuesIndex specificies which index is of interest. When used, NFluent error message will displays an extract of the enumerable that is centered on this index (e.g: value[index-2], value[index-1], value[index], value[index +1], value[index +2]) Signature:

ICheckLogic<T> SetValuesIndex(long index);

DefineExpectedType

Specify the expected result of the check as an instance of an expected type; you can provide extra description text to make it more specific. Signature:

ICheckLogic<T> DefineExpectedType(Type type);

where

  • type is the expected type

This method will add a description block to the error message. It follows this pattern:


The expected value: [different from]

an instance of type: [Type]


ComparingTo

Specify the expected result of the check as a value; you can provide extra description text to make it more specific. This method should cover most use cases. Signature:

ICheckLogic<T> ComparingTo<TU>(TU other, string comparison, string negatedComparison);

where

  • expected is the expected value
  • comparison is an expression that will be added after the Expected block label
  • negatedComparison is an expression that will be added after the Expected block label when the check is Negated.

This method will add a description block to the error message. It follows this pattern:


The given value: [comparison|negatedComparison]

expectedvalue [of type: [Type]] [with HashCode: [hashcode]]


Refining error messages when the check is negated

OnNegateWhen(...)

Specify an error message when the check is negated and some user defined conditions are met. Signature:

ICheckLogic<T> OnNegateWhen(Func<T, bool> predicate, string error, MessageOption options = MessageOption.None)

where

  • predicate is a function that returns true when the conditions are met (for the specific message)
  • error is the error message template to use when the check is negated and the coditions are met.
  • options are the options for error message genetarion (see MessageOptions above). Example
.OnNegateWhen((sut) => sut == null, "The {checked} is null, where as it must contain at least one element.", MessageOption.NoCheckedBlock)

This generates an error message when the check is negated and the sut is null.

Clone this wiki locally