我的代码完美无缺,但是。在这种情况下最好的做法是什么?
以下是重要的代码。
这是在控制器中。
private IProductRepository repository;
[HttpPost]
public ActionResult Delete(int productId) {
Product prod = repository.Products.FirstOrDefault(p => p.ProductID == productId);
if (prod != null) {
repository.DeleteProduct(prod);
TempData["message"] = string.Format("{0} was deleted", prod.Name);
}
return RedirectToAction("Index");
}
这是存储库(接口等)
public interface IProductRepository {
IQueryable<Product> Products { get; }
void SaveProduct(Product product);
void DeleteProduct(Product product);
}
这就是存储库.....(重要的部分)我想指出的是......这不是一个非常清楚的假类。测试是在假类上进行的。
private EFDbContext context = new EFDbContext();
public IQueryable<Product> Products {
get { return context.Products; }
}
public void DeleteProduct(Product product) {
context.Products.Remove(product);
context.SaveChanges();
}
第一个问题: 在对此进行测试时,我将在“ControllerTest”中在Controller上创建两个TestMethods。 “Can_delete_valid_product”和“Cannot_delete_invalid_product”。为存储库提供测试类是否有任何意义?像“RepositoryTest”一样,控制器会测试删除功能是否正常工作,不需要对它进行两次测试吗?
第二个问题: 在此我在控制器中测试产品是否存在,然后尝试删除它。如果它存在,我调用存储库中的deletefunction。这意味着永远不应该有例外的可能性。但是如果你向下发送null,你仍然可以在存储库中创建一个例外。 (这不可能发生在这里,但如果忘记检查是否为空,你仍然可以这样做)。问题是,如果产品存在的测试应该在存储库中完成吗?
答案 0 :(得分:1)
我更倾向于在大多数情况下将逻辑排除在控制器之外。对控制器操作的测试将验证是否调用了存储库,但是在该测试中模拟了存储库本身。我会让存储库负责处理空检查。
答案 1 :(得分:0)
这意味着永远不应该有例外的可能性。但是如果你向下发送null,你仍然可以在存储库中创建一个例外。 (这不可能发生在这里,但如果忘记检查是否为null,你仍然可以这样做。)
或者,如果在检查它之后但在删除之前将其删除。或者,如果您失去与存储库的连接(或者在这种情况下方法永远不会返回?)。你不能以这种方式避免例外。
答案 2 :(得分:0)
我个人为我的存储库/数据访问创建单独的测试,以确保它正常工作。控制器本身将使用模拟进行测试。
实际上,有人可以删除某个产品,就像其他人试图删除它一样,这完全有可能(可能不是那么可能)。在这种情况下,您可能不关心/需要知道有人这样做但我可能只是在存储库中吞下该异常(尽管我会先记录它)。在空检查/防御性编程方面,这完全是个人选择。有些人将这样的支票留给系统的入口点,而其他人则会构建一个分层防御,在整个代码中都有额外的检查。问题是这些检查会变得非常难看,这是我希望Code Contracts获得更多牵引力的重要原因。