【发布时间】:2013-12-12 08:37:31
【问题描述】:
我想对采用FormCollection 并具有Find 方法的控制器进行单元测试。我已经成功实现了一个假的DbContext 和一个FakeDbSet(来自here),这样我就可以在我的测试中使用假对象。
我要测试的具体方法是这样的:
//
// POST: /Order/SettleOrders
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult SettleOrders(FormCollection c)
{
int i = 0;
if (ModelState.IsValid)
{
var ordersCollection = new List<Order>();
// Get values of form
var OrderIdArray = c.GetValues("item.OrderId");
var UnitPriceArray = c.GetValues("item.UnitPrice");
for (i = 0; i < OrderIdArray.Count(); i++)
{
// Find order in database and update the unitprice and isconfirmed status
Order order = db.Orders.Find(Convert.ToInt32(OrderIdArray[i]));
order.UnitPrice = Convert.ToDecimal(UnitPriceArray[i]);
order.IsConfirmed = true;
db.SetModified(order);
ordersCollection.Add(order);
}
db.SaveChanges();
}
// Return orders of this date to view
var currentDate = DateTime.Today;
var orders = db.Orders.Include(o => o.Product)
.Include(o => o.User)
.Where(o => o.Date == currentDate);
return View("Confirmation", orders.ToList().OrderBy(o => o.User.Name));
}
这就是我使用Moq 设置OrderController 测试的方式:
[TestInitialize]
public void OrderControllerTestInitialize()
{
// Arrange
var unconfirmedMemoryItems = new FakeOrderSet
{
// @TODO Tests/methods should ideally not be dependent on DateTime.Today...
new Order { OrderId = 1, UnitPrice = 1.00M, Quantity = 2, Date = DateTime.Today, IsConfirmed = false },
new Order { OrderId = 2, UnitPrice = 2.00M, Quantity = 1, Date = DateTime.Today, IsConfirmed = false }
};
// Create mock unit of work
var unconfirmedMockData = new Mock<ISeashellBrawlContext>();
unconfirmedMockData.Setup(m => m.Orders).Returns(confirmedMemoryItems);
// Setup controller
unconfirmedOrderController = new OrderController(confirmedMockData.Object);
}
那么测试就这样进行,确认未确认的订单正在变成确认。
[TestMethod]
public void TestSettleOrdersPost()
{
// Invoke
FormCollection form = CreatesettleOrdersPostFormCollection();
var viewResult = unconfirmedOrderController.SettleOrders(form) as ViewResult;
var ordersFromView = (IEnumerable<Order>)viewResult.Model;
// Assert
Assert.AreEqual(3, ordersFromView.ElementAt(0).Quantity,
"New item should be added to older one since it has same index and is of same date");
Assert.AreEqual(true, ordersFromView.ElementAt(0).IsConfirmed,
"The item should also be set to confirmed");
}
// Helper methods
private static FormCollection CreatesettleOrdersPostFormCollection()
{
FormCollection form = new FormCollection();
form.Add("item.OrderId", "1");
form.Add("item.UnitPrice", "2.00");
form.Add("item.OrderId", "2");
form.Add("item.UnitPrice", "3.00");
return form;
}
很遗憾,我收到以下错误消息:
测试名称:TestSettleOrdersPost 结果消息:
System.NullReferenceException:对象引用未设置为实例 的一个对象。结果堆栈跟踪:在 Controllers.OrderController.SettleOrders(FormCollection c) 在 Controllers\OrderController.cs:第 121 行
这可能与伪造的Find 方法没有做它应该做的事情有关。我使用的 find 方法是这样的:
class FakeOrderSet : FakeDbSet<Order>
{
public override Order Find(params object[] keyValues)
{
var id = Convert.ToInt32(keyValues[0]);
Order o = null;
IQueryable<Order> keyQuery = this.AsQueryable<Order>();
foreach (var order in keyQuery)
{
if (order.OrderId == id)
{
o = order;
}
}
return o;
}
}
我不知道如何改进此代码。看着它,我相信它应该可以工作。由于我是一个 7 天的单元测试新手,因此这种信念并没有多大价值。我希望有人可以帮助我。
【问题讨论】:
-
"这可能不得不做" ...在第 121 行之前放置一个断点,看看会发生什么。
-
@CodeCaster 在进行单元测试时如何在代码中设置断点?我无法做到这一点,我也无法写信给控制台......拥有这些工具将是一个巨大的帮助。啊,我找到了答案:
Debug->Tests In Current Context(或其他) -
见stackoverflow.com/questions/12594018/… 和stackoverflow.com/questions/4103712/…。右键单击测试资源管理器中的测试,如果其他方式不起作用,请单击调试。
标签: c# unit-testing visual-studio-2012 moq