2013-06-20 47 views
3

我使用需要模拟的异步(.net 4.5)方法封装类。我使用Microsoft Fakes,因此它们将成为“垫片”。下面的代码是我需要做的一个例子。它会构建,但是在运行时以及“Login”控制器方法中的“LoginAsync”方法被调用时,测试会挂起。使用Microsoft Fakes进行模拟

[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(); 
} 

回答

9

不要使用Task构造函数async代码。如果你只需要完成Task有一个返回值,使用Task.FromResult

IUser user = new User(); 
Fakes.ShimUserManager.AllInstances.LoginAsyncString = (um, u) => Task.FromResult(user); 

作为一个额外的小费,它在你的单元测试,以覆盖这些案件是一个好主意:

  • 同步成功(Task.FromResult(user) )。
  • 异步成功(Task.Run(() => user))。
  • 异步错误(Task.Run(() => { throw new InvalidOperationException("or whatever"); return user; }))。
+0

在错误情况下,不会明确指定委托类型比无法返回的'return'更清晰吗? – svick

+0

不错。非常感谢。 –

3
Fakes.ShimUserManager.AllInstances.LoginAsyncString = 
    (um, u) => new Task<IUser>(() => { return user; }); 

这将创建一个未开始Task。您的代码挂起,因为Task从未启动。为了解决这个问题,您可以:

  1. 使用Task.Run()(或Task.Factory.StartNew()),它返回一个已经开始Task
  2. 使用Task.FromResult()。它返回已完成的Task
  3. 使拉姆达async

    Fakes.ShimUserManager.AllInstances.LoginAsyncString = async (um, u) => user; 
    
+0

关于从未开始任务的好处。我正在思考这些问题。第二点,LoginAsync方法(返回IUser)是一个业务层方法,Login方法(返回ViewResult)是一个控制器操作方法。他们不是一样的方法。 –

+0

async lambda?那样有用吗?我得看看那个。 –

+0

@ user2507103我想我现在明白了。我从我的答案中删除了关于类型的部分。 – svick

相关问题