How to claim that all selected properties are set (but not empty or empty)

I want to verify that certain properties on my DTO object are set. I tried to do this with Fluent Assertions, but the following code does not seem to work:

mapped.ShouldHave().Properties(
    x => x.Description,
    ...more
    x => x.Id)
    .Should().NotBeNull(); 

Can this be achieved with Fluent Assertions or another tool? Free statements have a value of ShouldBeEquivalentTo, but in fact I don't care if they are not nulls / empty, so I could not use it.

Of course, I can just do Assert at every level of the property, but I'm interested in a more elegant way.

+4
source share
3 answers

, Properties PropertiesAssertion, EqualTo . NotEqualTo NotNull. PropertiesAssertion null, .

AssertionHelper Func s, . , ,

public static void CheckAllPropertiesAreNotNull<T>(this T objectToInspect, 
                                                params Func<T, object>[] getters)
{
    if (getters.Any(f => f(objectToInspect) == null))
        Assert.Fail("some of the properties are null");
}

some of the properties are null

var myDto = new MyDto();

myDto.CheckAllPropertiesAreNotNull(x => x.Description, 
                                   x => x.Id); 

:

  • Id , getter(objectToInspect) == null false
  • , null, .

, :

  • CheckAllPropertiesAreNotNull, Generic Func<TInput, TFirstOutput> firstGetter, default(TFirstOutput)
  • Activator, Equals

. IsDefault, object ( , inted ):

private static bool IsDefault(this object value)
{
    if (value == null)
        return true;

    if (!value.GetType().IsValueType) //if a reference type and not null
        return false;

    //all value types should have a parameterless constructor
    var defaultValue = Activator.CreateInstance(value.GetType());
    return value.Equals(defaultValue);
}

, :

public static void CheckAllPropertiesAreNotDefault<T>(this T objectToInspect, 
                                                params Func<T, object>[] getters)
{
    if (getters.Any(f => f(objectToInspect).IsDefault()))
        Assert.Fail("some of the properties are not null");
}

, Expression<Func<T, object>>[] getters, Property. GetName, Expression<Func<T, object>>

public static string GetName<T>(Expression<Func<T, object>> exp)
{
    var body = exp.Body as MemberExpression;

    //return type is an object, so type cast expression will be added to value types
    if (body == null) 
    {
        var ubody = (UnaryExpression)exp.Body;
        body = ubody.Operand as MemberExpression;
    }

    return body.Member.Name;
}

:

public static void CheckAllPropertiesAreNotDefault<T>(this T objectToInspect, 
                                     params Expression<Func<T, object>>[] getters)
{
    var defaultProperties = getters.Where(f => f.Compile()(objectToInspect).IsDefault());

    if (defaultProperties.Any())
    {
        var commaSeparatedPropertiesNames = string.Join(", ", defaultProperties.Select(GetName));
        Assert.Fail("expected next properties not to have default values: " + commaSeparatedPropertiesNames);
    }
}

myDto.CheckAllPropertiesAreNotDefault(x => x.Description,
                                      x => x.Id);

, : , Id

. Dto Description string, Id - int. , , , .

+4

. , @Dennis, , , , . , Dennis , , ReferenceEqualityEquivalencyStep . , .When(info = > true) . - . (info = > ! Info.RuntimeType.IsComplexType() & &! (Info.RuntimeType IEnumerable)), , . , ISubjecInfo , , , , .

, . , .

namespace FluentAssertions
{
   public class SimpleIsNotDefaultEquivalencyStep : IEquivalencyStep
   {
        public bool CanHandle(EquivalencyValidationContext context, IEquivalencyAssertionOptions config)
        {
            return true;
        }

        public virtual bool Handle(EquivalencyValidationContext context, IEquivalencyValidator structuralEqualityValidator, IEquivalencyAssertionOptions config)
        {
            context.Subject.Should().NotBeDefault( context.Reason, context.ReasonArgs );
            return true;
        }
    }



    public static class FluentAssertionsDefaultnessExtensions
    {
        private static bool IsDefault( object value, bool orValueTypeDefault = false )
        {
            if( value == null )
            {
                return true;
            }

            Type t = value.GetType();
            t = orValueTypeDefault ? Nullable.GetUnderlyingType( t ) ?? t : t;

            if( t.IsValueType )
            {
                object defaultValue = Activator.CreateInstance( t );
                return value.Equals( defaultValue );
            }
            else if( value is string )
            {
                return string.IsNullOrWhiteSpace( value as string );
            }

            return false;
        }

        private static bool IsDefaultOrValueTypeDefault( object value )
        {
            return IsDefault( value, orValueTypeDefault: true );
        }

        public static AndConstraint<ObjectAssertions> NotBeDefault( this ObjectAssertions assertions, string because = "", params object[] reasonArgs )
        {
            Execute.Assertion
                .BecauseOf( because, reasonArgs )
                .ForCondition( !IsDefault( assertions.Subject ) )
                .FailWith( "Expected {context:object} to not be default{reason}, but found {0}.", assertions.Subject );

            return new AndConstraint<ObjectAssertions>( assertions );
        }

        public static AndConstraint<StringAssertions> NotBeDefault( this StringAssertions assertions, string because = "", params object[] reasonArgs )
        {
            Execute.Assertion
                .BecauseOf( because, reasonArgs )
                .ForCondition( !IsDefault( assertions.Subject ) )
                .FailWith( "Expected {context:object} to not be default{reason}, but found {0}.", assertions.Subject );

            return new AndConstraint<StringAssertions>( assertions );
        }

        public static AndConstraint<Numeric.NumericAssertions<T>> NotBeDefault<T>( this Numeric.NumericAssertions<T> assertions, string because = "", params object[] reasonArgs ) where T : struct
        {
            Execute.Assertion
                .BecauseOf( because, reasonArgs )
                .ForCondition( !IsDefault( assertions.Subject ) )
                .FailWith( "Expected {context:object} to not be default{reason}, but found {0}.", assertions.Subject );

            return new AndConstraint<Numeric.NumericAssertions<T>>( assertions );
        }

        public static AndConstraint<BooleanAssertions> NotBeDefault( this BooleanAssertions assertions, string because = "", params object[] reasonArgs )
        {
            Execute.Assertion
                .BecauseOf( because, reasonArgs )
                .ForCondition( !IsDefault( assertions.Subject ) )
                .FailWith( "Expected {context:object} to not be default{reason}, but found {0}.", assertions.Subject );

            return new AndConstraint<BooleanAssertions>( assertions );
        }

        public static AndConstraint<GuidAssertions> NotBeDefault( this GuidAssertions assertions, string because = "", params object[] reasonArgs )
        {
            Execute.Assertion
                .BecauseOf( because, reasonArgs )
                .ForCondition( !IsDefault( assertions.Subject ) )
                .FailWith( "Expected {context:object} to not be default{reason}, but found {0}.", assertions.Subject );

            return new AndConstraint<GuidAssertions>( assertions );
        }


        public static void ShouldNotBeEquivalentToDefault<T>( this T subject, string because = "", params object[] reasonArgs )
        {
            ShouldNotBeEquivalentToDefault( subject, config => config, because, reasonArgs );
        }

        public static void ShouldNotBeEquivalentToDefault<T>( this T subject, 
            Func<EquivalencyAssertionOptions<T>, EquivalencyAssertionOptions<T>> config, string because = "", params object[] reasonArgs )
        {
            var context = new EquivalencyValidationContext
            {
                Subject = subject,
                Expectation = subject,
                CompileTimeType = typeof( T ),
                Reason = because,
                ReasonArgs = reasonArgs
            };

            var validator = new EquivalencyValidator( 
                config( EquivalencyAssertionOptions<T>.Default()
                    .Using<string>( ctx => ctx.Subject.Should().NotBeDefault() ).WhenTypeIs<string>() )
                    .WithStrictOrdering()
                    );

            validator.Steps.Remove( validator.Steps.Single( _ => typeof( TryConversionEquivalencyStep ) == _.GetType() ) );
            validator.Steps.Remove( validator.Steps.Single( _ => typeof( ReferenceEqualityEquivalencyStep ) == _.GetType() ) );
            validator.Steps.Remove( validator.Steps.Single( _ => typeof( SimpleEqualityEquivalencyStep ) == _.GetType() ) );

            validator.Steps.Add( new SimpleIsNotDefaultEquivalencyStep() );

            validator.AssertEquality( context );
        }

    }
}

:

[TestMethod]
[TestCategory( TestCategory2 )]
public void Test_NotBeDefault()
{
    ((Action)(() => ((int?)null).Should().NotBeDefault())).ShouldThrow<AssertFailedException>( "because null is default for int?" );
    ((Action)(() => ((int?)0).Should().NotBeDefault())).ShouldThrow<AssertFailedException>( "because 0 is value type default for int?" );
    ((Action)(() => 0.Should().NotBeDefault())).ShouldThrow<AssertFailedException>( "because null is value type default for int" );
    ((Action)(() => ((int?)1).Should().NotBeDefault())).ShouldNotThrow( "because 1 is not default for int?" );
    ((Action)(() => 1.Should().NotBeDefault())).ShouldNotThrow( "because 1 is not default for int" );

    ((Action)(() => ((object)null).Should().NotBeDefault())).ShouldThrow<AssertFailedException>( "because null is default for object" );
    ((Action)(() => ((object)new object()).Should().NotBeDefault())).ShouldNotThrow( "because not null is not default for object" );

    ((Action)(() => ((string)null).Should().NotBeDefault())).ShouldThrow<AssertFailedException>( "because null is default for string" );
    ((Action)(() => ((string)"").Should().NotBeDefault())).ShouldThrow<AssertFailedException>( "because empty string is default for string" );
    ((Action)(() => ((string)"hi").Should().NotBeDefault())).ShouldNotThrow( "because \"hi\" is not default for string" );

    ((Action)(() => ((bool?)null).Should().NotBeDefault())).ShouldThrow<AssertFailedException>( "because null is default for bool?" );
    ((Action)(() => ((bool?)false).Should().NotBeDefault())).ShouldThrow<AssertFailedException>( "because false is default for bool?" );
    ((Action)(() => false.Should().NotBeDefault())).ShouldThrow<AssertFailedException>( "because false is default for bool" );
    ((Action)(() => ((bool?)true).Should().NotBeDefault())).ShouldNotThrow( "because true is not default for bool?" );
    ((Action)(() => true.Should().NotBeDefault())).ShouldNotThrow( "because true is not default for bool" );

    var actual = new
    {
        i1 = (int?)null,
        i2 = (int?)0,
        i3 = 0,
        i4 = (int?)1,
        i5 = 1,

        s1 = (string)null,
        s2 = (string)"",
        s3 = (string)"hi",

        b1 = (bool?)null,
        b2 = (bool?)false,
        b3 = false,
        b4 = (bool?)true,
        b5 = true,

        n1 = (PlainClass)null,
        n2 = new PlainClass(),
        n3 = new PlainClass
        {
            Key = 10,
            NestedProperty = new object()
        },

        a1 = (PlainClass[])null,
        a2 = new [] { "", "hi", null },
        a3 = new [] { 0, 11 },
        a4 = new [] { new PlainClass { Key = 42 } },

        g1 = (Guid?)null,
        g2 = (Guid)Guid.Empty,
        g3 = Guid.NewGuid()
    };

    ((Action)(() => actual.ShouldNotBeEquivalentToDefault())).ShouldThrow<AssertFailedException>().WithMessage(
@"Expected property i1 to not be default, but found <null>.
Expected property i2 to not be default, but found 0.
Expected property i3 to not be default, but found 0.
Expected property s1 to not be default, but found <null>.
Expected property s2 to not be default, but found """".
Expected property b1 to not be default, but found <null>.
Expected property b2 to not be default, but found False.
Expected property b3 to not be default, but found False.
Expected property n1 to not be default, but found <null>.
Expected property n2.Key to not be default, but found 0.
Expected property n2.NestedProperty to not be default, but found <null>.
Expected property a1 to not be default, but found <null>.
Expected property a2[0] to not be default, but found """".
Expected property a2[2] to not be default, but found <null>.
Expected property a3[0] to not be default, but found 0.
Expected property a4[0].NestedProperty to not be default, but found <null>.
Expected property g1 to not be default, but found <null>.
Expected property g2 to not be default, but found {00000000-0000-0000-0000-000000000000}.

With configuration:
- Select all declared properties
- Match property by name (or throw)
- Invoke Action<String> when info.RuntimeType.IsSameOrInherits(System.String)
- Invoke Action<String> when info.RuntimeType.IsSameOrInherits(System.String)
" );
}
+1

You can add an override for all property types using the `ShouldBeEquivalentTo 'API as follows:

mapped.ShouldBeEquivalentTo(mapped, options => options
   .ExcludingNestedObjects()
   .Using<object>(ctx => ctx.Subject.Should().NotBeNull())
   .When(info => true));

Then, when the predicate determines which properties this user statement should use.

0
source

All Articles