NUnit - Comment tester toutes les classes qui implémentent une interface particulière

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

  •  09-06-2019
  •  | 
  •  

Question

Si j'ai de l'interface IFoo, et ont plusieurs classes qui l'implémentent, quel est le meilleur/le plus élégant/plus habile moyen de tester toutes les classes à l'encontre de l'interface?

J'aimerais réduire test de la duplication de code, mais encore "rester vrai" les principes de tests Unitaires.

Quelles seraient, selon vous la meilleure pratique?Je suis l'aide de NUnit, mais je suppose que des exemples de tout framework de test Unitaire serait valide

Était-ce utile?

La solution

Si vous avez des classes en œuvre toute une interface puis ils ont tous besoin de mettre en œuvre les méthodes de cette interface.Afin de tester ces classes dont vous avez besoin pour créer une unité de la classe de test pour chacune des classes.

Laisse aller avec un plus intelligente de la route au lieu;si votre objectif est de éviter de code et test de la duplication de code vous pouvez créer une classe abstraite plutôt que gère l' récurrents code.

E. g.vous avez l'interface suivante:

public interface IFoo {

    public void CommonCode();

    public void SpecificCode();

}

Vous pouvez créer une classe abstraite:

public abstract class AbstractFoo : IFoo {

    public void CommonCode() {
          SpecificCode();
    }

    public abstract void SpecificCode();

}

Les tests c'est facile;mettre en œuvre la classe abstraite dans la classe de test, soit comme un intérieur de classe:

[TestFixture]
public void TestClass {

    private class TestFoo : AbstractFoo {
        boolean hasCalledSpecificCode = false;
        public void SpecificCode() {
            hasCalledSpecificCode = true;
        }
    }

    [Test]
    public void testCommonCallsSpecificCode() {
        TestFoo fooFighter = new TestFoo();
        fooFighter.CommonCode();
        Assert.That(fooFighter.hasCalledSpecificCode, Is.True());
    }
}

...ou laissez-la classe de test étendre la classe abstraite même si cela correspond à votre fantaisie.

[TestFixture]
public void TestClass : AbstractFoo {

    boolean hasCalledSpecificCode;
    public void specificCode() {
        hasCalledSpecificCode = true;
    }

    [Test]
    public void testCommonCallsSpecificCode() {
        AbstractFoo fooFighter = this;
        hasCalledSpecificCode = false;
        fooFighter.CommonCode();
        Assert.That(fooFighter.hasCalledSpecificCode, Is.True());
    }        

}

Ayant une classe abstraite prendre soin de code commun qu'une interface implique donne une beaucoup plus propre conception du code.

J'espère que cela fait sens pour vous.


Comme une note de côté, il s'agit d'un modèle de conception appelé le Méthode de modèle de modèle.Dans l'exemple ci-dessus, le modèle de la méthode est la CommonCode méthode et SpecificCode est appelé un talon ou un crochet.L'idée est que n'importe qui peut s'étendre comportement sans avoir besoin de connaître les coulisses des trucs.

Beaucoup de cadres s'appuient sur ce modèle de comportement, par exemple ASP.NET où vous avez à mettre en œuvre les crochets dans une page ou un utilisateur des contrôles tels que le générés Page_Load la méthode qui est appelée par le Load événement, le modèle des appels de méthode les crochets derrière les coulisses.Il y a beaucoup d'autres exemples de cette.Fondamentalement, tout ce que vous avez à mettre en œuvre qui utilise les mots "charger", "init", ou "rendu" est appelé par une méthode de modèle.

Autres conseils

Je suis en désaccord avec Jon Limjap quand il dit,

Il n'est pas un contrat sur un.) comment la méthode doit être mis en œuvre et b).) ce que cette méthode devrait être en train de faire exactement (il ne garantit que le type de retour), les deux raisons que j'ai glaner serait votre motivation à vouloir ce genre de test.

Il pourrait y avoir de nombreuses parties du contrat n'est pas spécifié dans le type de retour.Un indépendant de la langue exemple:

public interface List {

  // adds o and returns the list
  public List add(Object o);

  // removed the first occurrence of o and returns the list
  public List remove(Object o);

}

Vos tests unitaires sur LinkedList, ArrayList, CircularlyLinkedList, et tous les autres devraient pas seulement que les listes se sont retournés, mais aussi qu'elles ont été correctement modifié.

Il y avait une question précédente sur la conception par contrat, ce qui peut aider à vous diriger dans la bonne direction sur une voie de se tarir ces tests.

Si vous ne voulez pas la surcharge de contrats, je vous recommande de bancs d'essai, à l'instar de ce Spoike recommandé:

abstract class BaseListTest {

  abstract public List newListInstance();

  public void testAddToList() {
    // do some adding tests
  }

  public void testRemoveFromList() {
    // do some removing tests
  }

}

class ArrayListTest < BaseListTest {
  List newListInstance() { new ArrayList(); }

  public void arrayListSpecificTest1() {
    // test something about ArrayLists beyond the List requirements
  }
}

Je ne pense pas que c'est la meilleure pratique.

La simple vérité est que l'interface n'est rien de plus qu'un contrat qu'une méthode est mise en œuvre.Il est pas un contrat sur un.) comment la méthode doit être mis en œuvre et b).) ce que cette méthode devrait être en train de faire exactement (il ne garantit que le type de retour), les deux raisons que j'ai glaner serait votre motivation à vouloir ce genre de test.

Si vous voulez vraiment être en contrôle de votre méthode de la mise en œuvre, vous avez la possibilité de:

  • La mise en œuvre de cette méthode dans une classe abstraite, et héritent de cette.Vous aurez toujours besoin d'en hériter dans un béton de classe, mais vous êtes sûr que sauf si c'est explicitement surchargée de la méthode ne sera que la chose correcte.
  • Dans .NET 3.5/C# 3.0, la mise en œuvre de la méthode comme une méthode d'extension de référencement à l'Interface

Exemple:

public static ReturnType MethodName (this IMyinterface myImplementation, SomeObject someParameter)
{
    //method body goes here
}

Toute mise en œuvre correctement référencement pour cette extension de la méthode émet précisément cette méthode d'extension si vous avez seulement besoin de le tester une fois.

@L'empereur XLII

J'aime le son de la combinatoire des tests dans MbUnit, j'ai essayé de la classe de base abstraite de l'interface technique de test avec NUnit, et même si elle fonctionne, vous aurez besoin d'avoir une salle de montage de test pour chaque interface d'une classe implémente (depuis en C# il n'y a pas d'héritage multiple - bien que les classes internes peuvent être utilisés, ce qui est plutôt cool).En réalité, c'est bien, peut-être même avantageuse, car elle regroupe vos tests pour la mise en œuvre par la classe de l'interface.Mais il serait bon que le cadre pourrait être plus intelligent.Si je pouvais utiliser un attribut pour marquer une classe comme une "officielle" de la classe de test pour une interface, et le cadre de recherche de l'assemblée en vertu de test pour toutes les classes qui implémentent l'interface, et d'exécuter ces tests.

Ça serait cool.

Comment à propos de la hiérarchie de [TestFixture]s des classes?Mettre le code de test dans la base de test de la classe et de l'héritage des enfants de classes de test..

Lors de l'essai d'une interface ou une classe de base contrat, je préfère laisser le framework de test automatiquement prendre en charge la recherche de tous les exécutants.Cela vous permet de vous concentrer sur l'interface en cours de test et d'être raisonnablement sûr que toutes les implémentations seront testés, sans avoir à faire beaucoup de manuel de mise en œuvre.

  • Pour xUnit.net, J'ai créé un Type De Résolution bibliothèque de recherche pour toutes les implémentations d'un type particulier (le xUnit.net les extensions sont juste un wrapper fin sur le Type de Résolveur de fonctionnalité, de sorte qu'il peut être adapté pour une utilisation dans d'autres cadres).
  • Dans MbUnit, vous pouvez utiliser un CombinatorialTest avec UsingImplementations les attributs sur les paramètres.
  • Pour les autres cadres de la classe de base, motif Spoike mentionnés peuvent être utiles.

Des tests sur les bases de l'interface, vous devez aussi vérifier que chaque individu suit la mise en œuvre de ses exigences particulières.

Je n'utilise pas NUnit mais je l'ai testé C++ interfaces.Je voudrais d'abord tester un TestFoo classe qui est une implémentation de base pour assurez-vous que le générique ça marche.Ensuite, vous avez juste besoin de tester le truc qui est unique pour chaque interface.

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