简体   繁体   中英

Error in Tests: The 'async' operator lacks await operator

I have a unit test to check null parameter. The test method is something like this:

[TestMethod]
public async Task Test_NullParam()
{
    Mock<IAuth> mockAuth = new Mock<IAuth>();
    Task<AuthenticationResult> mockResult = null;

    await Assert.ThrowsExceptionAsync<ArgumentNullException>(async () =>
    {
        mockAuth
          .Setup(x => x.Authenticate(null, param2, param3, param4))
          .Returns(mockResult);
    });
}

It is showing error under (async () .

The error is:

The async lacks await operator

mockResult showing:

Result is always null

** Edited **

Class code below:

  public async Task<AuthenticationResult> Authenticate(string param1, string param2, string param3, string param4)
    {
        try
        {
            var authContext = new AuthenticationContext(param1);
            if (authContext.TokenCache.ReadItems().Any()) authContext = new AuthenticationContext(authContext.TokenCache.ReadItems().First().Authority);
            var authResult = await
            authContext.AcquireTokenAsync(param2, param3, new Uri(param4), new PlatformParameters(PromptBehavior.Auto, false));
            return authResult;
        }
        catch (Exception)
        {
            return null;
        }
    }

You are getting the error because you aren't performing an await statement in the lambda you've passed to ThrowsExceptionAsync .

More importantly, you appear to be performing your mocking setup within the lambda instead of actually executing the method that you are testing. You should move your mocking outside of the lambda, and execute the method you are testing within the lambda like this:

[TestMethod]
public async Task Test_NullParam()
{
    // create all of your mocks that are required to run this test
    Mock<IAuth> mockAuth = new Mock<IAuth>();
    Task<AuthenticationResult> mockResult = null;
    mockAuth
        .Setup(x => x.Authenticate(null, param2, param3, param4))
        .Returns(mockResult);

    // you actually need to create the concrete type you want to test
    // you shouldn't make a unit test that just uses mocks!

    // construct the concrete type and pass in any mocks that the 
    // object requires
    var objectToTest = new MyRealObject(mockAuth.Object);

    await Assert.ThrowsExceptionAsync<ArgumentNullException>(async () =>
    {
        // actually execute the real method here, the assertion
        // is expecting this method to throw an exception, and will
        // only pass if an exception is thrown
        await objectToTest.MyMethodAsync();
    });
}

Note that if the method you are testing isn't asynchronous, you should change your unit test to not be async, and use Assert.ThrowsException instead.

Edit:

In response to your comment about mocking the Task , if you want to mock a Task that returns a null AuthenticationResult , you should do the following:

// declare the result
AuthenticationResult mockResult = null;

// set up the authenticate method to return the task
mockAuth
    .Setup(x => x.Authenticate(null, param2, param3, param4))
    .Returns(Task.FromResult(mockResult));

The important part here is Task.FromResult . This will give you an actual Task , that will return null when awaited, instead of the Task being null itself.

If you want to use an AuthenticationResult that isn't null, you are going to run into problems, due to the AuthenticationResult class having no public constructors, and no way of getting one, other than actually running a real Authenticate request. Out of the box, AuthenticationResult isn't mockable, which is why @Nkosi has provided a link to an article about using wrapper classes to make it mockable .

Edit 2

It seems the linked blog post is not complete, as it does not include all the wrapper types required, so I decided to make them myself so this answer could be complete.

Due to the character limit constraints on StackOverflow answers, I've had to put the files on Github here: https://github.com/DoctaJonez/DoctorJones.IdentityModel.Clients.ActiveDirectory/tree/master

Once you've added these classes and interfaces, you'll be able to do the following:

// you can now mock authentication results, and set them up to 
// emulate whatever conditions you like
var mockResult = new Mock<IAuthenticationResultWrapper>();

// you'll need to use the IAuthenticationContextWrapper, so all the
// method signatures know about our new IAuthenticationResultWrapper
var mockAuth = new Mock<IAuthenticationContextWrapper>();

// here's how we mock the IAuthenticationContextWrapper to return our
// mocked IAuthenticationResultWrapper
mockAuth.Setup(x => x.Authenticate(null, param2, param3, param4))
        .Returns(Task.FromResult(mockResult.Object));

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM