רקע

    בפוסט הקודם (תחליף ל-ExpectedException ב-Unit Test) כתבתי על בעיה שנתקלתי בה במהלך כתיבת Unit tests. היה צורך לבדוק שקריאה לשיטה מסוימת, מעלה חריג, ולא מבצעת אף פעולה נוספת. הפתרון של Microsoft לבדיקת שגיאות, ExpectedException, לא מספיק, כי אמנם הוא מאפשר לבדוק שאכן נזרק חריג, ואת הסוג שלו, אבל אחר כך ריצת המתודה מסתיימת, ואי אפשר לבדוק שאכן השיטה לא עשתה את הפעולה.

    הפתרון שהצעתי בפוסט הקודם היה מחלקה, קראתי לה ExceptionAssert, שמפעילה את הפונקציה המבוקשת, ובודקת שאכן נזרק חריג תואם, אם כן היא חוזרת להמשך הבדיקה, ואם לא מכשילה את הבדיקה.

    השימוש במחלקה הוא כך:

    Code Snippet
    [TestMethod]
    public void TestExamle()
    {
        BankAccount account = new BankAccount { Balance = 100 };
        ExceptionAssert.OfType<AccountException>(
            () => account.Withdraw(300));
        Assert.AreEqual(100, account.Balance);
    }

    הבדיקה מריצה את השיטה Withdraw ומניחה שיתרחש חריג, ולאחר מכן בודקת שאכן היתרה לא השתנתה.

    הצורך והעדכון

    מירי, חברת הצוות המוכשרת שלי, פנתה אלי שעדיין יש לה בעיה, יש מקרים שבהם היא צריכה לבדוק גם את הפרמטרים של ה-Exception ולא רק את הסוג שלו. בצורה הנוכחית שהמחלקה כתובה, אין לה דרך לגשת אל האוביקט שנזרק, ואין אפשרות לבדוק את זה.

    הפתרון שלנו היה שינוי קטן . וכך נראית המחלקה אחריו:

    Code Snippet
    static class ExceptionAssert
    {
        static public T OfType<T>(Action command) where T : Exception       //1
        {
            try
            {
                command.Invoke();
                string msg = string.Format(“Test method did not throw an exception. An exception of type {0} was expected.”, typeof(T).Name);
                Assert.Fail(msg);
                return null;    //4
            }
            catch (T ex)        //2
            {
                return ex;      //3
            }
        }
    }

    השינויים שנעשו הם (המספור תואם למספרים בקוד):

    1. השיטה הייתה מוגדרת ככזו שלא מחזירה ערך (void), ועכשיו היא מחזירה חריג מסוג T.
    2. קיבלתי את ה-Exception במשפט ה-catch.
    3. ואז החזרתי אותו.
    4. השורה הזו מיותרת ולא מגיעים אליה לעולם, ריצת הפונקציה נעצרת בשורה מעליה (בהנחה שעוד לא היה חריג שהקפיץ את הריצה לחלק ה-catch). הוספתי אותה רק כדי ש-Visual Studio לא יצעק עלי (“not all code paths return a value”). אז עכשיו כל נתיבי הקוד מחזירים ערך, ו-Visual Studio מרוצה.

    והנה שימוש בפועל במחלקה המעודכנת:

    Code Snippet
    [TestMethod]
    public void TestExample()
    {
        BankAccount account = new BankAccount { Balance = 100 };
        AccountException ex = ExceptionAssert.OfType<AccountException>(
            () => account.Withdraw(300));
        Assert.AreEqual(“Balance is too low”, ex.Message);
    }

    מקווה שהועלתי למישהו מהקוראים.