I want to verify (assert) that certain properties on my DTO object are set. I was trying to do it with Fluent Assertions, but the following code does not seem to work:
mapped.ShouldHave().Properties(
x => x.Description,
...more
x => x.Id)
.Should().NotBeNull();
Is it possible to achieve that with Fluent Assertions, or other tool ? Fluent assertions have ShouldBeEquivalentTo, but actually I only care whether those are not nulls/empties, so that one I was not able to utilize.
Of course I can just do an Assert on each property level, but interested in some more elegant way.
Indeed, Properties
method returns PropertiesAssertion
, which only have EqualTo
method for equality comparison. No NotEqualTo
method or NotNull
. In your test, your expected PropertiesAssertion
not to be null
, that's why it will always pass.
You can implement a AssertionHelper
static class and pass an array of Func
s, which you would use to validate an object. This is very naive implementation and you won't get nice error reporting, but I'm just showing the general idea
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");
}
Now this test would fail with some of the properties are null
message
var myDto = new MyDto();
myDto.CheckAllPropertiesAreNotNull(x => x.Description,
x => x.Id);
Two problems with that solution:
Id
property is of a value type, getter(objectToInspect) == null
is always false
To address the first point, you can:
CheckAllPropertiesAreNotNull
, each will have different number of Generic Func<TInput, TFirstOutput> firstGetter
, then you would compare return value of each getter to corresponding default(TFirstOutput)
Activator
, to create default instance and call Equals
I'll show you the second case. You can create a IsDefault
method, which would accept parameter of type object
(note, that this could be a boxed int):
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);
}
Now our overall code, that handler value types will look like:
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");
}
To address the second point, you can pass an Expression<Func<T, object>>[] getters
, which will contain information about a called Property. Create a method GetName
, which would accept Expression<Func<T, object>>
and return the called property name
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;
}
Now the resulting code would look like:
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);
}
}
Now for my call
myDto.CheckAllPropertiesAreNotDefault(x => x.Description,
x => x.Id);
I get
expected next properties not to have default values: Description, Id
error message. In my Dto Description
is a string
and Id
is a value type int
. If I set that properties to some non-default values, I'll get no error and test will pass.