Asp.net mvc 3 您应该如何在MVC3中使用存储库类进行单元测试?

Asp.net mvc 3 您应该如何在MVC3中使用存储库类进行单元测试?,asp.net-mvc-3,testing,Asp.net Mvc 3,Testing,我正在尝试对从存储库类获取数据的控制器进行测试。 这是我要测试的存储库的一部分: public class NewsRepository { public IEnumerable<NewsItem> GetNews() { var result = (from n in n_db.NewsItems orderby n.ID descending select n).T

我正在尝试对从存储库类获取数据的控制器进行测试。 这是我要测试的存储库的一部分:

public class NewsRepository
{
    public IEnumerable<NewsItem> GetNews()
    {
        var result = (from n in n_db.NewsItems
                     orderby n.ID descending
                     select n).Take(3);
        return result;
    }
}
我对测试完全陌生,所以所有的解释都会很好。
谢谢。

您的控制器不可能单独进行单元测试,因为它与您的存储库在以下行上紧密耦合:

NewsRepository n_rep = new NewsRepository();
您只需对存储库的特定实现进行硬编码,在单元测试中无法对其进行模拟。为了正确地做到这一点,您应该首先在此存储库上定义一个抽象:

public interface INewsRepository
{
    IEnumerable<NewsItem> GetNews();
}
好了,现在我们有了一个抽象,让我们使用这个抽象来削弱数据访问和控制器逻辑之间的耦合:

public class NewsController: Controller
{
    private readonly INewsRepository repository;
    public NewsController(INewsRepository repository)
    {
        this.repository = repository;
    }

    public ActionResult Index()
    {
        ViewBag.Message = "Announcements";
        var model = this.repository.GetNews();
        return View(model);
    }    
}
好的,现在您有了一个控制器,它不再与某些特定的实现紧密耦合。您可以选择您最喜欢的模拟框架并编写单元测试。例如,以下是索引操作的单元测试的外观:

[TestMethod]
public void Index_Action_Fetches_Model_From_Repo()
{
    // arrange
    var repo = Substitute.For<INewsRepository>();
    IEnumerable<NewsItem> expectedNews = new[] { new NewsItem() };
    repo.GetNews().Returns(expectedNews);
    var sut = new NewsController(repo);

    // act
    var actual = sut.Index();

    // assert
    Assert.IsInstanceOfType(actual, typeof(ViewResult));
    var viewResult = actual as ViewResult;
    Assert.AreEqual(expectedNews, viewResult.Model);
}
[TestMethod]
公共无效索引\u操作\u从\u Repo()获取\u模型\u
{
//安排
var repo=替换为();
IEnumerable expectedNews=new[]{new NewsItem()};
repo.GetNews().Returns(expectedNews);
var sut=新的新闻控制器(repo);
//表演
var-actual=sut.Index();
//断言
IsInstanceOfType(实际的,typeof(ViewResult));
var viewResult=实际为viewResult;
AreEqual(expectedNews、viewResult.Model);
}

差不多就是这样。您的控制器现在可以轻松地单独进行单元测试。你不需要设置数据库或其他什么。这不是测试控制器逻辑的重点。

这很有意义,我不认为这不是我正在寻找的解决方案的原因,但我得到以下错误:1。在public NewsController(INewsRepository repository)中,错误为:可访问性不一致:参数类型“TestProject.Models.INewsRepository”的可访问性低于方法“TestProject.Controller.NewsController.NewsController(TestProject.Models.INewsRepository)”----2。行中:var repo=Substitute.For();错误:名称“Substitute”在当前上下文中不存在。---是否有我可能缺少的命名空间?替换,忘记检查链接。但是第一个错误仍然存在。您的所有类和接口都必须是公共的,如我的回答所示。就您关于NSubstitute的错误而言,您需要在单元测试项目中安装NSubstitute NuGet。如果存储库返回3条最新公告,那该怎么办。拿(3)?如何测试这种功能?不,您可以在
expectedNews
数组中放置5个对象。然后,您可以将
viewResult.Model
强制转换为
NewsItem[]
并确保它包含3个元素,并且这3个元素是
expectedNews
数组的前3个元素。当然,这假设存储库不再执行
。执行(3)
调用,但您是在控制器操作中执行此操作的,这是正确的操作。
public class NewsController: Controller
{
    private readonly INewsRepository repository;
    public NewsController(INewsRepository repository)
    {
        this.repository = repository;
    }

    public ActionResult Index()
    {
        ViewBag.Message = "Announcements";
        var model = this.repository.GetNews();
        return View(model);
    }    
}
[TestMethod]
public void Index_Action_Fetches_Model_From_Repo()
{
    // arrange
    var repo = Substitute.For<INewsRepository>();
    IEnumerable<NewsItem> expectedNews = new[] { new NewsItem() };
    repo.GetNews().Returns(expectedNews);
    var sut = new NewsController(repo);

    // act
    var actual = sut.Index();

    // assert
    Assert.IsInstanceOfType(actual, typeof(ViewResult));
    var viewResult = actual as ViewResult;
    Assert.AreEqual(expectedNews, viewResult.Model);
}