Soru Atacağından emin olmak için Assert ile istisnaları test etmenin en iyi yolu


Bunun istisnaları test etmek için iyi bir yol olduğunu mu düşünüyorsunuz? Baska öneri?

Exception exception = null;
try{
    //I m sure that an exeption will happen here
}
catch (Exception ex){
    exception = ex;
}

Assert.IsNotNull(exception);

MS Testini kullanıyorum.


76
2018-04-12 00:12


Menşei




Cevaplar:


Kullandığım birkaç farklı desen var. ben kullanıyorum ExpectedException Bir istisna beklenildiği zaman çoğu zaman. Bu çoğu vaka için yeterlidir, ancak bunun yeterli olmadığı bazı durumlar vardır. İstisna gösterilmeyebilir - yansıma tarafından çağrılan bir yöntemle atıldığından - ya da belki sadece diğer koşulların beklemediğini kontrol etmek istiyorum, bir işlemin geri sarıldığını veya bir değerin hala ayarlandığını söylemek istiyorum. Bu durumlarda onu bir try/catch Tam istisna bekler blok, bir Assert.Fail Kod başarılı olursa ve farklı bir özel durumun atılmadığından emin olmak için genel istisnaları yakalarsa.

İlk durum:

[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void MethodTest()
{
     var obj = new ClassRequiringNonNullParameter( null );
}

İkinci durum:

[TestMethod]
public void MethodTest()
{
    try
    {
        var obj = new ClassRequiringNonNullParameter( null );
        Assert.Fail("An exception should have been thrown");
    }
    catch (ArgumentNullException ae)
    {
        Assert.AreEqual( "Parameter cannot be null or empty.", ae.Message );
    }
    catch (Exception e)
    {
        Assert.Fail(
             string.Format( "Unexpected exception of type {0} caught: {1}",
                            e.GetType(), e.Message )
        );
    }
}

118
2018-04-12 00:40



Birçok birim test çerçevesi, onaylama hatalarını istisna olarak uygular. Yani ikinci durumda Assert.Fail () istisna mesajını gizleyecek olan catch (Exception) bloğu tarafından yakalanacaktır. Bir catch (NUnit.Framework.AssertionException) {throw;} veya benzeri eklemeniz gerekiyor - cevabımı görün. - GrahamS
@Graham - Bunu başımın üstünden yazdım. Normalde, bunun yanı sıra, istisna mesajını da yazdıracağım. Buradaki nokta, ikinci işleyicinin onaylama yetkisini yakalayacağından ve hatayla ilgili bilgileri "yeniden" göstereceğinden testin başarısız olacağıdır. - tvanfosson
Kodunuz işlevsel olarak sağlam olsa da, ExpectedException özniteliğini (çok kısıtlayıcı ve hataya meyilli olduğundan) veya her sınamada bir try / catch bloğu yazmanızı önermiyorum (çok karmaşık ve hataya eğilimli olduğundan). Test çerçeveniz tarafından sağlanan veya kendi yazdığınız iyi tasarlanmış bir assert yöntemi kullanın. Daha iyi bir kod elde edebilirsiniz ve farklı teknikler arasında seçim yapmak zorunda kalmazsınız ya da test değiştikçe birinden diğerine geçersiniz. Görmek stackoverflow.com/a/25084462/2166177 - steve
FYI - Kesinlikle yazılan xUnit kullanarak taşındım Assert.Throws Bu durumların her ikisini de kapsayan yöntem. - tvanfosson
ExpectedException özniteliği, istisnaların atılıp atılmadığını sınamak için kötü ve tarihli bir yöntemdir. Tam yanıtımı aşağıya bakın. - bytedev


Burada yeniyim ve yorumda bulunmak veya reddetmekle ilgili itibarım yok, ancak örnekte bir kusuru belirtmek istedim Andy White'ın cevabı:

try
{
    SomethingThatCausesAnException();
    Assert.Fail("Should have exceptioned above!");
}
catch (Exception ex)
{
    // whatever logging code
}

Tüm birim test çerçevelerinde aşina olduğum Assert.Fail Bir istisna atarak çalışır, bu yüzden jenerik yakalama aslında testin başarısızlığını maskeleyecektir. Eğer SomethingThatCausesAnException() fırlatmaz Assert.Fail olacak, ama bu başarısızlık belirtmek için test koşucuya asla kabarcık olmayacaktır.

Beklenen istisnayı yakalamanız gerekiyorsa (yani, istisnadaki mesaj / özellikler gibi belirli ayrıntıları belirtmek için), özel Exception sınıfını değil, belirli bir beklenen türü yakalamak önemlidir. Bu izin verir Assert.Fail dışarlama istisnası (birim sınama çerçevenizin yaptığı aynı türde bir istisna atmadığınızı varsayarak), ancak yine de sizin tarafınızdan atılan istisnada doğrulama yapılmasına izin verir. SomethingThatCausesAnException() yöntem.


16
2018-04-12 19:01





Şimdi, 2017, yeni ile daha kolay yapabilirsiniz MSTest V2 Çerçevesi:

Assert.ThrowsException<Exception>(() => myClass.MyMethodWithError());

12
2018-05-07 00:19





V'den itibaren 2.5, NUnit aşağıdaki yöntem seviyesine sahiptir Assertistisnaları test etmek için s:

Assert.ThrowsTam bir istisna türü için test edecek:

Assert.Throws<NullReferenceException>(() => someNullObject.ToString());

Ve Assert.CatchBu, belirli bir türde bir istisna veya bu türden türetilen bir istisna türü için test edecek:

Assert.Catch<Exception>(() => someNullObject.ToString());

Bir istisna olarak, istisnalar atan birim testleri hata ayıklama yaparken, VS önlemek isteyebilirsiniz istisna kırma.

Düzenle

Sadece Matthew'ın yorumuna bir örnek vermek gerekirse, jenerik geri dönüşü Assert.Throws ve Assert.Catch istisna tipine göre istisnadır, daha sonra incelemek için inceleyebilirsiniz:

// The type of ex is that of the generic type parameter (SqlException)
var ex = Assert.Throws<SqlException>(() => MethodWhichDeadlocks());
Assert.AreEqual(1205, ex.Number);

11
2017-10-25 11:33



Roy Osherove bunu, Birim Sanatı Testi, ikinci baskı, bölüm 2.6.2'de önermektedir. - Avi
severim Assert.Throwsek olarak, istisnayı döndürür, böylece istisna kendisi hakkında başka iddialar yazabilirsiniz. - Matthew
Soru MSTest değil NUnit içindi. - bytedev
@nashwan OP'nin asıl sorusu bu kalifikasyona sahip değildi ve etiketleme hala MS Testini hak etmiyor. Bu haliyle C #, .Net, Unit-Testing sorusudur. - StuartLC


Ne yazık ki MSTest STILL gerçekten sadece ExpectedException özelliğine sahiptir (sadece MS'in MSTest hakkında ne kadar önemsediğini gösterir) IMO oldukça kötüdür çünkü Düzen / Hareket / Assert modelini bozar ve istisnai olarak hangi kod satırını beklediğinizi belirtmenize izin vermez meydana gelmek.

MSTest'i kullanmak için (bir istemci tarafından) / kullanırken, her zaman bu yardımcı sınıfı kullanırım:

public static class AssertException
{
    public static void Throws<TException>(Action action) where TException : Exception
    {
        try
        {
            action();
        }
        catch (Exception ex)
        {
            Assert.IsTrue(ex.GetType() == typeof(TException), "Expected exception of type " + typeof(TException) + " but type of " + ex.GetType() + " was thrown instead.");
            return;
        }
        Assert.Fail("Expected exception of type " + typeof(TException) + " but no exception was thrown.");
    }

    public static void Throws<TException>(Action action, string expectedMessage) where TException : Exception
    {
        try
        {
            action();
        }
        catch (Exception ex)
        {
            Assert.IsTrue(ex.GetType() == typeof(TException), "Expected exception of type " + typeof(TException) + " but type of " + ex.GetType() + " was thrown instead.");
            Assert.AreEqual(expectedMessage, ex.Message, "Expected exception with a message of '" + expectedMessage + "' but exception with message of '" + ex.Message + "' was thrown instead.");
            return;
        }
        Assert.Fail("Expected exception of type " + typeof(TException) + " but no exception was thrown.");
    }
}

Kullanım örneği:

AssertException.Throws<ArgumentNullException>(() => classUnderTest.GetCustomer(null));

10
2017-10-03 14:10





Kullanmaya alternatif olarak ExpectedExceptionözniteliği, bazen test sınıflarım için iki yararlı yöntem tanımladım:

AssertThrowsException() bir temsilci alır ve beklenen iletiyi beklenen iletiyle atar.

AssertDoesNotThrowException() aynı delegeyi alır ve bir istisna atmadığını ileri sürer.

Bu eşleme, bir istisnanın bir durumda atıldığını test etmek istediğinizde çok yararlı olabilir, ancak diğerinde değil.

Onları kullanarak birim test kodum şöyle görünebilir:

ExceptionThrower callStartOp = delegate(){ testObj.StartOperation(); };

// Check exception is thrown correctly...
AssertThrowsException(callStartOp, typeof(InvalidOperationException), "StartOperation() called when not ready.");

testObj.Ready = true;

// Check exception is now not thrown...
AssertDoesNotThrowException(callStartOp);

Güzel ve düzgün ha?

Benim AssertThrowsException() ve AssertDoesNotThrowException() yöntemler, aşağıdaki gibi ortak bir temel sınıfta tanımlanır:

protected delegate void ExceptionThrower();

/// <summary>
/// Asserts that calling a method results in an exception of the stated type with the stated message.
/// </summary>
/// <param name="exceptionThrowingFunc">Delegate that calls the method to be tested.</param>
/// <param name="expectedExceptionType">The expected type of the exception, e.g. typeof(FormatException).</param>
/// <param name="expectedExceptionMessage">The expected exception message (or fragment of the whole message)</param>
protected void AssertThrowsException(ExceptionThrower exceptionThrowingFunc, Type expectedExceptionType, string expectedExceptionMessage)
{
    try
    {
        exceptionThrowingFunc();
        Assert.Fail("Call did not raise any exception, but one was expected.");
    }
    catch (NUnit.Framework.AssertionException)
    {
        // Ignore and rethrow NUnit exception
        throw;
    }
    catch (Exception ex)
    {
        Assert.IsInstanceOfType(expectedExceptionType, ex, "Exception raised was not the expected type.");
        Assert.IsTrue(ex.Message.Contains(expectedExceptionMessage), "Exception raised did not contain expected message. Expected=\"" + expectedExceptionMessage + "\", got \"" + ex.Message + "\"");
    }
}

/// <summary>
/// Asserts that calling a method does not throw an exception.
/// </summary>
/// <remarks>
/// This is typically only used in conjunction with <see cref="AssertThrowsException"/>. (e.g. once you have tested that an ExceptionThrower
/// method throws an exception then your test may fix the cause of the exception and then call this to make sure it is now fixed).
/// </remarks>
/// <param name="exceptionThrowingFunc">Delegate that calls the method to be tested.</param>
protected void AssertDoesNotThrowException(ExceptionThrower exceptionThrowingFunc)
{
    try
    {
        exceptionThrowingFunc();
    }
    catch (NUnit.Framework.AssertionException)
    {
        // Ignore and rethrow any NUnit exception
        throw;
    }
    catch (Exception ex)
    {
        Assert.Fail("Call raised an unexpected exception: " + ex.Message);
    }
}

9
2018-04-12 18:51





Testi ExpectedExceptionAttribute ile işaretleyin (bu NUnit veya MSTest'teki terimdir; diğer birim test çerçevelerinin kullanıcıları çevirmek zorunda kalabilir).


4
2018-04-12 00:17



ExpectedExceptionAttribute (aşağıdaki yazımda verilen neden) kullanmayın. NUnit Assert.Throws <YourException> () ve MSTest için aşağıdaki AssertException sınıfım gibi bir şey kullanır. - bytedev


Çoğu. Net birim test çerçevesi ile test yöntemine bir [ExpectedException] özniteliği koyabilirsiniz. Ancak bu, istisnanın beklediğiniz noktada gerçekleştiğini söyleyemez. Bu nerede xunit.net yardım edebilir.

Xunit ile Assert.Throws var, bu yüzden böyle şeyler yapabilirsiniz:

    [Fact]
    public void CantDecrementBasketLineQuantityBelowZero()
    {
        var o = new Basket();
        var p = new Product {Id = 1, NetPrice = 23.45m};
        o.AddProduct(p, 1);
        Assert.Throws<BusinessException>(() => o.SetProductQuantity(p, -3));
    }

[Gerçek] [TestMethod] xunit eşdeğeridir


2
2018-04-12 00:17



Eğer MSTest'i (sık sık işverenler tarafından zorlandım) kullanmanız gerekiyorsa, cevabımı aşağıya bakın. - bytedev