Reputation: 55
I have sealed classes with async(.net 4.5) methods that need to be mocked. I'm using Microsoft Fakes and so they will be "shims". The following code is an example of what I need to do. It builds but when run and the "LoginAsync" method within the "Login" controller method is called, the test hangs.
[TestMethod]
public async Task LoginPost_Returns() {
using (ShimsContext.Create()) {
var c = new TestController();
var user=new User();
Fakes.ShimUserManager.AllInstances.LoginAsyncString = (um, u) => new Task<IUser>(() => { return user; });
//call controller method
var result = await c.Login(model, returnUrl) as ViewResult;
var expectedViewName = "Index";
Assert.IsNotNull(result);
Assert.AreEqual(expectedViewName, result.ViewName);
}
//Controller method
public async Task<ActionResult> Login(LoginModel model, string returnUrl) {
var user = await UserManager.LoginAsync(model.UserName, model.password);
return View();
}
Upvotes: 3
Views: 4271
Reputation: 245008
Fakes.ShimUserManager.AllInstances.LoginAsyncString =
(um, u) => new Task<IUser>(() => { return user; });
This creates an unstarted Task
. Your code hangs, because the Task
is never started. To fix that, you can:
Task.Run()
(or Task.Factory.StartNew()
), which returns an already started Task
.Task.FromResult()
. which returns an already completed Task
.Make the lambda async
:
Fakes.ShimUserManager.AllInstances.LoginAsyncString = async (um, u) => user;
Upvotes: 3
Reputation: 457217
Don't use the Task
constructor in async
code. If you just need a completed Task
with a return value, use Task.FromResult
:
IUser user = new User();
Fakes.ShimUserManager.AllInstances.LoginAsyncString = (um, u) => Task.FromResult(user);
As an additional tip, it's a good idea to cover these cases in your unit tests:
Task.FromResult(user)
).Task.Run(() => user)
).Task.Run(() => { throw new InvalidOperationException("or whatever"); return user; })
).Upvotes: 9