2017-02-15 4 views
0

Moq 및 Nunit Framework를 사용하여 내 컨트롤러에서 내 메서드에 대한 단위 테스트를 수행하고 있습니다. 저는 모킹 저장소 & 다른 객체의 개념을 이해하려고 열심히 노력하고 있지만 많은 성공을 이루지 못하고 있습니다.System.NullReferenceException-.Net (MVC) 모의 단위 테스트

나는 계좌 잔고가있는 학생을 삭제할 수없는 방법이 있습니다. 메서드에 대한 논리는 Student 메서드의 POST 메서드에 있으며 저장소 및 Dependency Injection (문제가 발생했는지 확실하지 않음)도 사용하고 있습니다. 단위 테스트를 실행할 때 가끔 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'은 무엇인지 알고 계십니까? 디버그하고 어떤 객체가 null인지 파악할 수 있습니까? – mason

+0

디버깅하여 오류가 발생한 행을 알려줄 수 있습니까? – Rinktacular

+0

@Rinktacular 그는 오류가 발생한 줄을 이미 말했습니다. – mason

답변

0

나는 당신의 GetStudentByID 방법은 일을 정확히 모르겠지만,이 보인다 다시 null이됩니다. 코드를보고 모의하지 않은 메서드를 호출하는지 또는 반환 값이 제대로 검색되는지 확인하십시오.

... 도움이되기를 바랍니다 : 당신은 GetStudentByID을 조롱하지 않은 S

5

. GetStudents (테스트중인 작업 방법으로도 호출되지 않음) 만 조롱했습니다. 모의되지 않은 메서드를 호출 할 때 Moq의 기본 동작은 null을 반환하는 것입니다. 따라서 컨트롤러가 studentRepository.GetStudentByID을 호출하면 null을 반환합니다. 그런 다음 나중에 학생의 PaymentDue 속성에 액세스하려고 시도하면 null이므로 NullReferenceException으로 이어집니다.

두 가지 사항 : 방법을 조롱하고 MockBehavior.Strict을 켜십시오.

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

null을 반환하는 대신 조롱되지 않은 저장소에서 메소드를 호출하려고하면 예외가 발생합니다. 이를 통해 조속히 실패하고 조롱되지 않은 것을 쉽게 찾을 수 있습니다.

그런 다음 방법에 대한 귀하의 모의를 추가

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

@ 트루 컬러 내가 제안한 것을 했습니까? 너 MockBehavior를 켜 봤니? 엄격? 내가 제안한 방법을 조롱 했니? – mason

+0

모의 행위가 발생했습니다 엄격한 오류가 발생했습니다. 내 게시물에 오류의 이미지를 추가했습니다. – Truecolor

관련 문제