2013-12-11 7 views
5

Ich arbeite an einer ASP.net MVC 3.0-Anwendung. Ich verwende MSTest zusammen mit Moq für Unit-Tests. Ich habe alle Testmethoden für meine Controller geschrieben und diese Tests durchgeführt, die zu erfolgreichen Ergebnissen führten.So testen Sie ordnungsgemäß Controller in ASP.net MVC mit Datenbankaufrufen

Jetzt habe ich einen Zweifel, ob ich Unit Tests ordnungsgemäß gemacht habe. Weil die meisten meiner Controller-Aktionen Datenbankaufrufe enthalten.

Ich verspotte sie nicht, ich verspotte nur Session und Request Objekte mit Moq.

Ist es wirklich notwendig, Datenbankaufrufe zu verspotten, da Komponententests das Testen einer einzelnen Codeeinheit bedeuten? Ich denke, Unit Testing Controller mit Datenbankaufrufen verletzt obige Aussage.

Wenn es so ist, kann mir jemand erklären, wie man Datenbankanrufe verspottet? Ich verwende kein Entity Framework.

Aktualisiertvor 2:

[httppost] 
    public void AjaxSave(Model m) 
{ 
    m.update(); // Database call 
} 

Antwort

10

Sie sollten Code extrahieren, die Datenbank (werfen Sie einen Blick auf Single Responsibility Principle) ruft in separate Objekt macht. Z.B. Sie haben 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<Person> people = new List<Person>(); 
      while(reader.Read()) 
      { 
       Person p = new Person(); 
       p.Name = reader["Name"].ToString(); 
       people.Add(p); 
      } 

      return View(people); 
     } 
    } 
} 

Extrahieren von Daten-Zugangscode in separate Klasse (in der Regel solche Klassen genannt repositories):

public class PersonRepository : IPersonRepository 
{ 
    public List<Person> 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<Person> people = new List<Person>(); 
      while(reader.Read()) 
      { 
       Person p = new Person(); 
       p.Name = reader["Name"].ToString(); 
       people.Add(p); 
      } 

      return people; 
     } 
    } 
} 

Wie Sie vielleicht schon bemerkt ich Abstraktion erklärt, die durch Datenzugriffsklasse implementiert:

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

Make-Controller hängen von dieser Abstraktion (es ist wichtig - Abstraktion ist einfach zu verspotten):

public class PersonController : Controller 
{ 
    private IPersonRepository _personRepository; 

    public PersonController(IPersonRepository personRepository) 
    { 
     _personRepository = personRepository; 
    } 

    public ActionResult Index() 
    { 
     var people = _personRepository.GetAllPeople(); 
     return View(people);    
    } 
} 

Dann Repository Implementierung in der Steuerung (Dependency Injection in .NET) injiziert und es für Tests verspotten:

var repositoryMock = new Mock<IPersonRepository>(); 
var people = new List<People>(); // 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(); 
+0

@ lazyberezovsky..can Sie bitte eine Probe Testmethode geben Sie mir –

+0

@Avinash ja, schon –

+1

@ layberezovsky..Super example..Thank kannst du so viel .. –

1

Nun, ich glaube, Sie hier einige Design-Probleme haben, weil richtiger prüfbar Code wird nie nach innen mit Datenbankcode am Ende ein MVC-Controller, müssen Sie die Trennung von Bedenken besser implementieren, so dass jedes Stück Code Unit-testbar ist, und dies wird durch die Verwendung einiger Entwurfsmuster wie Service Factory, Dependency Injection und Inversion of Control erreicht ... Joel Abrahamsson erklärt es schön gut here für den Fall, dass Sie nicht wissen, wovon ich spreche. Sie können sogar Castle Windsor, ein ziemlich gutes Open-Source-Tool für diesen Zweck (Inversion der Kontrolle)

Sie können immer noch Einheit testen Sie Controller mit ein wenig Aufwand, Implementieren von Setup-Funktionen und Aufräumfunktionen in Ihrem Komponententests. Aber, ich empfehle dringend, wenn Sie ein wenig Zeit haben, Ihren Code REFAKTOR, Sie werden nicht sehr weit mit einigen vielen unnötigen Abhängigkeiten kommen.

Leo

+0

@ leo..how, um Modell vom Controller –

+0

@ Leo..Ich rufe nicht direkt Datenbank, sondern ich rufe mit Modell-Methoden. –

1

Ein Controller nie sollte die Datenbank direkt anrufen (ein - nicht der wichtigste - Grund dafür ist, dass dieser die Steuerung zu testen, fast unmöglich macht, ...).Stattdessen rate ich Ihnen dringend zu Refactor Ihren Code, um Testbarkeit in erster Linie zu ermöglichen und auch ordnungsgemäß Separation of Concerns: Setzen Sie alle Ihre Daten Zugriffscode in Repositories, die Sie dann in Ihren Controllern über Schnittstellen zugreifen. Auf diese Weise können Sie sie leicht mit Moq verspotten.

+0

@ Thomas..Bitte aktualisierter Teil meiner Frage finden, Dies ist, wie ich gerade mache. Beim Klicken von Speichern, mache ich einen Ajax-Anruf, der das aktualisierte Modell erhält. dann rufe ich model.update() auf, um diese Werte in der Datenbank zu speichern. Wie sollte ich die obige Methode umgestalten. Bitte schlagen Sie vor. –