C# System.NullReferenceException-.Net(MVC)模拟单元测试
我正在控制器中使用Moq和Nunit框架对我的一个方法进行单元测试。我正在努力理解模拟存储库和其他对象的概念,但没有取得多大成功 我有一种方法不允许用户删除帐户中有未结余额的学生。该方法的逻辑在我的StudentController中,在POST方法中,我还使用了存储库和依赖项注入(不确定这是否导致了问题)。当我运行单元测试时,有时它会转到我的C# System.NullReferenceException-.Net(MVC)模拟单元测试,c#,asp.net,asp.net-mvc,unit-testing,moq,C#,Asp.net,Asp.net Mvc,Unit Testing,Moq,我正在控制器中使用Moq和Nunit框架对我的一个方法进行单元测试。我正在努力理解模拟存储库和其他对象的概念,但没有取得多大成功 我有一种方法不允许用户删除帐户中有未结余额的学生。该方法的逻辑在我的StudentController中,在POST方法中,我还使用了存储库和依赖项注入(不确定这是否导致了问题)。当我运行单元测试时,有时它会转到我的GET Delete()方法,如果它转到POST方法,我会收到一个错误,在代码行中说“Object reference not set to a inst
GET Delete()
方法,如果它转到POST方法
,我会收到一个错误,在代码行中说“Object reference not set to a instance of a Object”,如果(s.PaymentDue>0)
学生控制员
public class StudentController : Controller
{
private IStudentRepository studentRepository;
public StudentController()
{
this.studentRepository = new StudentRepository(new SchoolContext());
}
public StudentController(IStudentRepository studentRepository)
{
this.studentRepository = studentRepository;
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Delete(int id)
{
//studentRepository.DeleteStudent(id);
Student s = studentRepository.GetStudentByID(id);
var paymentDue = false;
if (s.PaymentDue > 0)
{
paymentDue = true;
ViewBag.ErrorMessage = "Cannot delete student. Student has overdue payment. Need to CLEAR payment before deletion!";
return View(s);
}
if (!paymentDue)
{
try
{
Student student = studentRepository.GetStudentByID(id);
studentRepository.DeleteStudent(id);
studentRepository.Save();
}
catch (DataException /* dex */)
{
//Log the error (uncomment dex variable name after DataException and add a line here to write a log.
return RedirectToAction("Delete", new { id = id, saveChangesError = true });
}
}
//return View(s);
return RedirectToAction("Index");
}
单元测试方法
private int studentID;
[TestMethod]
public void StudentDeleteTest()
{
//create list of Students to return
var listOfStudents = new List<Student>();
listOfStudents.Add(new Student
{
LastName = "Abc",
FirstMidName = "Abcd",
EnrollmentDate = Convert.ToDateTime("11/23/2010"),
PaymentDue = 20
});
Mock<IStudentRepository> mockStudentRepository = new Mock<IStudentRepository>();
mockStudentRepository.Setup(x => x.GetStudents()).Returns(listOfStudents);
var student = new StudentController(mockStudentRepository.Object);
//Act
student.Delete(studentID);
////Assert
mockStudentRepository.Verify(x => x.DeleteStudent(studentID), Times.AtLeastOnce());
}
private int studentID;
[测试方法]
public void StudentDeleteTest()
{
//创建要返回的学生列表
var listOfStudents=新列表();
学生列表。添加(新学生)
{
LastName=“Abc”,
FirstMidName=“Abcd”,
EnrollmentDate=Convert.ToDateTime(“11/23/2010”),
应付账款=20
});
Mock mockStudentRepository=新建Mock();
mockStudentRepository.Setup(x=>x.GetStudents()).Returns(学生列表);
var student=newstudentcontroller(mockStudentRepository.Object);
//表演
student.Delete(studentID);
////断言
mockStudentRepository.Verify(x=>x.deleteStustudent(studentID),Times.atlestOnce());
}
我不知道您的GetStudentByID方法到底在做什么,但它似乎返回null。 查看它的代码,检查它是否调用了您没有模拟的方法,或者返回值是否被很好地检索到
希望这有助于S我不知道您的GetStudentByID方法到底在做什么,但它似乎返回null。 查看它的代码,检查它是否调用了您没有模拟的方法,或者返回值是否被很好地检索到
希望这有助于S您没有嘲笑
GetStudentByID
。您只模拟了GetStudents
(您正在测试的操作方法甚至没有调用它)。调用未模拟的方法时,Moq的默认行为是返回null。因此,当控制器调用studentRepository.GetStudentByID
时,它返回null。随后,当您尝试访问学生的PaymentDue
属性时,该属性为null,从而导致NullReferenceException
有两件事需要解决:模拟方法并启用MockBehavior.Strict
var mockStudentRepository = new Mock<IStudentRepository>(MockBehaviorStrict);
我还没有检查您的代码的其余部分,以查看您是否没有模拟任何其他内容,但是启用严格的模拟行为将帮助您找到需要模拟的内容
…好的,我检查过了。您还需要模拟存储库的Save
方法
另一方面,您的控制器正在调用
studentRepository.GetStudentByID(id)
两次。这将导致对您的存储库(可能还有数据库)进行不必要的调用,并降低速度。相反,只需重用已经包含您的学生的s
另一方面,您似乎没有在控制器中使用依赖项注入框架。我建议你调查一下(我最喜欢的),团结等等。。这将允许您在应用程序中使用单个控制器,并防止控制器需要了解有关
StudentRepository
或SchoolContext
的任何信息。它所需要知道的就是是udentrepository
。退房。你没有嘲笑过GetStudentByID
。您只模拟了GetStudents
(您正在测试的操作方法甚至没有调用它)。调用未模拟的方法时,Moq的默认行为是返回null。因此,当控制器调用studentRepository.GetStudentByID
时,它返回null。随后,当您尝试访问学生的PaymentDue
属性时,该属性为null,从而导致NullReferenceException
有两件事需要解决:模拟方法并启用MockBehavior.Strict
var mockStudentRepository = new Mock<IStudentRepository>(MockBehaviorStrict);
我还没有检查您的代码的其余部分,以查看您是否没有模拟任何其他内容,但是启用严格的模拟行为将帮助您找到需要模拟的内容
…好的,我检查过了。您还需要模拟存储库的Save
方法
另一方面,您的控制器正在调用
studentRepository.GetStudentByID(id)
两次。这将导致对您的存储库(可能还有数据库)进行不必要的调用,并降低速度。相反,只需重用已经包含您的学生的s
另一方面,您似乎没有在控制器中使用依赖项注入框架。我建议你调查一下(我最喜欢的),团结等等。。这将允许您在应用程序中使用单个控制器,并防止控制器需要了解有关
StudentRepository
或SchoolContext
的任何信息。它所需要知道的就是是udentrepository
。退房。你知道什么是NullReferenceException
吗?你能调试并找出哪个对象是空的吗?你能调试并告诉我们你在哪一行得到错误吗?@Rinktacular他已经告诉我们错误来自哪一行了。s.PaymentDue是可空类型吗?您必须指定s.PaymentDue.Value吗?您没有模拟GetStudentByID
。你只模拟了GetStudents
。你知道什么是NullReferenceException
吗?你能调试并找出哪个对象是空的吗?你能调试并告诉我们你在哪一行得到错误吗