27

I am trying to set up the fluent assertion for the below condition. But couldnt find a method with expression or an ObjectAssertion with Or().

I got to check the status of my service is of enum value Pending or Active

services.Should().HaveCount(totalServices).And.BeOfType<Service>().Which.ServiceStatusKey.Should().Be(Status.Pending);

I want something like,

.Be(Status.Pending).Or().Be(Status.Active)

Could someone please help me achieving this.

FluentAsserstions Version : 4.1.1 (Latest from Nuget) Attaching the 4.1 FluentAssertions.Primitive namespace.

 // Decompiled with JetBrains decompiler
// Type: FluentAssertions.Primitives.ObjectAssertions
// Assembly: FluentAssertions.Core, Version=4.1.1.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a
// MVID: 090116C5-E9A5-4878-B62E-DE0EBFEBBE14
// Assembly location: C:\RA\P4V\BOSS\trunk\M5Portal\packages\FluentAssertions.4.1.1\lib\net45\FluentAssertions.Core.dll

using FluentAssertions;
using FluentAssertions.Common;
using FluentAssertions.Execution;
using System;
using System.Diagnostics;

namespace FluentAssertions.Primitives
{
  /// <summary>
  /// Contains a number of methods to assert that an <see cref="T:System.Object"/> is in the expected state.
  /// 
  /// </summary>
  [DebuggerNonUserCode]
  public class ObjectAssertions : ReferenceTypeAssertions<object, ObjectAssertions>
  {
    /// <summary>
    /// Returns the type of the subject the assertion applies on.
    /// 
    /// </summary>
    protected override string Context
    {
      get
      {
        return "object";
      }
    }

    public ObjectAssertions(object value)
    {
      this.Subject = value;
    }

    /// <summary>
    /// Asserts that an object equals another object using its <see cref="M:System.Object.Equals(System.Object)"/> implementation.
    /// 
    /// </summary>
    /// <param name="expected">The expected value</param><param name="because">A formatted phrase as is supported by <see cref="M:System.String.Format(System.String,System.Object[])"/> explaining why the assertion
    ///             is needed. If the phrase does not start with the word <i>because</i>, it is prepended automatically.
    ///             </param><param name="reasonArgs">Zero or more objects to format using the placeholders in <see cref="!:because"/>.
    ///             </param>
    public AndConstraint<ObjectAssertions> Be(object expected, string because = "", params object[] reasonArgs)
    {
      Execute.Assertion.BecauseOf(because, reasonArgs).ForCondition(ObjectExtensions.IsSameOrEqualTo(this.Subject, expected)).FailWith("Expected {context:object} to be {0}{reason}, but found {1}.", expected, this.Subject);
      return new AndConstraint<ObjectAssertions>(this);
    }

    /// <summary>
    /// Asserts that an object does not equal another object using its <see cref="M:System.Object.Equals(System.Object)"/> method.
    /// 
    /// </summary>
    /// <param name="unexpected">The unexpected value</param><param name="because">A formatted phrase explaining why the assertion should be satisfied. If the phrase does not
    ///             start with the word <i>because</i>, it is prepended to the message.
    ///             </param><param name="reasonArgs">Zero or more values to use for filling in any <see cref="M:System.String.Format(System.String,System.Object[])"/> compatible placeholders.
    ///             </param>
    public AndConstraint<ObjectAssertions> NotBe(object unexpected, string because = "", params object[] reasonArgs)
    {
      Execute.Assertion.ForCondition(!ObjectExtensions.IsSameOrEqualTo(this.Subject, unexpected)).BecauseOf(because, reasonArgs).FailWith("Did not expect {context:object} to be equal to {0}{reason}.", unexpected);
      return new AndConstraint<ObjectAssertions>(this);
    }

    /// <summary>
    /// Asserts that an object is an enum and has a specified flag
    /// 
    /// </summary>
    /// <param name="expectedFlag">The expected flag.</param><param name="because">A formatted phrase explaining why the assertion should be satisfied. If the phrase does not
    ///             start with the word <i>because</i>, it is prepended to the message.
    ///             </param><param name="reasonArgs">Zero or more values to use for filling in any <see cref="M:System.String.Format(System.String,System.Object[])"/> compatible placeholders.
    ///             </param>
    public AndConstraint<ObjectAssertions> HaveFlag(Enum expectedFlag, string because = "", params object[] reasonArgs)
    {
      Execute.Assertion.BecauseOf(because, reasonArgs).ForCondition(this.Subject != null).FailWith("Expected type to be {0}{reason}, but found <null>.", (object) expectedFlag.GetType()).Then.ForCondition(this.Subject.GetType() == expectedFlag.GetType()).FailWith("Expected the enum to be of type {0} type but found {1}{reason}.", (object) expectedFlag.GetType(), (object) this.Subject.GetType()).Then.Given<Enum>((Func<Enum>) (() => this.Subject as Enum)).ForCondition((Func<Enum, bool>) (@enum => @enum.HasFlag(expectedFlag))).FailWith("The enum was expected to have flag {0} but found {1}{reason}.", (Func<Enum, object>) (_ => (object) expectedFlag), (Func<Enum, object>) (@enum => (object) @enum));
      return new AndConstraint<ObjectAssertions>(this);
    }

    /// <summary>
    /// Asserts that an object is an enum and does not have a specified flag
    /// 
    /// </summary>
    /// <param name="unexpectedFlag">The unexpected flag.</param><param name="because">A formatted phrase explaining why the assertion should be satisfied. If the phrase does not
    ///             start with the word <i>because</i>, it is prepended to the message.
    ///             </param><param name="reasonArgs">Zero or more values to use for filling in any <see cref="M:System.String.Format(System.String,System.Object[])"/> compatible placeholders.
    ///             </param>
    public AndConstraint<ObjectAssertions> NotHaveFlag(Enum unexpectedFlag, string because = "", params object[] reasonArgs)
    {
      Execute.Assertion.BecauseOf(because, reasonArgs).ForCondition(this.Subject != null).FailWith("Expected type to be {0}{reason}, but found <null>.", (object) unexpectedFlag.GetType()).Then.ForCondition(this.Subject.GetType() == unexpectedFlag.GetType()).FailWith("Expected the enum to be of type {0} type but found {1}{reason}.", (object) unexpectedFlag.GetType(), (object) this.Subject.GetType()).Then.Given<Enum>((Func<Enum>) (() => this.Subject as Enum)).ForCondition((Func<Enum, bool>) (@enum => !@enum.HasFlag(unexpectedFlag))).FailWith("Did not expect the enum to have flag {0}{reason}.", new object[1]
      {
        (object) unexpectedFlag
      });
      return new AndConstraint<ObjectAssertions>(this);
    }
  }
}
Raghav
  • 2,890
  • 7
  • 36
  • 56

4 Answers4

30

You can use Match for this case.

ServiceStatusKey.Should().Match<Status>(p=>p==Status.Pending || p == Status.Active);

FluentAssertions does not support an Or()-statement. Use Match() for general purpose assertions.

Jehof
  • 34,674
  • 10
  • 123
  • 155
6

Try this way:

(services.Status == Status.Pending || services.Status == Status.Active).Should().BeTrue("Message when it is false.");
ono2012
  • 4,967
  • 2
  • 33
  • 42
  • 3
    this sort of works, but if if the condition is false, the error message does not indicate what the actual value of service.Status was. The Match() function works better in this regard. – user1691896 Jan 16 '20 at 07:34
4

If you could upgrade your FluentAssertion version, now there is the method BeOneOf that does what you want

ihebiheb
  • 3,673
  • 3
  • 46
  • 55
3

OR is a lot harder then AND, as what does

A().Or.B().And.C().Or.D()

mean?

The real problem is trying to support Or while also supporting And. Therefore I would be looking at writing a methods like

services.ShouldDoAtLeastOneof(params Predicate predicates)

as anything more flexible is questionable for asserts in unit tests.

(As I have never used Fluent Assertions, I don't know if it has a built in method that does this. I think the BeOneOf well solve the very limited case of "OR" asked for in the quesion, as shown in Matthew Watson's answer.)

Ian Ringrose
  • 51,220
  • 55
  • 213
  • 317
  • BeOneOf is not aavailable in 4.1 – Raghav Jan 07 '16 at 14:48
  • @Raghav, then back-port it yourself from 4.1 into your own source code. – Ian Ringrose Jan 07 '16 at 15:40
  • lol, very astute Ian. I was thinking this but wondered if there was something naive that would work in the simple case. Really all it needs is a check in the builder that prevents mixing of And and Or - that would solve the problem. Buuut I just checked the FluentAssertion source - there's no builder and the And restriction is baked in where a builder should be. Oh well :) Good answer – Seth Jul 03 '18 at 23:35