Utilisation de la même suite de tests sur différentes implémentations d'une interface de référentiel

StackOverflow https://stackoverflow.com/questions/81317

Question

J'ai créé une petite application Web jouet en C # inspirée de la vitrine Asp.net MVC de Rob Connery.

Je constate que j'ai une interface de référentiel, appelez-le IFooRepository, avec des méthodes, par exemple

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

Et j'ai trois implémentations de cela: ISqlFooRepository, IFileFooRepostory et IMockFooRepository.

J'ai aussi quelques cas de test. Ce que j'aimerais faire et que je n'ai pas encore trouvé comment faire, c'est d'exécuter les mêmes scénarios de test pour chacune de ces trois implémentations et de cocher la case verte pour chaque test sur chaque type d'interface.

par exemple

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

Je souhaite que cette méthode de test soit exécutée trois fois, avec des variations dans l’environnement qui lui permettent d’obtenir trois types de référentiels différents. À l'heure actuelle, j'ai trois classes de test copier-coller qui diffèrent uniquement par la mise en œuvre de la méthode d'assistance privée IFooRepository GetRepository (); Évidemment, ça sent mauvais.

Cependant, je ne peux pas simplement supprimer les doublons en consolidant les méthodes couper / coller, car elles doivent être présentes, publiques et marquées comme test pour que le test s'exécute.

J'utilise le framework de test Microsoft et je préférerais y rester si je le peux. Mais une suggestion sur la manière de procéder, par exemple, dans MBUnit serait également intéressante.

Était-ce utile?

La solution

Créez une classe abstraite contenant des versions concrètes des tests et une méthode abstraite GetRepository qui renvoie IFooRepository. Créez trois classes dérivées de la classe abstraite, chacune d'elles implémentant GetRepository de manière à renvoyer l'implémentation IFooRepository appropriée. Ajoutez les trois classes à votre suite de tests et vous êtes prêt.

Pour pouvoir exécuter les tests de manière sélective pour certains fournisseurs et non pour d'autres, envisagez d'utiliser l'attribut MbUnit '[FixtureCategory]' pour classer vos tests - les catégories suggérées sont 'rapides' 'lentes' 'db' 'importantes' 'et' sans importance '(les deux dernières sont des blagues - honnête!)

Autres conseils

Dans MbUnit, vous pouvez éventuellement utiliser l'attribut RowTest pour spécifier les paramètres de votre test.

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

Si vous avez vos 3 méthodes de test copiées et collées, vous devriez pouvoir les refactoriser (méthode d'extraction) pour éliminer la duplication.

i.e. Voici ce que j'avais en tête:

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);
}
[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);
}

Pour résumer, il y a trois façons de faire:

1) Réalisez les tests en utilisant des méthodes communes (réponse de Rick, également Hallgrim)

2) Utilisez la fonction RowTest de MBUnit pour automatiser cette opération (réponse de Jon Limjap). Je voudrais aussi utiliser une énumération ici, par exemple.

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

3) Utilisez une classe de base, répondez par le bélugabob
J'ai fait un échantillon basé sur cette idée

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;
    }
}

Ceci produit quatre tests de réussite dans deux classes de test.
Les points positifs de 3 sont:
1) Moins de code supplémentaire, moins de maintenance
2) Au moins dactylographie pour connecter un nouveau référentiel si nécessaire - cela se ferait à un endroit, contrairement aux autres.

Les inconvénients sont:
1) Moins de flexibilité pour ne pas exécuter de test sur un fournisseur si besoin est
2) Plus difficile à lire.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top