Asp.net mvc 3 MVC应用程序中的详细单元测试方法出错

Asp.net mvc 3 MVC应用程序中的详细单元测试方法出错,asp.net-mvc-3,unit-testing,Asp.net Mvc 3,Unit Testing,我正在开发一个MVC应用程序,并试图在其中创建一个单元测试 我已经编写了如下所示的单元测试 [TestMethod] public void Company_Details2() { Company oCompany = new Company(); var result = oCompanyController.Details(97) as ViewResult; var Comp =

我正在开发一个MVC应用程序,并试图在其中创建一个单元测试

我已经编写了如下所示的单元测试

   [TestMethod]
        public void Company_Details2()
        {
            Company oCompany = new Company();
            var result = oCompanyController.Details(97) as ViewResult;
            var Comp = (Company)result.ViewData.Model;
            Assert.AreEqual("TName", Comp.Name);

        }
控制器如下所示:

[SessionFilterAction]
public ViewResult Details(int id)
{
    Company company = db.Companies.Find(id);
    return View(company);
}
当我尝试运行单元测试时,它在最后一行的测试方法中返回一个错误:

对象引用未设置为对象的实例


由于我是单元测试新手,我不知道如何为索引、创建、编辑、详细信息和删除方法编写测试方法,在哪里可以找到它?

在代码中有许多地方可能会出现NRE。例如,控制器操作中使用的
db
变量可能为空。确保它已初始化。在单元测试中,您还有以下调用
var Comp=(Company)result.ViewData.Model。如果数据库中没有id=97的公司,则该值可能为空。然后尝试比较名称
Assert.AreEqual(“TName”,Comp.name)。但如果
Comp
为空,则会引发异常。此外,在单元测试中,您使用了一些
oCompanyController
变量,不清楚它是在哪里初始化的

对某些代码进行单元测试的正确方法是单独对其进行单元测试。这意味着您的代码应该在编写时考虑抽象。它不应该依赖于实际的实现。不清楚控制器中的
db
变量是什么,但它应该是某种接口类型(或抽象类),可以在单元测试中模拟。通过这种方式,您将在隔离不同层的情况下实现单元测试。在本例中,您正在对控制器操作进行单元测试,因此此控制器中的代码不应依赖于具体的类

让我们举一个控制器代码的示例:

public class CompaniesController: Controller
{
    public readonly ICompaniesRepository repository;
    public CompaniesController(ICompaniesRepository repository)
    {
        this.repository = repository;
    }

    [SessionFilterAction]
    public ViewResult Details(int id)
    {
        Company company = this.repository.GetCompanyById(id);
        return View(company);
    }
}
现在,您可以使用Moq、NSubstitute或Rhino.Mocks等模拟框架来模拟单元测试中的存储库,并能够定义期望值。例如,NSubstitute:

[TestMethod]
public void Company_Details2()
{
    // arrange
    var repository = Substitute.For<ICompaniesRepository>();
    var id = 97;
    var company = new Company();
    repository.GetCompanyById(id).Returns(company);
    var sut = new CompaniesController(repository);

    // act
    var actual = sut.Details(id);

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

}
[TestMethod]
公开作废公司_详情2()
{
//安排
var repository=Substitute.For();
var-id=97;
var公司=新公司();
GetCompanyById(id).Returns(company);
var sut=新公司控制器(存储库);
//表演
var实际值=汇总明细(id);
//断言
IsInstanceOfType(实际的,typeof(ViewResult));
var viewResult=(viewResult)实际值;
arenequal(company,viewResult.Model);
}

如其他人所示,为接口编写代码是一种很好的做法。Moq是一个框架,可以帮助您对所需函数进行假调用,而且非常容易学习。现在我假设您正在调用,
IRepository.Detials()
,所以现在

[TestMethod]
SomeMethod()
{
 // This will create a fake/mock for your interface so that you can still call func. 
 // but not actual one.
  Mock<IRepository> mockedRepository= new Mock<IRepository>();

 // Here, we are making fake call but still Returns will give us the output.
  mockedRepository.SetUp(x=>x.Details()).Returns(Company Object);
  var result=controller.Action() as ViewResult;
  // Assert
}
[TestMethod]
SomeMethod()
{
//这将为您的接口创建一个假/模拟,以便您仍然可以调用func。
//但不是真的。
Mock mockedRepository=new Mock();
//在这里,我们正在进行假呼叫,但仍然返回将为我们提供输出。
mockedRepository.SetUp(x=>x.Details())。返回(公司对象);
var result=controller.Action()作为ViewResult;
//断言
}

有些人可能会说,但我没有测试详细的方法,因为您将编写另一个单元测试等等。

谢谢Darin,但是我完全不知道ICompaniesRepository和moq的事情…它们是什么?如果我在我的当前代码中得到我必须做的更改会很好。你希望我在不显示当前代码的情况下告诉你当前代码的更改吗?您所展示的只是代码的一小部分。其思想是使用一个接口来抽象所有数据访问操作,这样控制器就不会与特定的数据访问提供程序紧密耦合。如果您不知道什么是接口,那么我强烈建议您在进入ASP.NET MVC之前,先阅读.NET/C中的一些基本入门教程。