Pertanyaan Cara terbaik untuk menguji pengecualian dengan Assert untuk memastikan mereka akan dilemparkan


Menurut Anda apakah ini cara yang baik untuk menguji pengecualian? Ada saran?

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

Assert.IsNotNull(exception);

Saya menggunakan MS Test.


76
2018-04-12 00:12


asal


Jawaban:


Saya memiliki beberapa pola berbeda yang saya gunakan. Saya menggunakan ExpectedException atribut sebagian besar waktu saat pengecualian diharapkan. Ini sudah cukup untuk sebagian besar kasus, namun, ada beberapa kasus ketika ini tidak cukup. Pengecualian mungkin tidak dapat ditelusuri - karena dilempar oleh metode yang dipanggil oleh refleksi - atau mungkin saya hanya ingin memeriksa apakah ketentuan lain berlaku, katakanlah transaksi digulirkan kembali atau beberapa nilai masih ditetapkan. Dalam kasus ini saya membungkusnya dalam try/catch blok yang mengharapkan eksepsi yang tepat, apakah sebuah Assert.Fail jika kode berhasil dan juga menangkap pengecualian umum untuk memastikan bahwa pengecualian yang berbeda tidak dibuang.

Kasus pertama:

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

Kasus kedua:

[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



Saya baru di sini dan tidak memiliki reputasi untuk berkomentar atau downvote, tetapi ingin menunjukkan cacat dalam contoh di Jawaban Andy White:

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

Di semua kerangka pengujian unit yang saya kenal, Assert.Fail bekerja dengan melempar pengecualian, sehingga tangkapan generik akan menutupi kegagalan pengujian. Jika SomethingThatCausesAnException() tidak melempar, the Assert.Fail akan, tetapi itu tidak akan pernah meletus ke pelari pengujian untuk menunjukkan kegagalan.

Jika Anda perlu menangkap pengecualian yang diharapkan (yaitu, untuk menegaskan detail tertentu, seperti pesan / properti pada pengecualian), penting untuk menangkap jenis yang diharapkan spesifik, dan bukan kelas Exception dasar. Itu akan memungkinkan Assert.Fail pengecualian untuk keluar (dengan asumsi Anda tidak melemparkan jenis pengecualian yang sama yang dilakukan oleh kerangka pengujian unit Anda), tetapi masih memungkinkan validasi pada pengecualian yang dilemparkan oleh Anda SomethingThatCausesAnException() metode.


16
2018-04-12 19:01



Sekarang, 2017, Anda dapat melakukannya dengan lebih mudah dengan yang baru Kerangka MSTest V2:

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

12
2018-05-07 00:19



Pada v 2,5, NUnit memiliki tingkat metode berikut Asserts untuk menguji pengecualian:

Assert.Throws, yang akan menguji jenis pengecualian yang tepat:

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

Dan Assert.Catch, yang akan menguji untuk pengecualian dari jenis yang diberikan, atau jenis pengecualian yang berasal dari jenis ini:

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

Sebagai tambahan, ketika menguji unit debugging yang membuang pengecualian, Anda mungkin ingin mencegah VS dari melanggar pengecualian.

Edit

Hanya untuk memberikan contoh komentar Matius di bawah ini, kembalinya generik Assert.Throws dan Assert.Catch adalah pengecualian dengan jenis pengecualian, yang kemudian dapat Anda periksa untuk pemeriksaan lebih lanjut:

// 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



Sayangnya MSTest STILL hanya benar-benar memiliki atribut ExpectedException (hanya menunjukkan seberapa banyak MS peduli tentang MSTest) yang IMO cukup mengerikan karena melanggar pola Arrange / Act / Assert dan itu tidak memungkinkan Anda untuk menentukan secara tepat baris kode yang Anda harapkan pengecualian terjadi.

Saat saya menggunakan (/ dipaksa oleh klien) untuk menggunakan MSTest, saya selalu menggunakan kelas helper ini:

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

Contoh penggunaan:

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

10
2017-10-03 14:10



Sebagai alternatif untuk menggunakan ExpectedExceptionatribut, saya terkadang mendefinisikan dua metode yang bermanfaat untuk kelas pengujian saya:

AssertThrowsException() mengambil delegasi dan menegaskan bahwa itu melempar pengecualian yang diharapkan dengan pesan yang diharapkan.

AssertDoesNotThrowException() mengambil delegasi yang sama dan menegaskan bahwa itu tidak melempar pengecualian.

Pasangan ini bisa sangat berguna ketika Anda ingin menguji bahwa pengecualian dilempar dalam satu kasus, tetapi tidak yang lain.

Menggunakannya kode pengujian unit saya mungkin terlihat seperti ini:

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

Bagus dan rapi ya?

Saya AssertThrowsException() dan AssertDoesNotThrowException() metode didefinisikan pada kelas dasar umum sebagai berikut:

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



Tandai tes dengan ExpectedExceptionAttribute (ini adalah istilah dalam NUnit atau MSTest; pengguna kerangka pengujian unit lain mungkin perlu menerjemahkan).


4
2018-04-12 00:17