How to Properly Test Controllers in ASP.net MVC that has database calls

前端 未结 3 1033
孤城傲影
孤城傲影 2020-12-31 20:52

I am working on an ASP.net MVC 3.0 Application. I am using MSTest along with Moq for unit testing. I have written all the test methods for my contr

3条回答
  •  感情败类
    2020-12-31 21:15

    You should extract code which makes database calls into separate object (take a look on Single Responsibility Principle). E.g. you have controller

    public class PersonController : Controller
    {
         public ActionResult Index()
         { 
             var connectionString = 
                 ConfigurationManager.ConnectionStrings["foo"].ConnectionString;
             using(var connection = new SqlConnection(connectionString))
             {
                 string sql = "SELECT Name FROM People";
                 var command = connection.CreateCommand(sql);
                 var reader = command.ExecuteReader();
                 List people = new List();
                 while(reader.Read())
                 {
                     Person p = new Person();
                     p.Name = reader["Name"].ToString();
                     people.Add(p);
                 }
    
                 return View(people);
             }
         }
    }
    

    Extract data-access code into separate class (usually such classes called repositories):

    public class PersonRepository : IPersonRepository
    {
         public List GetAllPeople()
         {
             var connectionString = 
                 ConfigurationManager.ConnectionStrings["foo"].ConnectionString;
             using(var connection = new SqlConnection(connectionString))
             {
                 string sql = "SELECT Name FROM People";
                 var command = connection.CreateCommand(sql);
                 var reader = command.ExecuteReader();
                 List people = new List();
                 while(reader.Read())
                 {
                     Person p = new Person();
                     p.Name = reader["Name"].ToString();
                     people.Add(p);
                 }
    
                 return people;
             }
         }
    }
    

    As you already notices I declared abstraction which is implemented by data-access class:

    public interface IPersonRepository
    {
        List GetAllPeople();
        // other data access API will go here
    }
    

    Make controller depend on this abstraction (it's important - abstraction is easy to mock):

    public class PersonController : Controller
    {
         private IPersonRepository _personRepository;
    
         public PersonController(IPersonRepository personRepository)
         {
             _personRepository = personRepository;
         }
    
         public ActionResult Index()
         { 
             var people = _personRepository.GetAllPeople();
             return View(people);             
         }
    }
    

    Then inject repository implementation into controller (Dependency Injection in .NET) and mock it for tests:

    var repositoryMock = new Mock();
    var people = new List(); // provide some sample list 
    repositoryMock.Setup(r => r.GetAllPeople()).Return(people);
    var controller = new PersonController(repositoryMock.Object);
    
    var result = (ViewResult)controller.Index();
    // Assert here
    Assert.AreEqual(result.ViewName, "Index");
    Assert.AreEqual(result.Model, people);
    repositoryMock.VerifyAll();
    

提交回复
热议问题