2013-06-20 33 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