2017-02-15 3 views
0

Я выполняю модульное тестирование для одного из моих методов в моем контроллере с использованием Moq и Nunit Framework. Я стараюсь понять концепцию Mocking repositories & других объектов, но не добился большого успеха.System.NullReferenceException-.Net (MVC) Mock Unit Test

У меня есть метод, который не позволяет пользователю удалять студента, у которого есть ожидающий остаток на его учетной записи. Логика метода находится в моем StudentController, в методе POST, и я также использую репозиторий и инъекцию зависимостей (не уверен, что это вызывает проблему). Когда я запускаю свой модульный тест, иногда он переходит к моему методу GET Delete(), и если он идет на POST method, я получаю сообщение об ошибке «Ссылка на объект, не установленная на экземпляр объекта» для строки кода, говорящей об этом 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"); 
     } 

Единицы измерения Метод испытания

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

+0

Вы знаете, что такое 'NullReferenceException'? Можете ли вы отладить и выяснить, какой объект является нулевым? – mason

+0

Можете ли вы отлаживать и сообщать нам, на какой строке вы получаете ошибку? – Rinktacular

+0

@ Rinktacular Он уже рассказал нам, в какой строке произошла ошибка. – mason

ответ

0

Я не знаю точно, что делает ваш GetStudentByID метод, но, кажется, это обращается в нуль. Посмотрите на свой код, проверьте, вызвал ли он метод, который вы не высмеивали, или если возвращаемое значение хорошо восстановлено.

Надежда, что помогает ...: S

5

Вы не издевались GetStudentByID. Вы только высмеивали GetStudents (который даже не вызван методом действия, который вы тестируете). Поведение Moq по умолчанию при вызове метода, который не издевается, - это вернуть значение null. Поэтому, когда контроллер вызывает studentRepository.GetStudentByID, он возвращает null. Затем, когда вы попытаетесь получить доступ к свойству ученика PaymentDue, оно равно null, что приводит к NullReferenceException.

Две вещи, чтобы исправить это: высмеять метод и включить MockBehavior.Strict.

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

Это вызовет исключение возникает при попытке вызвать метод хранилища, не издевались, а не возвращаться нуль. Это позволяет вам быстро и легко обнаруживать то, что не издевались.

Добавьте макет для этого метода:

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

Я не исследовал остальную часть кода, чтобы увидеть, если вы не издеваетесь что-нибудь еще, но позволяя строгий макет поведения поможет вам найти то, что вам нужно высмеивать.

... хорошо, я изучил его. Вам также необходимо издеваться над методом Save вашего репозитория.


На стороне записки, контроллер звонит studentRepository.GetStudentByID(id) дважды. Это приведет к ненужному вызову вашего репозитория (и, возможно, базы данных) и замедлит работу. Вместо этого просто повторно используйте s, который уже содержит вашего ученика.


С другой стороны обратите внимание, что вы не используете в своем контроллере инфраструктуру внедрения зависимостей. Я предлагаю вам посмотреть AutoFac (мой любимый), Ninject, Unity и т. Д., Который позволит вам использовать один контроллер в вашем приложении и не дать контроллеру ничего знать о StudentRepository или SchoolContext. Все, о чем нужно было знать, это IStudentRepository. Проверьте this excellent video.

+0

Спасибо за ресурсы с информацией. На данный момент это становится очень запутанным, поскольку я очень новичок в этом. Для моего проекта у нас есть несколько контроллеров, служб, UnitOfWork, Generic Repository .... Я редактировал свой тестовый код, внедряя 'ID', и я все еще получаю' NullException'. – Truecolor

+0

@Truecolor Вы сделали то, что я предложил? Вы включили MockBehavior.Strict? Вы издевались над предложенным мной методом? – mason

+0

Я получаю макет поведения Строгая ошибка. Я добавил изображение ошибки в свой пост. – Truecolor

Смежные вопросы