3

I am using a client to interact with CloudMQTT API. I am trying to create a user but after trying the code provided below, I was not able to create a user. When using the code provided within the Github repository for this project, I noticed that I am unable to make use of a ShouldThrow() method (apparently it should be provided by Fluent Assertions).

I did find a post on StackOverflow which looked very similar to the problem I am having. In the question is mentioned that FluentAssertions does not support async methods. In the example code for the client, however, I can see that the ShouldThrow() method is used regardless of this fact.

How could I get the ShoudldThrow() to work or do I even need it to work (because I think it is only supposed to be required in this code if you are applying unit testing)?

This is what a tried so far:

public static async void CreateCloudUser(ICloudMqttApi client)
{
    var users = await client.GetUsers();
    Console.WriteLine($"Creating a user. Current users available: {users.Count}");
    var expectedUser = new NewUser
    {
        Password = $"{Guid.NewGuid()}",
        Username = $"staging-{Guid.NewGuid()}",
    };

    var createUserResponse = await client.CreateUser(expectedUser);
    createUserResponse.IsSuccessStatusCode.Should().BeTrue();

    var actual = await client.GetUser(expectedUser.Username);
    actual.Should().NotBeNull();
    actual.Username.Should().Be(expectedUser.Username);

    //users.Should().Contain(u => u.Username == expectedUser.Username); // <-- This throws an exception as well, but not of importance for this specific question.

    Func<Task> verifyUser = async () => await client.GetUser(expectedUser.Username);
    verifyUser.ShouldThrow<ApiException>() // <-- Not recognized
            .And.StatusCode.Should().Be(HttpStatusCode.NotFound);

    Console.WriteLine($"Created a user. Current users available: {users.Count}");
}

The client is defined in the way as provided in the documentation for the client right before calling the method:

var client = CloudMqttApi.GetInstance("username", "password");

The user count will result in the same number before and after executing the method (which obviously should have incremented).

Barrosy
  • 1,407
  • 2
  • 25
  • 56
  • Is `CreateCloudUser` the test method or the method under test? – Nkosi Jan 15 '20 at 10:45
  • It's a custom made method I wrote myself. It contains the same code (or at least should work the same as) provided below the test method found within the Github repository. @Nkosi – Barrosy Jan 15 '20 at 12:24

1 Answers1

6

Given the asynchronous nature of the shown code, the syntax should be

//...

var deleteResponse = await client.DeleteUser(expectedUser.Username);
deleteResponse.IsSuccessStatusCode.Should().BeTrue();

Func<Task> verifyUser = async () => await client.GetUser(expectedUser.Username);

var exceptionAssertion = await verifyUser.Should().ThrowAsync<ApiException>();
exceptionAssertion.And.StatusCode.Should().Be(HttpStatusCode.NotFound);

//...

Reference FluentAssertions: Exceptions

Also avoid using async void. Have the function return Task

public static async Task CreateCloudUser(ICloudMqttApi client) {

    //...

}

Reference Async/Await - Best Practices in Asynchronous Programming

Nkosi
  • 235,767
  • 35
  • 427
  • 472
  • I am using a default console app as per example for this problem. When I am trying to call `CreateCloudUser(client);` within the main method of my `Program` class, the compiler will complain about the fact that the call is not being awaited. When I await that method, the compiler will complain about the main method not being an async method? Or should I make this main method an async Task method as well? – Barrosy Jan 15 '20 at 12:16
  • And fixing that problem apparently does not fix the issue of not being able to create users. It does really help though. – Barrosy Jan 15 '20 at 12:26
  • @Barrosy, your options are either to make `Main` an async method and await the call, or to call `CreateCloudUser(...).Wait();` which will block execution until `CreateCloudUser` returns. What you _really_ need to do is learn how async works, where its complexities lie, and how/where to use it appropriately (it isn't "magic performance juice"). – Tullo_x86 Jan 15 '20 at 23:22
  • I figured it had something to do with async, I did not know about the `.Wait()` part of your comment. @Tullo_x86 . This really helps. – Barrosy Jan 16 '20 at 12:29
  • 2
    @Barrosy check added link in answer to find out more – Nkosi Jan 16 '20 at 12:31
  • exceptionAssertion.And.StatusCode looks like a hack(assuming some internal/not documented structure) Can we use instead Where: await verifyUser.Should().ThrowAsync() .Where(e => e..StatusCode==HttpStatusCode.NotFound) – Michael Freidgeim Dec 13 '22 at 04:29