2017-02-15 100 views
0

我正在爲我的控制器中的一個方法使用Moq和Nunit框架進行單元測試。我正在努力理解其他對象的嘲笑庫的概念,但沒有取得太大的成功。System.NullReferenceException - .Net(MVC)模擬單元測試

我有一種方法,不允許用戶刪除他/她帳戶中有未結餘額的學生。該方法的邏輯是在我的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屬性時,它將爲空,從而導致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等。這將允許你在你的應用中使用單個控制器,並防止控制器需要知道關於StudentRepositorySchoolContext的任何信息。所有它需要知道的是IStudentRepository。檢查this excellent video了。

+0

感謝有關這些信息的資源。由於我很新,因此目前變得非常混亂。對於我的項目,我們有多個控制器,服務,UnitOfWork,Generic Repository ....我通過實現'ID'編輯我的測試代碼,而且我仍然得到'NullException'。 – Truecolor

+0

@真彩你有沒有做我建議的?你打開MockBehavior.Strict了嗎?你嘲笑我建議的方法嗎? – mason

+0

我得到模擬行爲嚴格的錯誤。我在帖子中添加了錯誤圖片。 – Truecolor