[英]How to mock method with ViewModel in MVC4 with Entity Framework 4.0
I want to test the following method: 我想测试以下方法:
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());
}
Here the Transaction
model has a list of Orders
, has a foreign key to User
and some more properties, see: 这里的
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; }
}
The TransactionViewModel
looks as follows: TransactionViewModel
如下所示:
public class TransactionViewModel
{
public string User { get; set; }
public int UserId { get; set; }
public decimal Total { get; set; }
}
and is used to calculate the Total
of different transactions belonging to a user. 并用于计算属于用户的不同交易的
Total
。
To test this method I have a FakeDbSet
and use a FakeContext
(which both work in tests of other controllers) in the following Setup: 为了测试此方法,我在以下安装程序中使用了
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 = "Kees@DeHond.nl",
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 = "Jan@DeBouvrier.de",
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.");
}
}
When I run the TestTransactionIndex
I get the following error: 当我运行
TestTransactionIndex
,出现以下错误:
Test Name: TestTransactionIndex Test Outcome: Failed Test Duration: 0:00:30.6276475
测试名称:TestTransactionIndex测试结果:测试失败持续时间: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.
结果消息:测试方法Tests.Controllers.TransactionControllerTest.TestTransactionIndex引发异常:System.NullReferenceException:对象引用未设置为对象的实例。 Result StackTrace: at lambda_method(Closure , IGrouping
2 ) at System.Linq.Enumerable.WhereSelectEnumerableIterator
2.MoveNext()结果StackTrace:位于
2 ) at System.Linq.Enumerable.WhereSelectEnumerableIterator
2.MoveNext()的lambda_method(Closure,IGrouping2 ) at System.Linq.Enumerable.WhereSelectEnumerableIterator
)
at System.Collections.Generic.List1..ctor(IEnumerable
1 collection)在System.Collections.Generic.List
1..ctor(IEnumerable
1 collection)
at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source) at Controllers.TransactionController.Index()在System.Linq.Enumerable.ToList [TSource](IEnumerable`1源)在Controllers.TransactionController.Index()
I find this strange since I setup my mock units in the proper way. 我发现这很奇怪,因为我以正确的方式设置了模拟单元。 I hope someone can explain how I can properly send the
FakeDbSet<Transaction>
to the view and not get a NullReferenceException
. 我希望有人能够解释如何正确地将
FakeDbSet<Transaction>
发送到视图,而不获取NullReferenceException
。
/Edit As requested, here are the contructors for TransactionController
: / Edit根据要求,这是
TransactionController
的构造函数:
private IContext _context;
public TransactionController()
{
_context = new Context();
}
public TransactionController(IContext context)
{
_context = context;
}
The query in your index method includes the line: 索引方法中的查询包括以下行:
db.Transactions.Include(t => t.User)
And the Select part of the query is using the User
property in the Transaction
class to populate the TransactionViewModel
as in 查询的Select部分正在使用
Transaction
类中的User
属性来填充TransactionViewModel
如下所示
User = group.FirstOrDefault().User.FullName,
That line will throw a NullReferenceException if the User property in the Transaction is null. 如果Transaction中的User属性为null,则该行将引发NullReferenceException 。 So you need the result of that query to contain a not null User property when executed in your unit test using the fake objects.
因此,当您在使用伪造对象的单元测试中执行查询时,您需要该查询的结果包含不为null的User属性。
I am not sure how your fake context and DbSets work, but the easiest thing to try is to populate the User property of the Transactions in your fake memoryTransactionItems
. 我不确定您的虚假上下文和DbSets如何工作,但是最容易尝试的方法是在虚假的
memoryTransactionItems
填充Transactions的User属性。
You may also try adding a fake users dbset as in the next code snippet (I'm assuming you have a Users DbSet in your EF context): 您还可以尝试在下一个代码片段中添加伪造的用户dbset(我假设您在EF上下文中有一个Users DbSet):
var memoryUsers = new FakeDbSet<User>
{
new User{ UserId = 1, ... },
...
};
mockData.Setup(m => m.Users).Returns(memoryUsers);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.