Question

J'utilise beaucoup Assert.Fail en TDD. Je travaille habituellement sur un test à la fois, mais lorsque j’ai des idées sur les choses que je veux implémenter plus tard, j’écris rapidement un test vide dans lequel le nom de la méthode de test indique ce que je veux implémenter comme une sorte de liste de tâches. Pour être sûr de ne pas oublier, je mets un Assert.Fail () dans le corps.

En testant xUnit.Net, j’ai découvert qu’ils n’avaient pas implémenté Assert.Fail. Bien sûr, vous pouvez toujours Assert.IsTrue (false) mais cela ne communique pas non plus mon intention. J'ai eu l'impression qu'Assert.Fail n'avait pas été implémenté exprès. Est-ce considéré comme une mauvaise pratique? Si oui pourquoi?

@Martin Meredith Ce n'est pas exactement ce que je fais. J'écris d'abord un test, puis j'implémente du code pour le faire fonctionner. En général, je pense à plusieurs tests à la fois. Ou je pense à un test à écrire quand je travaille sur autre chose. C'est alors que j'écris un test d'échec vide à retenir. Au moment où j'arrive à écrire le test, je travaille parfaitement test-en premier.

@Jimmeh Cela semble être une bonne idée. Les tests ignorés n'échouent pas mais ils apparaissent toujours dans une liste séparée. Je dois essayer ça.

@Matt Howells Bonne idée. NotImplementedException communique mieux l'intention que l'assert.Fail () dans ce cas

@Mitch Blé C'est ce que je cherchais. Il semble qu'il ait été laissé de côté pour éviter qu'il ne soit maltraité d'une autre manière. J'en abuse.

Était-ce utile?

La solution

Pour ce scénario, plutôt que d'appeler Assert.Fail, je procède comme suit (en C # / NUnit)

[Test]
public void MyClassDoesSomething()
{
    throw new NotImplementedException();
}

Il est plus explicite qu'un Assert.Fail.

Il semble exister un accord général sur le fait qu’il est préférable d’utiliser des assertions plus explicites que Assert.Fail (). Cependant, la plupart des frameworks doivent l'inclure car ils n'offrent pas une meilleure alternative. Par exemple, NUnit (et d’autres) fournissent un ExpectedExceptionAttribute pour vérifier que du code lève une classe d’exception particulière. Toutefois, pour vérifier qu'une propriété de l'exception est définie sur une valeur particulière, il est impossible de l'utiliser. Au lieu de cela, vous devez recourir à Assert.Fail:

[Test]
public void ThrowsExceptionCorrectly()
{
    const string BAD_INPUT = "bad input";
    try
    {
        new MyClass().DoSomething(BAD_INPUT);
        Assert.Fail("No exception was thrown");
    }
    catch (MyCustomException ex)
    {
         Assert.AreEqual(BAD_INPUT, ex.InputString); 
    }
}

La méthode xUnit.Net Assert.Throws rend cette tâche plus simple sans nécessiter de méthode Assert.Fail. En n'incluant pas de méthode Assert.Fail (), xUnit.Net encourage les développeurs à rechercher et à utiliser des alternatives plus explicites, et à prendre en charge la création de nouvelles assertions si nécessaire.

Autres conseils

Il a été délibérément laissé de côté. C'est la réponse de Brad Wilson quant à pourquoi il n'y a pas d'assert.Fail ():

  

Nous n'avons pas négligé cela, en fait. je   trouver Assert.Fail est une béquille qui   implique qu'il y a probablement un   affirmation manquante. Parfois c'est juste   la façon dont le test est structuré, et   parfois c'est parce qu'assert pourrait   utilisez une autre assertion.

J'ai toujours utilisé Assert.Fail () pour traiter les cas dans lesquels vous avez détecté qu'un test devait échouer par une logique dépassant la simple comparaison de valeurs. Par exemple:

try 
{
  // Some code that should throw ExceptionX
  Assert.Fail("ExceptionX should be thrown")
} 
catch ( ExceptionX ex ) 
{
  // test passed
}

Ainsi, l'absence d'Assert.Fail () dans le cadre me semble être une erreur. Je suggérerais de corriger la classe Assert pour inclure une méthode Fail (), puis de soumettre le correctif aux développeurs de l’infrastructure, ainsi que votre raisonnement pour l’ajouter.

Pour ce qui est de votre pratique consistant à créer des tests qui échouent intentionnellement dans votre espace de travail, rappelez-vous de les mettre en œuvre avant de vous engager, cela me semble être une bonne pratique.

J'utilise MbUnit pour mes tests unitaires. Ils ont une option Ignorer les tests, qui apparaissent en orange (plutôt qu'en vert ou en rouge) dans la suite de tests. Peut-être que xUnit a quelque chose de similaire, ce qui signifierait que vous n’avez même pas à faire d’assertion dans la méthode, car elle apparaîtrait dans une couleur agaçante, ce qui la rendrait difficile à rater?

Modifier:

Dans MbUnit, cela se présente de la manière suivante:

[Test]
[Ignore]
public void YourTest()
{ } 

C’est le motif que j’utilise lors de l’écriture d’un test pour le code et que je souhaite générer une exception par conception:

[TestMethod]
public void TestForException()
{
    Exception _Exception = null;

    try
    {
        //Code that I expect to throw the exception.
        MyClass _MyClass = null;
        _MyClass.SomeMethod();
        //Code that I expect to throw the exception.
    }
    catch(Exception _ThrownException)
    {   
        _Exception = _ThrownException
    }
    finally
    {
        Assert.IsNotNull(_Exception);
        //Replace NullReferenceException with expected exception.
        Assert.IsInstanceOfType(_Exception, typeof(NullReferenceException));
    }
}

IMHO, c’est un meilleur moyen de tester les exceptions que d’utiliser Assert.Fail (). La raison en est que non seulement je teste une exception, mais je vérifie également le type d’exception. Je me rends compte que c’est similaire à la réponse de Matt Howells, mais IMHO utilisant le dernier bloc est plus robuste.

Évidemment, il serait toujours possible d'inclure d'autres méthodes Assert pour tester la chaîne d'entrée des exceptions, etc. Je vous serais reconnaissant de vos commentaires et de votre point de vue sur mon modèle.

Personnellement, je n'ai aucun problème à utiliser une suite de tests en tant que liste de tâches à faire de cette manière, à condition que vous finissiez par écrire le test avant de mettre en œuvre le code à transmettre.

Cela dit, j’utilisais moi-même cette approche, même si j’aperçois à présent que cela me conduit à écrire trop de tests à l’avance, ce qui ressemble étrangement au problème inverse du fait de ne pas écrire de tests. du tout: vous finissez par prendre des décisions sur la conception un peu trop tôt IMHO.

Incidemment, dans MSTest, le modèle de test standard utilise Assert.Inconclusive à la fin de ses exemples.

À vrai dire, le framework xUnit.NET se veut extrêmement léger et oui, ils ont délibérément coupé Fail, pour encourager le développeur à utiliser une condition de défaillance explicite.

Wild supposition: retenir Assert.Fail est destiné à vous empêcher de penser qu'un bon moyen d'écrire du code de test est un énorme tas de spaghettis menant à un Assert.Fail dans les cas graves. [Modifier pour ajouter: les réponses des autres personnes le confirment largement, mais avec des citations]

Puisque ce n'est pas ce que vous faites, il est possible que xUnit.Net soit trop protecteur.

Ou peut-être pensent-ils qu'il est si rare et si peu orthogonal qu'il soit inutile.

Je préfère implémenter une fonction appelée ThisCodeHasNotBeenWrittenYet (en réalité, quelque chose de plus court, pour faciliter la frappe). Vous ne pouvez pas communiquer l'intention plus clairement que cela, et vous avez un terme de recherche précis.

Si cela échoue ou s'il n'est pas implémenté (pour provoquer une erreur de l'éditeur de liens) ou s'il s'agit d'une macro non compilée, vous pouvez le modifier en fonction de vos préférences actuelles. Par exemple, lorsque vous voulez exécuter quelque chose que est terminé, vous voulez un échec. Lorsque vous vous asseyez pour vous en débarrasser, vous souhaiterez peut-être une erreur de compilation.

Avec le bon code que je fais habituellement:

void goodCode() {
     // TODO void goodCode()
     throw new NotSupportedOperationException("void goodCode()");
}

Avec le code de test, je fais habituellement:

@Test
void testSomething() {
     // TODO void test Something
     Assert.assert("Some descriptive text about what to test")
}

Si vous utilisez JUnit et que vous ne voulez pas obtenir l'échec, mais l'erreur, je le fais habituellement:

@Test
void testSomething() {
     // TODO void test Something
     throw new NotSupportedOperationException("Some descriptive text about what to test")
}

Méfiez-vous Assert.Fail et son influence corruptrice pour obliger les développeurs à écrire des tests idiots ou interrompus. Par exemple:

[TestMethod]
public void TestWork()
{
    try {
        Work();
    }
    catch {
        Assert.Fail();
    }
}

C’est idiot, parce que le try-catch est redondant. Un test échoue s'il lève une exception.

Aussi

[TestMethod]
public void TestDivide()
{
    try {
        Divide(5,0);
        Assert.Fail();
    } catch { }
}

Ceci est cassé, le test réussira toujours quel que soit le résultat de la fonction Diviser. Encore une fois, un test échoue si et seulement s'il lève une exception.

Pourquoi voudriez-vous utiliser Assert.Fail pour dire qu’une exception devrait être levée? C'est inutile. Pourquoi ne pas simplement utiliser l'attribut ExpectedException?

Si vous écrivez un test qui échoue simplement, écrivez ensuite le code correspondant, puis écrivez le test. Ce n'est pas un développement piloté par les tests.

Techniquement, Assert.fail () ne devrait pas être nécessaire si vous utilisez correctement le développement piloté par les tests.

Avez-vous pensé à utiliser une liste Todo ou à appliquer une méthodologie GTD à votre travail?

MS Test a Assert.Fail () , mais également Assert.Inconclusive () . Je pense que l'utilisation la plus appropriée pour Assert.Fail () est si vous avez une logique en ligne qu'il serait difficile de mettre dans une assertion, bien que je ne puisse même pas penser à de bons exemples. Pour la plupart, si la structure de test prend en charge autre chose que Assert.Fail (), utilisez-le.

Je pense que vous devriez vous demander ce que les tests (initiaux) devraient faire.

D'abord, vous écrivez un test (ensemble de) tests sans implémentation. Peut-être aussi les scénarios de jours de pluie.

Tous ces tests doivent échouer, pour être correct: Donc, vous voulez réaliser deux choses: 1) Vérifiez que votre implémentation est correcte. 2) Vérifiez que vos tests unitaires sont corrects.

Maintenant, si vous utilisez TDD en amont, vous souhaitez exécuter tous vos tests, ainsi que les parties NYI. Le résultat de votre test total est réussi si: 1) Tout le matériel mis en œuvre réussit 2) Tous les produits de la JNI échouent

Après tout, ce serait une commission de test unitaire si vos tests unitaires réussissent en l'absence d'implémentation, n'est-ce pas?

Vous voulez vous retrouver avec un courrier de votre test d'intégration continue qui vérifie tout le code implémenté et non implémenté, et qui est envoyé si un code implémenté échoue ou si tout code non implémenté réussit. Les deux sont des résultats indésirables.

Il suffit d'écrire un [ignorer] tests qui ne feront pas le travail. Ni l'une ni l'autre des assertions qui arrêtent le premier échec d'assertion, sans exécuter d'autres lignes de test dans le test.

Maintenant, comment y arriver alors? Je pense que cela nécessite une organisation plus avancée de vos tests. Et il faut un autre mécanisme, puis affirmé pour atteindre ces objectifs.

Je pense que vous devez diviser vos tests et créer des tests qui s'exécutent complètement mais échouent, et vice versa.

Les idées sont de fractionner vos tests sur plusieurs assemblages, utilisez le regroupement de tests (les tests ordonnés dans mstest peuvent faire l'affaire).

Néanmoins, une version de CI qui envoie des courriers électroniques si tous les tests du service NYI échouent n’est pas simple et facile.

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