问题
Below is the setup that I am trying to test.
The controller:
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;
}
The Test method:
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());
}
When it hits the UpsertDoc
call, it goes to the actual call in the controller and tries to execute CheckStudentUpdateForEdit()
the GetStudentInfo()
tries to get an object from the db and returns a null object as there is no student with the id that's passed from the test method.
The test then fails with Null Reference exception.
Now the system under test is not supposed to hit the db. I don't know why this is doing the other way!
Anyone else writing this test will also try to pass a dummy object which is bound to fail at GetStudentInfo()
the way the test is setup now.
What do I do to make this work?
回答1:
I am not sure if I have understood you problem correctly, but looking at the code snippets provided, the test will go and hit the database, because the mock object and its expectation is not defined.
I would have implemented the solution like this -
I am making some assumptions that your _updateStudentManager
object is for the class that is doing the DB interaction for Student
. I'll call it say StudentRepository
. And to allow you to mock the behavior I would make it Interface
driven.
So typically my setup would look like this -
//Interface
public interface IStudentrepository
{
StudentModel GetStudentInfo(int studentId);
}
//Class implementing IStudentrepository
public class StudentRepository : IStudentrepository
{
public StudentModel GetStudentInfo(int studentId)
{
//Implementation goes here
}
}
Now in my controller, I would have an instance of IStudentrepository, which can be injected via constructor.
public class StudentController
{
private readonly IStudentrepository updateStudentManager;
public StudentController(IStudentrepository updateStudentManager)
{
this.updateStudentManager = updateStudentManager;
}
}
//Rest of the code for controller....
Now while writing my Test
, I will create a mock object of IStudentrepository
, define the expectation for the mock object, and inject it when creating the controller object. Something like this.
[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());
}
Now when your test method calls the GetStudentInfo
method, instead of hitting the db, it will return the value as set in mock object.
This is just a high level implementation, and of course you can modify it as per your design. Hope it helps
来源:https://stackoverflow.com/questions/32244667/testing-a-mvc-controller-fails-with-null-reference-exception