How to call async from [TestMethod]?

I have a rather complicated method in a WebAPI MVC project. It performs a number of actions, including hitting a remote server to authenticate users. Depending on the results of this, it returns either a redirect (page), a string error message, or an object representing all of the monkey business authentication.

It works great when called from a browser. It even works great in debugging. I can even write a redirect to it and call it โ€œmanuallyโ€, passing any parameters that it needs.

The problem I am encountering is causing it from the VS test project created when creating the WebAPI project. I suspect this is because of all the async and await that arise. When it enters it, it eventually returns with the error "Object not installed in the object instance."

Since it works in any other context, I assume that it is because of it inside the test project and it needs to wait. Can anyone give me any advice on this?

Edit: to be very specific, it does not work in the second line of code here:

 BoxConfig boxConfig = new BoxConfig(ClientID, ClientSecret, enterpriseID, prvt, JWTPublicKeyPass, JWTPublicKeyID); BoxJWTAuth boxJWT = new BoxJWTAuth(boxConfig); //This is a JSON web token and is needed to authorize the enterprise level app user. 

Code Context:

This is using Box.com API. The BoxJWT call creates a JSON Web Token. I do not know in which process it fails, because when I trace it, it cannot show me the code for things like PEMReader.cs, etc. (What is connected with cryptographic, bouncy castle). But in particular, the error message says that the source Box.V2.JWTAuth.PEMPasswordFinder.GetPassword()

+6
source share
4 answers

Whenever I create a test method that will test asynchronous code, I make sure that the signature of the test method is Async and returns Task. This avoids the terrible deadlock that comes from asynchronous void. Now you can wait inside the testing method.

Additional information: https://msdn.microsoft.com/en-us/magazine/dn818493.aspx

Example:

 [TestMethod] public async Task myTestMethod() { await MyMethodToTest(); } 
+5
source

This is a great use case for the Moq framework. For example, when you pay attention to a remote call, this service should be mocked. Ideally, you should try to mock various variable aspects so that you can easily and consistently provide a known set of inputs to test the expected set of outputs. Since you have not provided any source code, I will make some assumptions - share some sample code and the corresponding unit tests.

 public class LoginResult { public string RedirectUrl { get; set; } public string FailureMsg { get; set; } public MonkeyBusiness AuthToken { get; set; } } [ HttpPost, AllowAnonymous, Route("api/[controller]/login") ] public async Task<LoginResult> Login( [FromBody] CredentialsModel credentials, [FromServices] ILogger logger, [FromServices] IAuthenticationModule authentication, [FromServices] IAuthClaimsPrincipalBuiler claimsPrincipalBuilder) { try { var result = await authentication.ExternalAuthenticateAsync(credentials); if (result.IsAuthenticated) { var principal = await claimsPrincipalBuilder.BuildAsync(credentials); await HttpContext.Authentication.SignInAsync("Scheme", principal); return new LoginResult { AuthToken = "Too much source to make up just for stackoverflow, " + "but I hope you now get the point..." }; } return new LoginResult { FailureMsg = "Invalid credentials." }; } catch (Exception ex) { logger.LogError(ex.Message, ex); return new LoginResult { FailureMsg = ex.Message }; } } 

Then you can have unit tests like this in which you mock specific implementations of interfaces. In this case, you would mock the IAuthenticationModule and have several tests to ensure that, although the remote server returns your Login logic, it processes it as desired / expected. Mocking async material is very simple.

 using xUnit; [Fact] public async Task LoginThrowsAsExpectedTest() { // Arrange var controller = new LoginController(); var loggerMock = new Mock<ILogger>(); var authModuleMock = new Mock<IAuthenticationModule>(); var expectedMessage = "I knew it!"; // Setup async methods! authModuleMock.Setup(am => am.ExternalAuthenticateAsync(It.IsAny<CredentialsModel>()) .ThrowsAsync(new Exception(expectedMessage)); // Act var result = await controller.Login( new CredentialsModel(), loggerMock.Object, authModuleMock.Object, null); // Assert Assert.Equal(expectedMessage, result.FailureMsg); } 

Or the one you expect to authenticate correctly.

 using xUnit; [Fact] public async Task LoginHandlesIsNotAuthenticatedTest() { // Arrange var controller = new LoginController(); var loggerMock = new Mock<ILogger>(); var authModuleMock = new Mock<IAuthenticationModule>(); var expectedMessage = "Invalid credentials."; // Setup async methods! authModuleMock.Setup(am => am.ExternalAuthenticateAsync(It.IsAny<CredentialsModel>()) .ReturnsAsync(new AuthResult { IsAuthenticated = false }); // Act var result = await controller.Login( new CredentialsModel(), loggerMock.Object, authModuleMock.Object, null); // Assert Assert.Equal(expectedMessage, result.FailureMsg); } 

The thing is, you can still write async and await unit tests using Moq quite easily ...

+1
source

In this particular instance, information is retrieved from the web.config file when it is run.

When the Test project tries to start, the "web.config" context changes, so any user information in the AppSettings project being tested should also be in the App.Config project file. This behavior is compatible with security methods for connection strings and therefore not surprising.

Adding this information to the test project The App.Config file fixed the problem.

0
source

You can use GetAwaiter and GetResult, where Execute is the asynchronous method you want to test.

 var mockParam = new Mock<IParam>(); var sut = new MyClass(mockParam); var result = sut.Execute().GetAwaiter().GetResult(); 

Here you can find more information. https://msdn.microsoft.com/en-us/magazine/dn818493.aspx?f=255&MSPPError=-2147217396 . It briefly mentions other verification methods, but this method will give you the most accurate result.

-2
source

All Articles