Bir istisnanın atıldığını doğrulamak için Assert'i nasıl kullanabilirim?


830

AssertBir istisnanın atıldığını doğrulamak için (veya başka bir Test sınıfını) nasıl kullanabilirim ?


Hangi birim test çerçevesini kullanıyorsunuz?
Kevin Pullin

3
Visual Studio Entegre
Alex

4
ExpectedException özelliği yardımcı olmuyor mu? ref: msdn.microsoft.com/tr-tr/library/…
shahkalpesh

2
Komik, bunun cevabını aramayı bitirdim, stackoverflow.com/questions/741029/testing-exceptions adresinde buldum .
dfjacobs

Yanıtlar:


978

"Visual Studio Team Test" için test yöntemine ExpectedException özniteliğini uyguladığınız görünür.

Buradaki belgelerden örnek: Visual Studio Team Testiyle Birim Testi İzlenecek Yol

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

25
Yukarıdaki ExpectedException özniteliği NUnit'te de çalışır (ancak [TestMethod] [Test] olmalıdır).
dbkk

5
@dbkk: NUnit'te tam olarak aynı şekilde çalışmaz - ileti, istisna iletiyi matcvh etmesi gereken bir dize olarak ele alınır (ve IU bunun daha anlamlı olduğunu düşünür)
Ruben Bartelink

29
Bu özellik işi yapar ve c # programcıları için yerleşik bir özelliktir, ancak yeterince esnek olmadığı için kullanmanızı önermiyorum. İstisna türü test kurulum kodunuz tarafından atılırsa ne olacağını düşünün: test başarılı olur, ancak gerçekte beklediğiniz gibi yapmadı. Ya da istisna nesnesinin durumunu test etmek isterseniz. Genellikle tüm mesajı test etmek yerine StringAssert.Contains (e.Message ...) kullanmak istiyorum. Diğer cevaplarda açıklandığı gibi bir onaylama yöntemi kullanın.
steve

3
NUnit 3.0'da bırakılacağı için NUnit'te ExpectedException kullanmaktan kaçının. Ben kullanım Assert.Throws () <SpecificException> tercih
Terence

5
MsTest içinde Assert.ThrowsException <T> ve Assert.ThrowsExceptionAsync <T> kullanabilirsiniz.
Gopal Krishnan

257

Genellikle test çerçeveniz bunun için bir cevap verecektir. Ancak yeterince esnek değilse, bunu her zaman yapabilirsiniz:

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

@Jonas'ın belirttiği gibi, bu bir temel İstisna yakalamak için işe yaramaz:

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

Kesinlikle İstisna yakalamanız gerekiyorsa, Assert.Fail () yöntemini yeniden oluşturmanız gerekir. Ama gerçekten, bu bunu el yazmamanız gereken bir işaret; seçenekler için test çerçevenizi kontrol edin veya test etmek için daha anlamlı bir istisna yapıp yapamayacağınıza bakın.

catch (AssertionException) { throw; }

Bu yaklaşımı, ne tür istisnaları yakalamak dahil olmak üzere, istediğiniz her şeye uyarlayabilmelisiniz. Yalnızca belirli türler bekliyorsanız, catchblokları aşağıdakilerle bitirin :

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

20
+1, sadece istisna türünün ötesinde iddialarda bulunmam gerektiğinde bu özelliği öznitelik yerine kullanıyorum. Örneğin, istisna örneğindeki belirli alanların belirli değerlere ayarlandığını kontrol etmesi gerekirse ne olur?
Pavel Repin

2
Hata mesajını belirtmeniz gerekmez. Bu yeterlidir: [ExpectedException (typeof (ArgumentException))]
mibollma

5
Bence bu çözüm en iyisi. [ExpectedException (typeof (ArgumentException))], test basitse kullanır, ancak benim görüşüme göre tembel bir çözümdür ve rahat olmak tuzaklara yol açabilir. Bu çözüm, daha doğru bir test yapmak için size özel kontrol sağlar, ayrıca test çalışması raporunuz için bir test Yazısı için, istisnanın gerçekten beklendiği gibi atıldığını yapabilirsiniz.
evilfish

12
Buna dikkat edin çünkü Assert.Fail () bir istisna oluşturur, eğer yakalarsanız test pasosu!
Jonas

4
@ Vinnyq12 Demek istediğim yukarıdaki örnekteki ilk testin asla başarısız olmayacağı. Bir istisna atılırsa (ExpectedExceptionAttribute tarafından "yakalanmaz") bir test başarısız olur
Jonas

113

Bunu uygulamak için tercih ettiğim yöntem Throws adlı bir yöntem yazmak ve bunu diğer Assert yöntemlerinde olduğu gibi kullanmaktır. Ne yazık ki, .NET statik bir uzantı yöntemi yazmanıza izin vermez, bu nedenle bu yöntemi aslında Assert sınıfındaki bir yapıya aitmiş gibi kullanamazsınız; MyAssert veya benzeri bir şey yapın. Sınıf şöyle görünür:

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

Bu, birim testinizin şöyle görüneceği anlamına gelir:

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

Hangi birim test sözdizimlerinizin geri kalanı gibi görünüyor ve davranıyor.


1
Bool bayrağından kurtulun ve daha kompakt bir uygulama için çağrıldıktan hemen sonra çizgiyi atın.
gt

11
Bunu daha iyi yapan tek şey, fonksiyonun yakalanan istisnayı döndürmesini sağlamaktır, böylece istisnadaki öznitelikler gibi şeylerin doğru olduğunu iddia etmeye devam edebilirsiniz.
Mark Hildreth

2
Teşekkürler! Bu benim için en iyi yaklaşım gibi görünüyor çünkü tek bir yöntemde birden fazla istisnayı test etmenin kısa bir yolu. Ayrıca çok daha okunabilir.
David Sherret

2
@MickeyPerlstein Nitelikler test için AAA kurallarını ihlal eder. Özellikle, Düzenlemeniz Yasaya bile geçmeden istisnayı atarsa, testiniz geçer ... eek!
freedomn-m

2
Microsoft nihayet MSTest - v2 desteklerini güncellemeye başladı Assert.ThrowsException<T>ve Assert.ThrowsExceptionAsync<T>bkz. Blogs.msdn.microsoft.com/visualstudioalm/2017/02/25/…
Quango

62

NUNIT kullanıyorsanız, böyle bir şey yapabilirsiniz:

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


Daha ileri doğrulamak için atılan istisnayı da saklamak mümkündür:

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

Bkz. Http://nunit.org/docs/2.5/exceptionAsserts.html


60

Başlangıçta bir ExpectedExceptionniteliği olmayan MSTest kullanıyorsanız , bunu yapabilirsiniz:

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

3
Bu işe yarıyor, ancak mantık aşırı derecede karmaşık olduğu için bunu genel olarak tavsiye etmiyorum. Kıvrımlı olduğunu söylememekle birlikte, bu kod bloğunu birden fazla test için yazıp yazmadığınızı düşünün - 10'lar, 100'ler testleri. Bu mantığın iyi tasarlanmış bir iddia yöntemine eklenmesi gerekir. Diğer cevaplara bakın.
steve

35

Burada gösterildiği gibi birkaç tuzağa neden olabileceğinden, ExpectedException'ı kullanmaya dikkat edin:

http://geekswithblogs.net/sdorman/archive/2009/01/17/unit-testing-and-expected-exceptions.aspx

Ve burada:

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

İstisnaları test etmeniz gerekiyorsa, yollarda daha az kaşlarını çattı. ExpectedException dışındaki özel durum testleri için doğrudan desteği olmayan çerçeveler için yararlı olabilecek try {act / fail} catch {assert} yöntemini kullanabilirsiniz.

Daha iyi bir alternatif, diğer tüm hatalardan öğrenilen ve geliştirilmiş çok modern, ileriye dönük ve genişletilebilir bir birim test çerçevesi olan xUnit.NET'i kullanmaktır. Böyle bir gelişme, istisnaları öne sürmek için çok daha iyi bir sözdizimi sağlayan Assert.Throws'dur.

XUnit.NET'i github'da bulabilirsiniz: http://xunit.github.io/


4
NUnit 2.5'in artık Assert.Throws stil sözdizimini de desteklediğine dikkat edin - nunit.com/index.php?p=releaseNotes&r=2.5
Alconja

Ünite testlerinin, ExpectedException kullanırken istisna hakkında bilgi vermek için durması beni deli ediyor. MS neden otomatik testlerde manuel bir adım atmanın iyi bir fikir olduğunu düşündü? Bağlantılar için teşekkürler.
Ant

@Ant: MS NUnit'i kopyaladı ... asıl soru şu ki, NUnit bunun neden iyi bir fikir olduğunu düşündü?
jrista

28

MSTest (v2) artık şu şekilde kullanılabilen Assert.ThrowsException işlevine sahiptir:

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

Nuget ile kurabilirsiniz: Install-Package MSTest.TestFramework


2018'de bu, en iyi uygulama olarak kabul edilir, çünkü başka bir kodu değil, yalnızca test edilen ünitenin fırlatıp atmadığını kontrol eder.
CM

24

Üzerinde çalıştığım bir projede bunu yapan başka bir çözümümüz var.

İlk olarak ExpectedExceptionAttribute sevmiyorum hangi istisna neden hangi yöntem çağrısı dikkate alır çünkü sevmiyorum.

Bunu bir yardımcı yöntemle yapıyorum.

Ölçek

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

Düzgün, değil;)


14

Test yönteminin bir özelliğidir ... Assert kullanmazsınız. Buna benzer:

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

13

Nuget'ten şu paketi kullanarak bir paket indirebilirsiniz: PM> Install-Package MSTestExtensions , MsTest'e nUnit / xUnit tarzında Assert.Throws () sözdizimi ekler .

Üst düzey talimatlar: montajı indirin ve BaseTest'ten devralın , Assert.Throws () sözdizimini kullanabilirsiniz.

Throws uygulamasının ana yöntemi aşağıdaki gibidir:

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

Açıklama: Bu paketi bir araya getirdim.

Daha Fazla Bilgi: http://www.bradoncode.com/blog/2012/01/asserting-exceptions-in-mstest-with.html


Örnek için teşekkürler. Assert.DoesNotThrow () veya eşdeğerini test etme örneğiniz var mı?
Lane Goolsby

10

Bunu basit bir tek satırla yapabilirsiniz.

Operasyonunuz Eğer foo.bar()zaman uyumsuz geçerli:

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

Eğer foo.bar()zaman uyumsuz değildir

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

1
Başka yanıtlar da var, benim için sadece Exception Type tarafından bilinen başarısızlık koşullarını test etmenin kısa bir yolunu arıyordum, bu en kolay okunabilir test senaryoları için yapar. Not: özel durum türü, standart try-catch gibi devralınan özel durum sınıfları ile eşleşmez, bu nedenle yukarıdaki örnek, örneğin bir tuzak ArgumentException. Eski Try Catch ve test istisna yanıtı, test etmek için gelişmiş kriterleriniz varsa hala tercih edilir, ancak çoğu durumum için bu çok yardımcı olur!
Chris Schaller

5

ExpectedException özniteliğini (çok kısıtlayıcı ve hata eğilimli olduğundan) veya her sınamada bir try / catch bloğu yazmak için (çok karmaşık ve hataya yatkın olduğundan) önerilmez. Test çerçeveniz tarafından sağlanan veya kendiniz yazdığınız iyi tasarlanmış bir onaylama yöntemi kullanın. İşte yazdıklarım ve kullandıklarım.

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

Örnek kullanımlar:

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

NOTLAR

Doğrulama geri aramasını desteklemek yerine istisnayı döndürmek makul bir fikirdir, ancak bunu yapmak bu iddianın çağrı sözdizimini kullandığım diğer iddialardan çok farklı kılar.

Diğerlerinden farklı olarak, sadece bir çağrıdan bir istisnanın yayılıp yayılmadığını test edebileceğimiz için, 'atar' yerine 'yayılımları' kullanıyorum. Bir istisna atıldığını doğrudan test edemeyiz. Ama sanırım görüntü atışları demek olabilir: atıldı ve yakalanmadı.

SON DÜŞÜNCE

Bu tür bir yaklaşıma geçmeden önce, bir test yalnızca istisna türünü doğruladığında ExpectedException özniteliğini ve daha fazla doğrulama gerekiyorsa bir try / catch bloğu kullanmayı düşündüm. Ancak, sadece her test için hangi tekniğin kullanılacağını düşünmek zorunda kalmayacağım, aynı zamanda ihtiyaçlar değiştikçe kodu bir teknikten diğerine değiştirmek önemsiz bir çaba değildi. Tek bir tutarlı yaklaşım kullanmak zihinsel çabadan tasarruf sağlar.

Özetle, bu yaklaşım spor: kullanım kolaylığı, esneklik ve sağlamlık (yanlış yapmak zor).


4

Yukarıdaki @Richiban tarafından sağlanan yardımcı, bir istisna atıldığı durumla başa çıkmadığı, ancak beklenen türle ilgilenmediği için harika çalışıyor. Aşağıdaki adresler:

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

2
Hmmm ... Fikri anlıyorum, ama daha iyi olduğuna katıldığımdan emin değilim. Belirli bir istisnanın ortaya çıkmasını sağlamak istediğimiz için, diğerlerinin bir iddia hatası olarak tamamlanması gerektiği anlamına gelmez. IMHO'nun bilinmeyen bir istisnası, yığını diğer herhangi bir iddia işleminde olduğu gibi patlatmalıdır.
Crono

@Martin İstisna içeren kodu kaldıracağımDiğer ve sadece ikinci yakalama maddesinden tekrar geri dönün
Tom Lint

4

Diğer test sınıflarını kullandığınızdan bahsettiğiniz için, ExpectedExceptionnitelikten daha iyi bir seçenek Shoudly 's Should.Throw'u kullanmaktır .

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

Diyelim ki müşteri , sipariş oluşturmak için bir adrese sahip olmalı . Değilse, yöntem bir . Sonra şunu yazabiliriz:CreateOrderForCustomerArgumentException

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

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

Bu, bir ExpectedExceptionözniteliği kullanmaktan daha iyidir çünkü hatayı neyin atması gerektiği konusunda spesifik davranıyoruz. Bu, testlerimizdeki gereksinimleri daha net hale getirir ve test başarısız olduğunda teşhisi kolaylaştırır.

Ayrıca Should.ThrowAsync, eşzamansız bir yöntem testi için de olduğunu unutmayın .


4

Alternatif olarak, testinizdeki sonraki 2 satırla birlikte istisnaların test edilmesini deneyebilirsiniz.

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

4

VS yerleşik birim testinde, "herhangi bir istisna" nın atıldığını doğrulamak istiyorsanız, ancak türü bilmiyorsanız, tümünü yakalama özelliğini kullanabilirsiniz:

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

3

Peki ben hemen hemen herkes burada ne dedi özetlemek ... Her neyse, işte ben iyi cevaplara göre inşa kod :) Yapmanız gereken tek şey kopyalamak ve kullanmak ...

/// <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.");  
}


2

Bu, hangi test çerçevesini kullandığınıza bağlı olacaktır.

Örneğin MbUnit'te, gerçekten beklediğiniz istisnayı aldığınızdan emin olmak için beklenen istisnayı bir öznitelikle belirtebilirsiniz.

[ExpectedException(typeof(ArgumentException))]

2

NUnit kullanılması durumunda şunu deneyin:

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

2

Denilen müthiş bir kütüphane bulunmaktadır NFluent size iddialarını yazma biçimini hızlandırır ve kolaylaştırır .

Bir istisna atmak için bir iddia yazmak oldukça basittir:

    [Test]
    public void given_when_then()
    {
        Check.ThatCode(() => MethodToTest())
            .Throws<Exception>()
            .WithMessage("Process has been failed");
    }

1

Bu eski bir soru olmasına rağmen, tartışmaya yeni bir düşünce eklemek istiyorum. Beklenecek Yerleştir, Yasa, Talep desenini, Yerleştir, Yasa, Talep'i genişlettim. Beklenen bir istisna işaretçisi yapabilir ve ardından atandığını iddia edebilirsiniz. Bu, Eklerinizi bir catch bloğunda yapmaktan daha temiz hissettirir ve Yasa bölümünüzü çoğunlukla test edilen yöntemi çağırmak için tek bir kod satırı için bırakır. Ayrıca kodda birden fazla noktaya Assert.Fail();veya returnnoktadan da gerek yoktur . Atılan diğer istisnalar, testin başarısız olmasına neden olur, çünkü yakalanmaz ve beklenen türünüzün bir istisnası atılırsa, ancak beklediğiniz kişi değildi, mesaja veya diğer özelliklerine karşı iddiada bulunmak istisna, testinizin yanlışlıkla geçmemesine yardımcı olur.

[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);
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.