2008-09-17 9 views
3

Ich habe eine kleine Spielzeug-Webanwendung in C# nach dem Vorbild von Connery Asp.net MVC Storefront gemacht.Verwendung der gleichen Testsuite auf verschiedenen Implementierungen einer Repository-Schnittstelle

finde ich, dass ich ein Repository-Schnittstelle haben, rufen Sie es IFooRepository, mit Methoden, sagen

IQueryable<Foo> GetFoo(); 
void PersistFoo(Foo foo); 

Und ich habe drei Implementierungen dieser: ISqlFooRepository, IFileFooRepostory und IMockFooRepository.

Ich habe auch einige Testfälle. Was ich gerne tun würde und noch nicht herausgefunden habe, ist, dass ich die gleichen Testfälle für jede dieser drei Implementierungen ausführen muss und für jeden Testdurchlauf bei jedem Schnittstellentyp ein grünes Häkchen haben muss.

z.B.

[TestMethod] 
Public void GetFoo_NotNull_Test() 
{ 
    IFooRepository repository = GetRepository(); 
    var results = repository. GetFoo(); 
    Assert.IsNotNull(results); 
} 

Ich mag diese Testmethode dreimal ausgeführt werden, mit einem gewissen Variation in der Umgebung, die es drei verschiedene Arten von Repository erhalten kann. Zur Zeit habe ich drei ausgeschnittene und eingefügte Testklassen, die sich nur in der Implementierung der privaten Hilfsmethode IFooRepository GetRepository() unterscheiden; Offensichtlich ist das übelriechend.

Allerdings kann ich die Duplizierung nicht einfach durch Konsolidierung der ausgeschnittenen und eingefügten Methoden entfernen, da sie vorhanden sein müssen, öffentlich und als Test für den Test gekennzeichnet sein müssen.

Ich verwende das Microsoft-Testframework und würde es vorziehen, wenn ich kann. Aber auch ein Vorschlag, wie man dies beispielsweise in MBUnit tun könnte, wäre von Interesse.

Antwort

3

eine abstrakte Klasse erstellen, die konkrete Versionen der Tests und eine abstrakte GetRepository Methode enthält, die IFooRepository zurückgibt. Erstellen Sie drei Klassen, die von der abstrakten Klasse abgeleitet sind. Jede Klasse implementiert GetRepository so, dass die entsprechende IFooRepository-Implementierung zurückgegeben wird. Fügen Sie alle drei Klassen zu Ihrer Testsuite hinzu und schon können Sie loslegen.

Um die Tests für einige Anbieter und nicht für andere selektiv ausführen zu können, sollten Sie das Attribut '[FixtureCategory]' von MbUnit verwenden, um Ihre Tests zu kategorisieren - empfohlene Kategorien sind 'schnell' 'langsam' 'db' 'wichtig' und ‚unwichtig‘ (Die letzten beiden sind Witze - ehrlich!)

3

In MbUnit können Sie möglicherweise das RowTest-Attribut verwenden, um Parameter für Ihren Test anzugeben.

[RowTest] 
[Row(new ThisRepository())] 
[Row(new ThatRepository())] 
Public void GetFoo_NotNull_Test(IFooRepository repository) 
{ 
    var results = repository.GetFoo(); 
    Assert.IsNotNull(results); 
} 
+0

gute Antwort, aber wenn ich NUnit und RowTest verwendete, würde ich eine Enum im rowtest verwenden, wie in Ricks Antwort. Im Wesentlichen macht er einen RowTest auf die harte Tour. – Anthony

+0

Nun, ich habe meine Antwort vor Rick's gepostet. also habe ich keine Ahnung was er gemacht hat. –

1

Wenn Sie Ihre 3 Kopieren und Einfügen Testmethoden haben, sollten Sie in der Lage sein, Refactoring (Extrakt-Methode) es der Vervielfältigung loszuwerden.

das heißt ist es das, was ich im Sinn hatte:

private IRepository GetRepository(RepositoryType repositoryType) 
{ 
    switch (repositoryType) 
    { 
      case RepositoryType.Sql: 
      // return a SQL repository 
      case RepositoryType.Mock: 
      // return a mock repository 
      // etc 
    } 
} 

private void TestGetFooNotNull(RepositoryType repositoryType) 
{ 
    IFooRepository repository = GetRepository(repositoryType); 
    var results = repository.GetFoo(); 
    Assert.IsNotNull(results); 
} 

[TestMethod] 
public void GetFoo_NotNull_Sql() 
{ 
    this.TestGetFooNotNull(RepositoryType.Sql); 
} 

[TestMethod] 
public void GetFoo_NotNull_File() 
{ 
    this.TestGetFooNotNull(RepositoryType.File); 
} 

[TestMethod] 
public void GetFoo_NotNull_Mock() 
{ 
    this.TestGetFooNotNull(RepositoryType.Mock); 
} 
+0

Ok, das macht Sinn. Ich bin mir nicht sicher, dass es der beste Weg ist, aber es ist ein Weg. – Anthony

+0

Es ist in Ordnung, bis es viele Tests in der Suite gibt, dann gibt es eine Menge Wiederholungen. – belugabob

0
[TestMethod] 
public void GetFoo_NotNull_Test_ForFile() 
{ 
    GetFoo_NotNull(new FileRepository().GetRepository()); 
} 

[TestMethod] 
public void GetFoo_NotNull_Test_ForSql() 
{ 
    GetFoo_NotNull(new SqlRepository().GetRepository()); 
} 


private void GetFoo_NotNull(IFooRepository repository) 
{ 
    var results = repository. GetFoo(); 
    Assert.IsNotNull(results); 
} 
0

zusammenzufassen, gibt es drei Wege zu gehen:

1) Stellen Sie die Tests Einzeiler, die auf gemeinsame Methoden rufen nach unten (Antwort von Rick, auch Hallgrim)

2) Verwenden Sie MBUnit's Row Test-Funktion, um dies zu automatisieren (Antwort von Jon Limjap). Ich würde hier auch ein Enum verwenden, z.

[RowTest] 
[Row(RepositoryType.Sql)] 
[Row(RepositoryType.Mock)] 
public void TestGetFooNotNull(RepositoryType repositoryType) 
{ 
    IFooRepository repository = GetRepository(repositoryType); 
    var results = repository.GetFoo(); 
    Assert.IsNotNull(results); 
} 

3) Verwenden Sie eine Basisklasse, Antwort von belugabob
ich eine Probe auf der Basis dieser Idee gemacht haben

public abstract class TestBase 
{ 
    protected int foo = 0; 

    [TestMethod] 
    public void TestUnderTen() 
    { 
     Assert.IsTrue(foo < 10); 
    } 

    [TestMethod] 
    public void TestOver2() 
    { 
     Assert.IsTrue(foo > 2); 
    } 
} 

[TestClass] 
public class TestA: TestBase 
{ 
    public TestA() 
    { 
     foo = 4; 
    } 
} 

[TestClass] 
public class TestB: TestBase 
{ 
    public TestB() 
    { 
     foo = 6; 
    } 
} 

Dies erzeugt vier bestandene Tests in zwei Testklassen.
Upsides von 3:
1) Die am wenigsten zusätzlichen Code, am wenigsten Wartung
2) Die am wenigsten Eingabe in ein neues Repository zu stecken, wenn es sein muss - es an einem Ort durchgeführt werden würde, im Gegensatz zu den anderen.

Nachteile sind:
1) Weniger Flexibilität keinen Test gegen einen Anbieter ausgeführt werden, wenn nötig
2) schwerer zu lesen.