1

Is there a way in FluentAssertions to avoid the automatic object graph drilldown for .And and .Which cascades? And some point of the drilldown I would like to go back to the root level and check the status code.

Small code example:

    Func<Task> action = async () => await this.someContext.someResponseTask;

    action.Should()
        .Throw<SwaggerOpenApiException<IList<ApiValidationError>>>()
        .Which.Result.Should().Contain(x => x.ErrorCode == errorCode)
        .Which.ErrorDetails.Should().Contain(dictionaryWithParsedErrorDetails)

        // NOTE: This does not work (compile) as it operates on "ErrorDetails",
        //       would like to access root level exception again.
        .Which.StatusCode.Should().Be(HttpStatusCode.Conflict);

Obviously I could wrap await this.someContext.someResponseTask into a try/catch and store the exception into a variable but this is not really an elegant way of doing this, especially with FluentAssertions at your fingertips.

Nkosi
  • 235,767
  • 35
  • 427
  • 472
timmkrause
  • 3,367
  • 4
  • 32
  • 59
  • 1
    The `Should().Throw` is already wrapping the invoked delegate in a try-catch and returning the exception. You should actually be using `ThrowAsync` – Nkosi Sep 17 '19 at 10:56
  • Take a look here https://fluentassertions.com/exceptions/ – Nkosi Sep 17 '19 at 12:10
  • Thanks, this makes totally sense. The actual question still remains though. :) – timmkrause Sep 17 '19 at 13:52
  • I am trying to build a solution but I am not able to figure out the definition of `SwaggerOpenApiException` – Nkosi Sep 17 '19 at 14:02
  • This is custom and generated code. The thing is that `SwaggerOpenApiException` has a property named `Result` and `Result` has a property named `ErrorDetails` so with `.Which` you can only walk deeper into the rabbit hole, but per this mechanism it is not possible to get to upper properties again (here I want to get to the exception level again). What would work is `action.Should().Throw>>().Which.Should().Match>>(e => e.Result.Any(r => r.ErrorCode == errorCode) && ...);` -without yr optimization – timmkrause Sep 17 '19 at 14:47
  • And what is wrong with that example? `Match` looks like a viable solution, – Nkosi Sep 17 '19 at 14:50
  • But this would also have a very bad message without any context when it fails. Thinking about `var someName = action.Should().Throw>>();` and just reuse `someName` twice on different levels. Will try it tomorrow and give feedback. – timmkrause Sep 17 '19 at 14:50
  • Include exception definition and relevant types so I can play around with it as well. – Nkosi Sep 17 '19 at 14:51

1 Answers1

4

These were the 3 solutions I found which enable the traversal of separate paths of the object graph when dealing with an Exception.

The order represents the information richness of the summary in case of a failing test. So #3 for example where everything is put into one expression does not exactly tell you what failed.

The first item is very accurate.

1.

var exceptionAssertion = action.Should().Throw<SwaggerOpenApiException<IList<ApiValidationError>>>();

exceptionAssertion.Which.Result.Should().Contain(x => x.ErrorCode == errorCode);
exceptionAssertion.Which.StatusCode.Should().Be((int)HttpStatusCode.Conflict);

2.

// NOTE: This .Where is not LINQ, it's from FluentAssertions!
action.Should().Throw<SwaggerOpenApiException<IList<ApiValidationError>>>()
    .Where(e => e.Result.Any(r => r.ErrorCode == errorCode))
    .Where(e => e.StatusCode == (int)HttpStatusCode.Conflict);

3.

action.Should()
    .Throw<SwaggerOpenApiException<IList<ApiValidationError>>>()
    .Which.Should().Match<SwaggerOpenApiException<IList<ApiValidationError>>>(
        e =>
            e.Result.Any(r => r.ErrorCode == errorCode) &&
            e.StatusCode == (int)HttpStatusCode.Conflict);
timmkrause
  • 3,367
  • 4
  • 32
  • 59