2013-12-23 54 views
1

我想測試下面的方法:如何使用視圖模型嘲笑方法MVC4與實體框架4.0

public ActionResult Index() 
{ 
    var transactions = db.Transactions.Include(t => t.User) 
          .GroupBy(t => t.UserId) 
          .Select(group => new TransactionViewModel 
          { 
           User = group.FirstOrDefault().User.FullName, 
           UserId = group.FirstOrDefault().UserId, 
           Total = (group.Sum(t => t.TransactionAmount)) 
          }); 

    // Show lowest balance first 
    return View(transactions.ToList()); 
} 

這裏Transaction模型具有Orders列表,有一個外鍵User和一些更多的屬性見:

public class Transaction 
{ 
    public int TransactionId { get; set; } 
    public DateTime Date { get; set; } 
    public int UserId { get; set; } 
    public List<Order> Orders { get; set; } 
    public decimal TransactionAmount { get; set; } 
    public virtual User User { get; set; } 
} 

TransactionViewModel如下所示:

public class TransactionViewModel 
{ 
    public string User { get; set; } 
    public int UserId { get; set; } 
    public decimal Total { get; set; } 
} 

和用於計算屬於用戶不同交易的Total

爲了測試這個方法我有一個FakeDbSet並使用FakeContext(這在其他控制器的測試都工作)在以下設置:

[TestClass] 
public class TransactionControllerTest 
{ 
    TransactionController trController; 

    [TestInitialize] 
    public void TransactionControllerTestInitialize() 
    { 
     // Arrange 
     var memoryTransactionItems = new FakeDbSet<Transaction> 
     { 
      new Transaction { 
       Date = DateTime.Today, 
       TransactionAmount = 5.10M, 
       UserId = 1, 
       Orders = new List<Order>{ 
        // Categorie 2 and confirmed 
        new Order { OrderId = 2, 
           UnitPrice = 2.00M, 
           Quantity = 1, 
           Date = DateTime.Today, 
           IsConfirmed = true, 
           User = new User { 
            Name = "Kees", 
            FullName="Kees Piet", 
            Email = "[email protected]", 
            isAvailable = true, 
            UserId = 1 
           }, 
           Product = new Product { 
            Category = new Category { 
             CategoryId = 2, 
             Name = "Categorie2" 
            }, 
            Name = "Testproduct2", 
            Price = 2.00M, 
            Visible = true 
           } 
        }, 
        // Categorie 2 and confirmed 
        new Order { OrderId = 2, 
           UnitPrice = 1.00M, 
           Quantity = 1, 
           Date = DateTime.Today, 
           IsConfirmed = true, 
           User = new User { 
            Name = "Jan", 
            FullName="Jan Piet", 
            Email = "[email protected]", 
            isAvailable = true, 
            UserId = 2 
           }, 
           Product = new Product { 
            Category = new Category { 
             CategoryId = 2, 
             Name = "Categorie2" 
            }, 
            Name = "Testproduct2", 
            Price = 3.10M, 
            Visible = true 
           } 
        } 
       } 
      } 
     }; 


     // Create mock units of work 
     var mockData = new Mock<FakeContext>(); 
     mockData.Setup(m => m.Transactions).Returns(memoryTransactionItems); 

     // Setup controller 
     trController = new TransactionController(mockData.Object); 
    } 

    [TestMethod] 
    public void TestTransactionIndex() 
    { 
     // Invoke 
     var viewResult = trController.Index() as ViewResult; 
     var transactionsFromView = (IEnumerable<TransactionViewModel>)viewResult.Model; 

     // Assert 
     Assert.AreEqual(1, transactionsFromView.Count(), 
      "The amount of transactions added to the Index View should be 1."); 
    } 
} 

當我運行TestTransactionIndex我收到以下錯誤:

Test Name: TestTransactionIndex Test Outcome: Failed Test Duration: 0:00:30.6276475

Result Message: Test method Tests.Controllers.TransactionControllerTest.TestTransactionIndex threw exception: System.NullReferenceException: Object reference not set to an instance of an object. Result StackTrace: at lambda_method(Closure , IGrouping 2) at System.Linq.Enumerable.WhereSelectEnumerableIterator 2.MoveNext()
at System.Collections.Generic.List 1..ctor(IEnumerable 1 collection)
at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source) at Controllers.TransactionController.Index()

我覺得這很奇怪,因爲我以適當的方式設置了我的模擬單元。我希望有人能解釋我是如何能夠正確地發送FakeDbSet<Transaction>的觀點,並沒有得到一個NullReferenceException

/編輯根據要求,這裏有contructors爲TransactionController

private IContext _context; 

public TransactionController() 
{ 
    _context = new Context(); 
} 

public TransactionController(IContext context) 
{ 
    _context = context; 
} 
+0

您可以發佈從TransactionController – tire0011

+0

建設者@ user1511384是的。完成。 –

+1

在索引方法你有'db.Transactions.Include(T => t.User)',你嘗試過與用戶的假dbSet配置假背景?因爲選擇具有此線'用戶= group.FirstOrDefault()。User.FullName,'將給出一個空引用如果_user_爲空。像'mockData.Setup(M => m.Users).Returns(....)'如果不工作,你可以嘗試在填充你的假交易 –

回答

1

在索引方法查詢包括行:

db.Transactions.Include(t => t.User) 

而且選擇部分查詢的是在Transaction類使用User屬性來填充TransactionViewModel

User = group.FirstOrDefault().User.FullName, 

如果Transaction中的User屬性爲null,那麼該行將拋出NullReferenceException。因此,在單元測試中使用僞造對象執行時,需要該查詢的結果包含非空的User屬性。

我不確定您的虛假上下文和DbSets是如何工作的,但最簡單的方法就是在您的虛假memoryTransactionItems中填充交易的用戶屬性。

您也可以嘗試加入一個假的用戶dbset如下面的代碼片段(我假設你有一個用戶DbSet在你的EF上下文中):

var memoryUsers = new FakeDbSet<User> 
{ 
    new User{ UserId = 1, ... }, 
    ... 
}; 

mockData.Setup(m => m.Users).Returns(memoryUsers);