2013-07-10 9 views
6

Ich habe diese Methode:Wie erstellen Sie einen richtigen Komponententest für eine Methode, die eine Liste zurückgibt?

public DataSourceResult GetProjectBySpec(int projectId, int seasonId, int episodeId) 
     { 
      using (var rep = RepositoryHelper.GetTake2Repository<ITake2RepositoryBase>()) 
      { 
       var spec = new ProjectCrewsByProjectSpec(projectId, seasonId, episodeId); 

       var personList = rep.GetList<ProjectDGACrew>(spec).Select(p => new 
       { 
        //big query... 
        .ToDataSourceResult(); 

       return personList; 
      } 
     } 

Ich brauche einen Unit-Test für diese zu schaffen.

Meine erste Frage ist:

  1. Was bin ich für die Prüfung? Prüfe ich NUR, ob die Methode eine Liste zurückgibt?

  2. Wenn ja, wie würde ich es testen?

Dies ist, was ich bisher:

[TestClass] 
    public class CrewControllerTest 
    { 
     [TestMethod] 
     public void GetProjectCrewsBySpecTest() 
     { 
      // arrange 
      int projectId = 1; 
      int seasonId = 2; 
      int episodeId = 3; 

      // act 
      var crewController = new CrewController(); 
      DataSourceResult dsr = crewController.GetProjectCrewsBySpec(1, 2, 3); 

      // assert 
      // what or how do I assert here? Am I just checking whether "dsr" is a list? How do I do that? 
     } 
    } 
+0

Ich bin auch ein Unit-Test-Noob, aber meine Annahme ist, dass Sie bereits wissen, was Sie * sollten * zurück von der Methode erhalten, wenn Sie diese Eingaben eingeben. Vergleichen Sie das mit dem, was Sie * tatsächlich * zurückbekommen, um festzustellen, ob der Komponententest erfolgreich ist. – MadHenchbot

+0

also im Grunde für eine Abfrage testen ist wertlos nein? da die Daten die ganze Zeit ändern können. –

+0

Wenn dies der Fall ist, müssen Sie möglicherweise die Datenbank nachbilden, um jedes Mal zuverlässige Ergebnisse zu erhalten. Da ich nicht positiv bin, werde ich einfach Ihre Frage +1 geben und darauf warten, dass sich jemand sachkundiger einwiegt. :) – MadHenchbot

Antwort

3

Ich bin auch kein Experte, und ich habe nur TDD für eine kleine Weile, also nehmen Sie, was ich schreibe in dieser weitläufigen Antwort mit einem Löffel Salz :) Ich bin sicher, jemand anderes kann darauf hinweisen, wenn ich ' Ich habe wirklich schlechte Fehler gemacht oder dich in die falsche Richtung gelenkt ...

Ich bin mir nicht sicher, ob dein Test wirklich ein Komponententest ist, weil er mehrere Abhängigkeiten ausübt. Angenommen, Sie führen diesen Test aus und erhalten eine Ausnahme, die aus der Methode ausgeschlossen wird. Hat diese Ausnahme kommen aus

  • RepositoryHelper.GetTake2Repository()) werfen? (Depencenz-Problem)
  • ProjectCrewsByProjectSpec Konstruktor werfen? (Abhängigkeitsproblem)
  • rep.GetList (spec) werfen? der Kendo (es ist kendo, richtig?) (Abhängigkeitsproblem)
  • ToDataSourceResult() werfen? (Behavioral Ausgabe)

Unit-Tests ist alles über das Testen Dinge in völligen Isolation von ihren Abhängigkeiten, so im Moment würde ich sagen, es ist eher wie ein Integrationstest, wodurch Sie wirklich wie die Systeme nicht kümmern interagieren, möchten Sie nur sicherstellen, dass für eine bestimmte projectID, seasonId und episodeId Sie erhalten die erwarteten Ergebnisse zurück - in diesem Fall, was wirklich rep.GetList() Methode in Verbindung mit der sind. ToDataSourceResult Erweiterung.

Jetzt sind Integrationstests sehr nützlich und 100% erforderlich als Teil einer testgetriebenen Methode, und wenn Sie das wirklich tun möchten, dann machen Sie es richtig (ich setze dieses in und erwarten dass zurück, habe ich dass zurück)

Aber wenn Sie wollen Unit Test dieser Code (genauer gesagt, wenn Sie an die Einheit wollen Ihre Klassen testen GetProjectBySpec Methode) Sie haben? zu tun, wie @jimmy_keen erwähnt und es umgestalten, so dass Sie dietesten können 210 Verhalten von GetProjectBySpec. z.B. hier ist ein spezifiziertes Verhalten gerade erfunden, ich natürlich könnten Sie anders sein:

  • Wenn Eingang schlecht, wirft Argument
  • ein neues ProjectCrewsByProjectSpec Objekt erstellt
  • rep.GetList Anrufe und leitet spec es
  • Gibt eine nicht-null-DataSourceResult

Das erste, was Sie tun müssen, um in der Lage zu testen, dass GetProjectBySpec all die Dinge, die in dieser Liste tut, ist es zu Refactoring so dass es keine eigenen Abhängigkeiten erzeugt - stattdessen geben Sie ihm die Abhängigkeiten, die es benötigt, über Dependency Injection.

DI wirklich funktioniert am besten, wenn Sie von Schnittstelle injizieren, so lautet, unabhängig Klasse diese Methode bietet, Konstruktor für diese Klasse soll für eine Instanz nehmen Beispiel IRepositoryHelper und speichert sie in einem privaten Nur-Lese-Mitglied. Es sollte auch eine Instanz von IProjectCrewsByProjectSpecFactory, die Sie verwenden würden, um Ihre Spezifikation zu erstellen.Jetzt, da Sie testen wollen, was GetProjectBySpec tatsächlich mit diesen Abhängigkeiten tut, dann werden Sie ein spöttisches Framework wie Moq verwenden, auf das ich hier nur im folgenden Beispiel nicht eingehen werde.

Wenn keine dieser Klassen derzeit eine solche Schnittstelle implementiert, verwenden Sie einfach Visual Studio, um die Schnittstellendefinition für Sie basierend auf der Klassendefinition zu extrahieren. Wenn es sich um Drittklassen handelt, auf die Sie keinen Einfluss haben, könnte dies schwierig sein.

Aber nehmen wir einmal an, dass Sie Schnittstellen so definieren können: T '' s sollte gehen ...) Code unten ist nicht getestet oder auf Tippfehler überprüft!

public interface IRepositoryHelper<ProjectDGACrew> 
{ 
    IList<ProjectDGACrew> GetList(IProjectCrewsByProjectSpecFactory spec); 
} 

public interface IProjectCrewsByProjectSpecFactory 
{ 
ProjectDGACrew Create(int projectId, int seasonId, int episodeId); 
} 

Ihr Code würde dann so etwas wie dies am Ende aussehen:

//somewhere in your class definition 
private readonly IRepositoryHelper<T> repo; 
private readonly IProjectCrewsByProjectSpecFactory pfactory; 
//constructor 
public MyClass(IRepositoryHelper<ProjectDGACrew> repo, IProjectCrewsByProjectSpecFactory pfactory) 
{ 
this.repo = repo; 
this.pfactory=pfactory; 
} 
//method to be tested 
public DataSourceResult GetProjectBySpec(int projectId, int seasonId, int episodeId) 
{ 
      var spec = pfactory.Create(projectId, seasonId, episodeId); 
      var personList = repo.GetList(spec).Select(p => new 
       {//big query...}).ToDataSourceResult(); 
       return personList; 
} 

jetzt haben Sie 4 Prüfverfahren schreiben:

[TestMethod] 
[ExepctedException(typeof(ArgumentException)] 
public void SUT_WhenInputIsBad_ThrowsArgumentException() 
{ 
    var sut = new MyClass(null,null); //don't care about our dependencies for this check 
    sut.GetProjectBySpec(0,0,0); //or whatever is invalid input for you. 
    //don't care about the return, only that the method throws. 
} 



[TestMethod] 
public void SUT_WhenInputIsGood_CreatesProjectCrewsByProjectSpec() 
{ 
    //create dependencies using Moq framework. 
    var pf= new Mock<IProjectCrewsByProjectSpecFactory>(); 
    var repo = new Mock<IRepository<ProjectDgaCrew>>(); 
    //setup such that a call to pfactory.Create in the tested method will return nothing 
    //because we actually don't care about the result - only that the Create method is called. 
    pf.Setup(p=>p.Create(1,2,3)).Returns<ProjectDgaCrew>(new ProjectDgaCrew()); 
    //setup the repo such that any call to GetList with any ProjectDgaCrew object returns an empty list 
//again we do not care about the result. 
//This mock dependency is only being used here 
//to stop an exception being thrown from the test method 
//you might want to refactor your behaviours 
//to specify an early exit from the function when the factory returns a null object for example. 
    repo.Setup(r=>r.GetList(It.IsAny<ProjectDgaCrew>()).Returns<IList<ProjectDGACrew>>(new List<ProjectDgaCrew>()); 
    //create our System under test, inject our mock objects: 
    var sut = new MyClass(repo,pf.Object); 
    //call the method: 
    sut.GetProjectBySpec(1,2,3); 
    //and verify that it did indeed call the factory.Create method. 
    pf.Verify(p=>p.Create(1,2,3),"pf.Create was not called with 1,2,3");    
} 
     public void SUT_WhenInputIsGood_CallsRepoGetList(){} //you get the idea 
     public void SUT_WhenInputIsGood_ReturnsNonNullDataSourceResult(){}//and so on. 

Hoffnung, dass Sie etwas Hilfe gibt ... und natürlich können Sie Ihre Testklassen umgestalten, um viel von der spöttischen Installation zu vermeiden, und alles an einem einzigen Ort haben, um die Codezeilen auf ein Minimum zu beschränken.

3

Unit-Tests in der Regel (sollten) Testvertrag als nach Klasse Client verstanden. Wenn der Client Ihres Codes .GetProjectBySpec(1, 2, 3) aufruft, was erwartet er dann? Unit-Test sollte Antwort diese Frage:

Wann gibt es 5 Projekte im Repository (A, B, C, D, E) und ich rufe GetProjectBySpec mit Parametern , , , ich sollte Projekte bekommen B und C

In Ihrem Fall könnte es davon ab, was in //big query... Teil getan wird. Wenn Filterung/Umwandlung der Ergebnisse aus dem Repository zurückgegeben wird, sollten Sie dies testen.

Beachten Sie, dass Sie wahrscheinlich einige Dinge ändern, um müssen diesen Test isoliert zu machen (aus dem Repository/Datenbank):

  • RepositoryHelper.GetTake2Repository sollte Schnittstelle, injected als Abhängigkeit und mocked später in Einheit gewickelt werden Test
  • wenn new ProjectCrewsByProjectSpec komplexe Objekt erstellt, könnte man factory statt

wenn Sie Repository verspottet werde verwenden wollen, müssen Sie Weisen Sie einfach Ihren Mock an, einige im Voraus bekannte Listen von Elementen zurückzugeben, wenn sie mit dem entsprechenden Parameter spec aufgerufen werden. Dann kann Ihr Komponententest überprüfen, ob die von GetProjectBySpec zurückgegebenen Daten Ihren Erwartungen entsprechen.

0

Ich schreibe Tests auf diese Weise:

  1. einen Test schreiben für jeden Pfad durch den Code.
  2. Schreiben Sie einen Test für die Randbedingungen. Zum Beispiel: null, eins oder zwei Einträge in Ihrer Liste. Schlechte Parameter usw.
  3. Schreiben Sie negative Tests. Dies ist am schwierigsten, da Sie unendlich viele nutzlose Tests schreiben können. Ein gutes Beispiel ist die Überprüfung, dass Dinge, die nicht geändert werden sollen, nicht geändert werden.

Viel Glück

0

Das ist einfacher Weg, mit dem Sie auch im Inneren Daten testen können. Ändern von wo Sie gegangen sind.

[TestClass] 
    public class CrewControllerTest 
    { 
     [TestMethod] 
     public void GetProjectCrewsBySpecTest() 
     { 
      // arrange 
      const String ExpectedOutput = ""; 
      int projectId = 1; 
      int seasonId = 2; 
      int episodeId = 3; 

      // act 
      var crewController = new CrewController(); 
      var resultList= crewController.GetProjectCrewsBySpec(1, 2,3) as DataSourceResult; 
      var someInsideData = resultlist.FirstOrDefault().GetType().GetProperty("PropertyName").GetValue(resultList.FirstOrDefault(),null); 

      // assert 
      Assert.AreEqual(someInsideData , ExpectedOutput);   
     } 
} 
Verwandte Themen