Asp.net mvc 测试MVC控制器失败,出现空引用异常

Asp.net mvc 测试MVC控制器失败,出现空引用异常,asp.net-mvc,unit-testing,moq,Asp.net Mvc,Unit Testing,Moq,下面是我尝试测试的设置 控制器: public ActionResult UpsertStudent(StudentModel studentModel) { try { if (!CheckStudentUpdateForEdit(studentModel)) { return Json(new { result = STUDENT_EXISTS }); } // remaining code

下面是我尝试测试的设置

控制器:

public ActionResult UpsertStudent(StudentModel studentModel)
{
    try
    {

        if (!CheckStudentUpdateForEdit(studentModel))
        {

            return Json(new { result = STUDENT_EXISTS });

        }
    // remaining code removed for brevity 
}

private bool CheckStudentUpdateForEdit(StudentModel studentModel)
{
    var returnVal = true;

    var existingStudent = _updateStudentManager.GetStudentInfo(studentModel.Id);

    if (existingStudent.StudentType == "Day Scholar")
    {
        returnVal = true;
    }
    else
    {
        returnVal = false;
    }

    return returnVal;
}
public void AllowStudentUpdates_Success()
{
    var studentModel = new StudentModel()
    {
        StudentName = "Joe",
        Id = "123",
        StudentType = "Day Scholar"
    };

    var studentToAdd = new Student()
    {
        Id = "123",
        Name = "Joe",
        StartDate = DateTime.UtcNow.ToShortDateString(),
        StudentType = "Day Scholar",
        EndDate = "08/10/2016"
    };

    _studentRulesHelper.Setup(x => x.GetStudentRule(studentModel, true)).Returns(studentToAdd);
    _productRulesHelper.Setup(x => x.ReturnStudentRule(studentModel, true)).Returns(studentToAdd);

    var res = _controller.UpsertStudent(studentModel) as JsonResult;
    if (res != null) Assert.AreEqual("{ result = True }", res.Data.ToString());
}
试验方法:

public ActionResult UpsertStudent(StudentModel studentModel)
{
    try
    {

        if (!CheckStudentUpdateForEdit(studentModel))
        {

            return Json(new { result = STUDENT_EXISTS });

        }
    // remaining code removed for brevity 
}

private bool CheckStudentUpdateForEdit(StudentModel studentModel)
{
    var returnVal = true;

    var existingStudent = _updateStudentManager.GetStudentInfo(studentModel.Id);

    if (existingStudent.StudentType == "Day Scholar")
    {
        returnVal = true;
    }
    else
    {
        returnVal = false;
    }

    return returnVal;
}
public void AllowStudentUpdates_Success()
{
    var studentModel = new StudentModel()
    {
        StudentName = "Joe",
        Id = "123",
        StudentType = "Day Scholar"
    };

    var studentToAdd = new Student()
    {
        Id = "123",
        Name = "Joe",
        StartDate = DateTime.UtcNow.ToShortDateString(),
        StudentType = "Day Scholar",
        EndDate = "08/10/2016"
    };

    _studentRulesHelper.Setup(x => x.GetStudentRule(studentModel, true)).Returns(studentToAdd);
    _productRulesHelper.Setup(x => x.ReturnStudentRule(studentModel, true)).Returns(studentToAdd);

    var res = _controller.UpsertStudent(studentModel) as JsonResult;
    if (res != null) Assert.AreEqual("{ result = True }", res.Data.ToString());
}
当它点击
UpsertDoc
调用时,它转到控制器中的实际调用,并尝试执行
checkstudentupdatefordit()
GetStudentInfo()尝试从db获取对象并返回空对象,因为没有学生具有从测试方法传递的id。 然后测试失败,出现空引用异常

现在被测试的系统不应该碰到数据库。我不知道为什么这是另一种方式

编写此测试的任何其他人也将尝试通过一个虚拟对象,该对象在
GetStudentInfo()
测试现在的设置方式下肯定会失败


我该怎么做才能使这项工作正常?

我不确定是否正确理解了您的问题,但查看提供的代码片段,测试将进入数据库,因为没有定义模拟对象及其期望值

我会像这样实施解决方案-

//Interface
public interface IStudentrepository
{
    StudentModel GetStudentInfo(int studentId);
}

//Class implementing IStudentrepository
public class StudentRepository : IStudentrepository
{
    public StudentModel GetStudentInfo(int studentId)
    {
        //Implementation goes here
    }
}
我假设您的
\u updateStudentManager
对象是为
学生
执行DB交互的类。我叫它say
StudentRepository
。为了让你模拟这个行为,我会让它
接口
驱动。
所以我的设置通常是这样的-

//Interface
public interface IStudentrepository
{
    StudentModel GetStudentInfo(int studentId);
}

//Class implementing IStudentrepository
public class StudentRepository : IStudentrepository
{
    public StudentModel GetStudentInfo(int studentId)
    {
        //Implementation goes here
    }
}
现在在我的控制器中,我将有一个IStudentrepository实例,它可以通过构造函数注入

public class StudentController
{
    private readonly IStudentrepository updateStudentManager;
    public StudentController(IStudentrepository updateStudentManager)
    {
        this.updateStudentManager = updateStudentManager;
    }
}
//Rest of the code for controller....
现在,在编写我的
测试
时,我将创建一个
IStudentrepository
的模拟对象,定义对模拟对象的期望,并在创建控制器对象时注入它。像这样的

    [TestMethod]
    public void TestMethod1()
    {
        //--Arrange--
        //Define a mock object for student repository
        var mock = new Mock<IStudentrepository>();

        //Define the expectations of the mock object
        mock.Setup(s => s.GetStudentInfo(It.IsAny<int>()))
            .Returns(new StudentModel {/*return the required object */ });

        //Instantiate controller and inject the mock object
        StudentController _controller = new StudentController(mock.Object);

        //--Act--
        var res = _controller.UpsertStudent(studentModel) as JsonResult;


        //--Assert--
        if (res != null) Assert.AreEqual("{ result = True }", res.Data.ToString());

    }
[TestMethod]
公共void TestMethod1()
{
//--安排--
//为学生存储库定义模拟对象
var mock=new mock();
//定义模拟对象的期望值
mock.Setup(s=>s.GetStudentInfo(It.IsAny())
.Returns(newstudentmodel{/*返回所需的对象*/});
//实例化控制器并注入模拟对象
StudentController\u controller=新的StudentController(mock.Object);
//--表演--
var res=\u controller.UpsertStudent(studentModel)作为JsonResult;
//--断言--
如果(res!=null)Assert.AreEqual({result=True}),则res.Data.ToString();
}
现在,当您的测试方法调用
GetStudentInfo
方法时,它将返回模拟对象中设置的值,而不是命中db


这只是一个高级实现,当然您可以根据自己的设计对其进行修改。希望它能有所帮助

如果没有学生具有该Id,那么我会说您的测试已成功识别错误。您需要处理
existingStudent
为空的情况。如果代码被破坏,那么编辑测试以使其正常工作是没有意义的。在实际使用中,这永远不会发生,因为会有学生。您是否试图模拟上下文?这是正确的代码吗。您有
\u controller.UpsertDoc()
,但没有显示任何具有该名称的方法。您有
.Setup(x=>x.GetStudentRule)
.Setup(x=>x.ReturnStudentRule)
,但还没有显示那些调用的方法。另外两个只是从一个对象到另一个对象的映射器,没有任何业务逻辑。