0

I am doing a Unit Test for one of my method in my Controller, using Moq and Nunit Framework. I am trying hard to understand the concept of Mocking repositories & other objects, but not making much success.

I have a method which doesn't allow the user to delete a student who has a pending balance in his/her account. The logic for the method is in my StudentController, in POST method, and I am also using repository and Dependency Injection (not sure if that is causing an issue). When I run my Unit test, sometimes it goes to my GET Delete() method and if it goes to the POST methodI get an error saying "Object reference not set to an instance of an object" for the line of code saying this if (s.PaymentDue > 0)?

StudentController

 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");
        }

Unit Test Method

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());
        }

enter image description here

8
  • You know what a NullReferenceException is? Can you debug and figure out what object is null? Commented Feb 15, 2017 at 14:34
  • Can you debug and tell us what line you get the error on? Commented Feb 15, 2017 at 14:35
  • @Rinktacular He already told us what line the error came from. Commented Feb 15, 2017 at 14:35
  • Is s.PaymentDue a nullable type? Do you have to specify s.PaymentDue.Value? Commented Feb 15, 2017 at 14:36
  • 1
    You haven't mocked GetStudentByID. You've only mocked GetStudents. Commented Feb 15, 2017 at 14:37

2 Answers 2

5

You haven't mocked GetStudentByID. You've only mocked GetStudents (which isn't even called by the action method you're testing). The default behavior of Moq when calling a method that isn't mocked is to return null. So when the controller calls studentRepository.GetStudentByID it returns null. Then later when you try to access the PaymentDue property of the student, it's null, thus leading to a NullReferenceException.

Two things to fix it: mock the method and turn on MockBehavior.Strict.

var mockStudentRepository = new Mock<IStudentRepository>(MockBehaviorStrict);

That will cause an exception to occur when you try to call a method on the repository that hasn't been mocked, instead of returning null. This allows you to fail fast and easily locate what hasn't been mocked.

Then add your mock for that method:

var student = new Student
{
    Id = 9974,
    LastName = "Abc",
    FirstMidName = "Abcd",
    EnrollmentDate = Convert.ToDateTime("11/23/2010"),
    PaymentDue = 20
};

mockStudentRepository.Setup(x =>
    x.GetStudentByID(student.Id))
    .Returns(student);

I haven't examined the rest of your code to see if you aren't mocking anything else, but enabling strict mock behavior will help you locate what you need to mock.

...okay I did examine it. You'll also need to mock the Save method of your repository.


On a side note, your controller is calling studentRepository.GetStudentByID(id) twice. That will lead to an unnecessary call to your repository (and probably database) and slow things down. Instead, just reuse s which already contains your student.


On another side note, you appear to not be using a dependency injection framework in your controller. I suggest you look into AutoFac (my favorite), Ninject, Unity etc.. that would allow you to use a single controller in your app and prevent the controller from needing to know anything about StudentRepository or SchoolContext. All it would need to know about is IStudentRepository. Check this excellent video out.

Sign up to request clarification or add additional context in comments.

4 Comments

Thanks for the resources with the information. It is getting very confusing at the moment as I am very new at it. For my project, we have multiple Controllers, Services, UnitOfWork, Generic Repository.... I edited my test code by implementing the ID, and I am still getting the NullException .
@Truecolor Did you do what I suggested? Did you turn on MockBehavior.Strict? Did you mock the method I suggested?
I am getting mock behavior Strict error. I have added an image of the error in my post.
@Truecolor I looked at the picture. It should be pretty clear. You're passing a Student ID of 0. The mock in my example passes the ID of whatever you define the ID as for the student. I suggest you eliminate your studentID variable and just use student.ID so you only set it in one place. Please read my answer carefully, there's a lot in there and it's important to get it all.
0

I don't know exactly what your GetStudentByID method is doing, but it seems that it returns null. Look at its code, check if it's calling a method you didn't mock, or if the return value is well retrieved.

Hope that helps... :S

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.