Question

Comment puis-je utiliser assert (ou une autre classe de test?) Pour vérifier qu'une exception a été levée?

Était-ce utile?

La solution

Pour "Visual Studio Team Test" il semble que vous appliquez l'attribut ExpectedException à la méthode du test.

Exemple de la documentation ici: Un test unitaire Walkthrough avec Visual studio Team test

[TestMethod]
[ExpectedException(typeof(ArgumentException),
    "A userId of null was inappropriately allowed.")]
public void NullUserIdInConstructor()
{
   LogonInfo logonInfo = new LogonInfo(null, "P@ss0word");
}

Autres conseils

En général, votre cadre de test aura une réponse pour cela. Mais si ce n'est pas assez flexible, vous pouvez toujours faire ceci:

try {
    somethingThatShouldThrowAnException();
    Assert.Fail(); // If it gets to this line, no exception was thrown
} catch (GoodException) { }

Comme le souligne @Jonas sur, cela ne fonctionne pas pour attraper une base Exception:

try {
    somethingThatShouldThrowAnException();
    Assert.Fail(); // raises AssertionException
} catch (Exception) {
    // Catches the assertion exception, and the test passes
}

Si vous devez absolument attraper exception, vous devez réémettre le Assert.Fail (). Mais vraiment, c'est un signe que vous ne devriez pas être d'écriture main ce; vérifier votre cadre de test pour les options, ou voir si vous pouvez lancer une exception plus significative pour tester.

catch (AssertionException) { throw; }

Vous devriez être en mesure d'adapter cette approche à ce que vous voulez - y compris en spécifiant quels types d'exceptions pour attraper. Si vous prévoyez que certains types, fini les blocs catch off avec:

} catch (GoodException) {
} catch (Exception) {
    // not the right kind of exception
    Assert.Fail();
}

Ma méthode préférée pour la mise en œuvre est d'écrire une méthode appelée lancers francs, et l'utiliser comme toute autre méthode Assert. Malheureusement, .NET ne vous permet pas d'écrire une méthode d'extension statique, de sorte que vous ne pouvez pas utiliser cette méthode comme si elle appartient en fait à la construction en classe Assert; juste faire un autre appelé MyAssert ou quelque chose de similaire. La classe ressemble à ceci:

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace YourProject.Tests
{
    public static class MyAssert
    {
        public static void Throws<T>( Action func ) where T : Exception
        {
            var exceptionThrown = false;
            try
            {
                func.Invoke();
            }
            catch ( T )
            {
                exceptionThrown = true;
            }

            if ( !exceptionThrown )
            {
                throw new AssertFailedException(
                    String.Format("An exception of type {0} was expected, but not thrown", typeof(T))
                    );
            }
        }
    }
}

Cela signifie que votre test unitaire ressemble à ceci:

[TestMethod()]
public void ExceptionTest()
{
    String testStr = null;
    MyAssert.Throws<NullReferenceException>(() => testStr.ToUpper());
}

Ce qui ressemble et se comporte beaucoup plus comme le reste de vos syntaxes de tests unitaires.

Si vous utilisez MSTest, qui à l'origine n'avait pas un attribut ExpectedException, vous pouvez faire ceci:

try 
{
    SomeExceptionThrowingMethod()
    Assert.Fail("no exception thrown");
}
catch (Exception ex)
{
    Assert.IsTrue(ex is SpecificExceptionType);
}

si vous utilisez NUnit, vous pouvez faire quelque chose comme ceci:

Assert.Throws<ExpectedException>(() => methodToTest());


Il est également possible de stocker l'exception lancée afin de valider davantage:

ExpectedException ex = Assert.Throws<ExpectedException>(() => methodToTest());
Assert.AreEqual( "Expected message text.", ex.Message );
Assert.AreEqual( 5, ex.SomeNumber);

Voir: http://nunit.org/docs/2.5/exceptionAsserts.html

Méfiez-vous d'utiliser ExpectedException, car il peut conduire à plusieurs écueils comme l'a démontré ici:

http: // geekswithblogs. net / sdorman / archive / 2009/01/17 / unité test-et-exceptions.aspx prévu

Et ici:

http://xunit.github.io/docs/comparisons.html

Si vous avez besoin de tester des exceptions, il y a moins désapprouvées façons. Vous pouvez utiliser la méthode try {act / échec} catch {} assert, qui peut être utile pour les cadres qui n'ont pas un soutien direct pour les tests d'exception autres que ExpectedException.

Une meilleure alternative est d'utiliser xUnit.NET, qui est un très moderne, tourné vers l'avenir et l'unité extensible cadre d'essai qui a appris de toutes les erreurs des autres, et l'amélioration. Une telle amélioration est Assert.Throws, qui fournit une syntaxe beaucoup mieux pour faire valoir des exceptions.

Vous pouvez trouver xUnit.NET à github: http://xunit.github.io/

Dans un projet de travail je suis sur que nous avons une autre solution faire.

D'abord, je ne aime pas le ExpectedExceptionAttribute Becuase il ne prend en considération que l'appel méthode qui a provoqué l'exception.

Je le fais avec un helpermethod à la place.

test

[TestMethod]
public void AccountRepository_ThrowsExceptionIfFileisCorrupt()
{
     var file = File.Create("Accounts.bin");
     file.WriteByte(1);
     file.Close();

     IAccountRepository repo = new FileAccountRepository();
     TestHelpers.AssertThrows<SerializationException>(()=>repo.GetAll());            
}

HelperMethod

public static TException AssertThrows<TException>(Action action) where TException : Exception
    {
        try
        {
            action();
        }
        catch (TException ex)
        {
            return ex;
        }
        Assert.Fail("Expected exception was not thrown");

        return null;
    }

Neat, il isnt;)

MSTest (v2) a maintenant une fonction Assert.ThrowsException qui peut être utilisé comme ceci:

Assert.ThrowsException<System.FormatException>(() =>
            {
                Story actual = PersonalSite.Services.Content.ExtractHeader(String.Empty);
            }); 

Vous pouvez l'installer avec NuGet: Install-Package MSTest.TestFramework

Il est un attribut sur la méthode d'essai ... vous n'utilisez pas Assertion. On dirait ceci:

[ExpectedException(typeof(ExceptionType))]
public void YourMethod_should_throw_exception()

Vous pouvez télécharger un package à partir Nuget en utilisant: PM> Installer-Package MSTestExtensions qui ajoute Assert.Throws () syntaxe dans le style de nUnit / xUnit à MSTEST.

instructions de haut niveau:. Télécharger l'assemblage et hériter de BaseTest et vous pouvez utiliser les Assert.Throws () syntaxe

La principale méthode pour la mise en œuvre Lancers se présente comme suit:

public static void Throws<T>(Action task, string expectedMessage, ExceptionMessageCompareOptions options) where T : Exception
{
    try
    {
        task();
    }
    catch (Exception ex)
    {
        AssertExceptionType<T>(ex);
        AssertExceptionMessage(ex, expectedMessage, options);
        return;
    }

    if (typeof(T).Equals(new Exception().GetType()))
    {
        Assert.Fail("Expected exception but no exception was thrown.");
    }
    else
    {
        Assert.Fail(string.Format("Expected exception of type {0} but no exception was thrown.", typeof(T)));
    }
}

Divulgation: Je mets ensemble ce paquet

.

Plus d'info: http: // www .bradoncode.com / blog / 2012/01 / affirmant-exceptions-en-MSTest-with.html

Vous pouvez y parvenir avec une simple ligne.

Si votre foo.bar() opération est async:

await Assert.ThrowsExceptionAsync<Exception>(() => foo.bar());

Si foo.bar() n'est pas async

Assert.ThrowsException<Exception>(() => foo.bar());

Je ne recommande pas d'utiliser l'attribut ExpectedException (puisqu'il est trop contraignant et sujettes à erreur) ou d'écrire un bloc try / catch dans chaque essai (car il est trop compliqué et sujette à erreur). Utilisez une méthode assert bien conçu - soit fourni par votre cadre de test ou d'écrire votre propre. Voici ce que je l'ai écrit et à l'utilisation.

public static class ExceptionAssert
{
    private static T GetException<T>(Action action, string message="") where T : Exception
    {
        try
        {
            action();
        }
        catch (T exception)
        {
            return exception;
        }
        throw new AssertFailedException("Expected exception " + typeof(T).FullName + ", but none was propagated.  " + message);
    }

    public static void Propagates<T>(Action action) where T : Exception
    {
        Propagates<T>(action, "");
    }

    public static void Propagates<T>(Action action, string message) where T : Exception
    {
        GetException<T>(action, message);
    }

    public static void Propagates<T>(Action action, Action<T> validation) where T : Exception
    {
        Propagates(action, validation, "");
    }

    public static void Propagates<T>(Action action, Action<T> validation, string message) where T : Exception
    {
        validation(GetException<T>(action, message));
    }
}

Exemples d'utilisation:

    [TestMethod]
    public void Run_PropagatesWin32Exception_ForInvalidExeFile()
    {
        (test setup that might propagate Win32Exception)
        ExceptionAssert.Propagates<Win32Exception>(
            () => CommandExecutionUtil.Run(Assembly.GetExecutingAssembly().Location, new string[0]));
        (more asserts or something)
    }

    [TestMethod]
    public void Run_PropagatesFileNotFoundException_ForExecutableNotFound()
    {
        (test setup that might propagate FileNotFoundException)
        ExceptionAssert.Propagates<FileNotFoundException>(
            () => CommandExecutionUtil.Run("NotThere.exe", new string[0]),
            e => StringAssert.Contains(e.Message, "NotThere.exe"));
        (more asserts or something)
    }

NOTES

Le retour du exception au lieu d'appuyer un rappel est de validation une idée raisonnable, sauf que cela rend ainsi la syntaxe d'appel de cette assertion très différente que les autres que j'utilise affirme.

Contrairement à d'autres, j'utilise « propage » au lieu de « lancers francs » puisque nous ne pouvons vérifier si une exception se propage d'un appel. Nous ne pouvons pas tester directement une exception est levée. Mais je suppose que vous pourriez l'image jette à dire:. Jetés et non pris

FINAL PENSEE

Avant de passer à ce genre d'approche que je considérais en utilisant l'attribut ExpectedException lorsqu'un seul test a vérifié le type d'exception et en utilisant un bloc try / catch si plus de validation était nécessaire. Mais, non seulement je dois penser à la technique à utiliser pour chaque test, mais en changeant le code d'une technique à l'autre que les besoins ont changé était pas l'effort trivial. L'utilisation d'une approche cohérente permet d'économiser l'effort mental.

Donc, en résumé, ce sport d'approche. Facilité d'utilisation, la flexibilité et la robustesse (difficile à faire mal)

L'aide fournie par @Richiban ci-dessus fonctionne très bien mais il ne gère pas la situation dans laquelle une exception est levée, mais pas le type attendu. Les adresses suivantes:

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace YourProject.Tests
{
    public static class MyAssert
    {
        /// <summary>
        /// Helper for Asserting that a function throws an exception of a particular type.
        /// </summary>
        public static void Throws<T>( Action func ) where T : Exception
        {
            Exception exceptionOther = null;
            var exceptionThrown = false;
            try
            {
                func.Invoke();
            }
            catch ( T )
            {
                exceptionThrown = true;
            }
            catch (Exception e) {
                exceptionOther = e;
            }

            if ( !exceptionThrown )
            {
                if (exceptionOther != null) {
                    throw new AssertFailedException(
                        String.Format("An exception of type {0} was expected, but not thrown. Instead, an exception of type {1} was thrown.", typeof(T), exceptionOther.GetType()),
                        exceptionOther
                        );
                }

                throw new AssertFailedException(
                    String.Format("An exception of type {0} was expected, but no exception was thrown.", typeof(T))
                    );
            }
        }
    }
}

Puisque vous mentionnez à l'aide d'autres classes d'essai, une meilleure option que l'attribut ExpectedException est d'utiliser rel="nofollow"> Shoudly » s Should.Throw .

Should.Throw<DivideByZeroException>(() => { MyDivideMethod(1, 0); });

Disons que nous avons une exigence que le client doit avoir une adresse pour créer un pour . Dans le cas contraire, la méthode de CreateOrderForCustomer devrait se traduire par un ArgumentException. Ensuite, nous pourrions écrire:

[TestMethod]
public void NullUserIdInConstructor()
{
  var customer = new Customer(name := "Justin", address := null};

  Should.Throw<ArgumentException>(() => {
    var order = CreateOrderForCustomer(customer) });
}

Il est mieux que d'utiliser un attribut ExpectedException parce que nous être précis sur ce qui devrait jeter l'erreur. Cela rend les exigences de nos tests plus clairs et permet également le diagnostic plus facile lorsque le test échoue.

Remarque il y a aussi un Should.ThrowAsync pour les tests de méthode asynchrone.

Comme alternative, vous pouvez tester les exceptions sont en fait d'être jeté avec les 2 lignes suivantes dans votre test.

var testDelegate = () => MyService.Method(params);
Assert.Throws<Exception>(testDelegate);

Eh bien je vais le faire à peu près résumer ce que tout le monde ici dit avant ... Quoi qu'il en soit, voici le code i construite d'après les bonnes réponses :) Tout reste à faire est de copier et utiliser ...

/// <summary>
/// Checks to make sure that the input delegate throws a exception of type TException.
/// </summary>
/// <typeparam name="TException">The type of exception expected.</typeparam>
/// <param name="methodToExecute">The method to execute to generate the exception.</param>
public static void AssertRaises<TException>(Action methodToExecute) where TException : System.Exception
{
    try
    {
        methodToExecute();
    }
    catch (TException) {
        return;
    }  
    catch (System.Exception ex)
    {
        Assert.Fail("Expected exception of type " + typeof(TException) + " but type of " + ex.GetType() + " was thrown instead.");
    }
    Assert.Fail("Expected exception of type " + typeof(TException) + " but no exception was thrown.");  
}

Dans VS tests unitaires intégré si vous voulez simplement vérifier que « toute exception » est jeté, mais vous ne savez pas le type, vous pouvez utiliser un fourre-tout:

[TestMethod]
[ExpectedException(typeof(Exception), AllowDerivedTypes = true)]
public void ThrowExceptionTest()
{
    //...
}

Consultez nUnit Docs pour des exemples sur:

[ExpectedException( typeof( ArgumentException ) )]

Cela va dépendre de ce cadre tests utilisez-vous?

Dans MbUnit, par exemple, vous pouvez spécifier l'exception attendue avec un attribut pour vous assurer que vous obtenez l'exception que vous attendez vraiment.

[ExpectedException(typeof(ArgumentException))]

En cas d'utilisation NUnit , essayez ceci:

Assert.That(() =>
        {
            Your_Method_To_Test();
        }, Throws.TypeOf<Your_Specific_Exception>().With.Message.EqualTo("Your_Specific_Message"));

Je Bien que ce soit une vieille question, comme d'ajouter une nouvelle pensée à la discussion. J'ai étendu la Réorganiser, Loi, modèle à Expecté Affirmer, Arranger, Loi, Assertion. Vous pouvez faire un pointeur d'exception attendue, affirmer alors il a été affecté. Cela se sent plus propre que faire votre Affirme dans un bloc catch, en laissant votre section Loi sur la plupart du temps juste pour une ligne de code pour appeler la méthode en cours de test. Vous n'avez pas à Assert.Fail(); ou return de plusieurs points dans le code. Toute autre exception levée entraînera l'échec du test, car il ne sera pas pris, et si une exception de votre type attendu est jeté, mais il n'a pas été celui que vous attendiez, Affirmant contre le message ou d'autres propriétés l'exception aider à vérifier votre test ne passera pas par inadvertance.

[TestMethod]
public void Bar_InvalidDependency_ThrowsInvalidOperationException()
{
    // Expectations
    InvalidOperationException expectedException = null;
    string expectedExceptionMessage = "Bar did something invalid.";

    // Arrange
    IDependency dependency = DependencyMocks.Create();
    Foo foo = new Foo(dependency);

    // Act
    try
    {
        foo.Bar();
    }
    catch (InvalidOperationException ex)
    {
        expectedException = ex;
    }

    // Assert
    Assert.IsNotNull(expectedException);
    Assert.AreEqual(expectedExceptionMessage, expectedException.Message);
}

Il y a une bibliothèque impressionnante appelée NFluent qui vitesses et facilite la façon dont vous écrire vos affirmations .

Il est assez simple d'écrire une affirmation pour lancer une exception:

    [Test]
    public void given_when_then()
    {
        Check.ThatCode(() => MethodToTest())
            .Throws<Exception>()
            .WithMessage("Process has been failed");
    }
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top