Tags: , | Categories: Reflection, UnitTests Posted by talgiladi on 12/4/2008 10:23 AM | Comments (4)

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

להזכיר, רציתי להשוות שני אובייקטים – אחד מה שהקוד החזיר ואחד זה מה שמצופה, אז כתבתי מתודה פשוטה שתשווה בין כל 2 אובייקטים.

הוספתי בסוף אופציה להוריד בדיקה של חלק מהמאפיינים – כמו למשל זמן יצירה  שיכול להיות שונה בדטה בייס:

public static void Compare<T>(T expected, T actual, params string[] exclude)

        {

            Assert.IsNotNull(expected);

            Assert.IsNotNull(actual);

            List<string> p = new List<string>();

            if (exclude != null)

            {

                p.AddRange(exclude);

            }

            foreach (var pi in typeof(T).GetProperties())

            {

                if (p.SingleOrDefault(a => a.Equals(pi.Name, StringComparison.InvariantCultureIgnoreCase)) != null)

                {

                    continue;

                }

                Assert.AreEqual(pi.GetValue(expected, null), pi.GetValue(actual, null), pi.Name);

            }

        }

והשימוש פשוט כמובן:

[TestMethod()]

        public static void Test()

        {

            var a = new MailMessage()

            {

                Subject = "MYSubject",

                Body = Guid.NewGuid().ToString()

            };

            var b = new MailMessage()

            {

                Subject = "MYSubject",

                Body = Guid.NewGuid().ToString()

            };

            Compare<MailMessage>(a, b, null);//will fail

            Compare<MailMessage>(a, b, "Body");//will pass

        }

Technorati Tags: ,

Currently rated 5.0 by 1 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Tags: , | Categories: Reflection, UnitTests Posted by talgiladi on 3/2/2008 5:34 PM | Comments (4)

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

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

 FieldInfo a;

            FieldInfo b;

            Assert.AreEqual(a.FieldType, b.FieldType, "field type");

            Assert.AreEqual(a.IsAssembly, b.IsAssembly, "is assembly");

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

 public static void CompareObjects(object a, object b)

        {

            if (a == null) Assert.Fail("a is null");

            if (b == null) Assert.Fail("b is null");

            Type t1 = a.GetType();

            Type t2 = b.GetType();

            if (t1 != t2)

            {

                Assert.Fail("Type a (" + t1.Name + ") is not as type as type b(" t2.Name + ")");

            }

            PropertyInfo[] pi = t1.GetProperties();

            for (int i = 0; i < pi.Length; i++)

            {

                object val1 = pi[i].GetGetMethod().Invoke(a, null);

                object val2 = pi[i].GetGetMethod().Invoke(b, null);

                Assert.AreEqual(val1, val2, pi[i].Name);

            }

        }

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

public static void CompareObjects(object a, object b, string[] exclude)

        {

            if (a == null) Assert.Fail("a is null");

            if (b == null) Assert.Fail("b is null");

            Type t1 = a.GetType();

            Type t2 = b.GetType();

            if (t1 != t2)

            {

                Assert.Fail("Type a (" + t1.Name + ") is not as type b (" + t2.Name + ")");

            }

            PropertyInfo[] pi = t1.GetProperties();

            for (int i = 0; i < pi.Length; i++)

            {

                if (exclude != null && Array.IndexOf<string>(exclude, pi[i].Name) > -1)

                {

                    continue;

                }

                object val1 = pi[i].GetGetMethod().Invoke(a, null);

                object val2 = pi[i].GetGetMethod().Invoke(b, null);

                Assert.AreEqual(val1, val2, pi[i].Name);

            }

        }

כעת הפונקציה מקבלת רשימה של פרופרטיס שאותם לא צריך לבדוק לדוגמה:

 Customer a;

            Customer b;

            CompareObjects(a, b, new string[] { "Id", "LastUpdate" });

Technorati Tags: ,

Currently rated 4.0 by 1 people

  • Currently 4/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5