我正在尝试对我的控制器进行测试,这些控制器从存储库类中获取数据。 这是我要测试的存储库的一部分:
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;
}
}
只是一些小代码来了解测试的工作原理。 在我的HomeController中,我在Index()中有这个代码:
public ActionResult Index()
{
ViewBag.Message = "Announcements";
NewsRepository n_rep = new NewsRepository();
var model = i_rep.GetNews();
return View(model);
}
我对测试完全陌生,所以所有的解释都会很棒。 感谢。
答案 0 :(得分:8)
您的控制器无法单独进行单元测试,因为它与以下行中的存储库紧密耦合:
NewsRepository n_rep = new NewsRepository();
您只是硬编码了存储库的特定实现,并且在您的单元测试中,您无法模拟它。为了正确地执行此操作,您应该首先在此存储库上定义抽象:
public interface INewsRepository
{
IEnumerable<NewsItem> GetNews();
}
然后让您的特定存储库实现此接口:
public class NewsRepository : INewsRepository
{
...
}
好了,现在我们有了一个抽象,让我们通过使用这个抽象来削弱数据访问和控制器逻辑之间的耦合:
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);
}
}
好吧,现在你有一个控制器不再与某些特定的实现紧密结合。您可以拾取您最喜欢的模拟框架并编写单元测试。例如,使用NSubstitute
这里的索引操作的单元测试可能如下所示:
[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);
}
这就是它。您的控制器现在可以单独进行单元测试。您不需要设置数据库或其他任何东西。这不是测试控制器逻辑的重点。