Reflection לבדיקות אוטומטיות

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

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

נקודות הכניסה האלו נקראות hooks והם באות לחשוף החוצה API בכדיי לאפשר, בין היתר, לבודקים לעבוד על התוכנית שלהם.

אך מה קורה כאשר קובץ אסמבלי מגיע ללא ה-AP

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

כל מה שעומד לרשותינו הוא קובץ Exe עליו נבצע את הבדיקות.

I הזה? לא נרצה לסבך את הקובץ וליצור hooks כאלו מהצד שלנו (זו למעשה תהיה העדיפות האחרונה) , נצטרך להתמודד עם הקובץ As Is.

השיטה המקובלת נקראת – Reflection. התוכנית שלנו (הבדיקה האוטומטית)  תיקח את קובץ Assembly (שזה EXE או DLL ) , תטען אותו לזכרון (בשיטה שנקראת סיריאליזציה), תיצור ממנו טיפוס חדש שממנו יוצרים אח"כ אובייקט חדש שהוא למעשה אובייקט המשתקף (Reflected) לאובייקט המקורי, עליו ניתן לעשות מניפולציות ככל העולה על רוחנו.

את הדוגמא שבחרתי לקחת , כל אחד בעל Visual Studio 2008 ומעלה יכול לבצע. ניקח פרוייקט אחד מאוסף הדוגמאות שלהם , נפתח אותו ונשלוף את קובץ ה-Exe שהא מייצר.

אז נתחיל, בואו נפתח את ה-Visual Studio (ההדגמה שלי מתבצעת על גרסת 2010), נקיש על Help->Samples

משם נקיש על הלינק של  Visual Studio 2010 Samples :

ונפתח את קובץ ה-Zip שנקרא: CSharpSamples

 

נעתיק את כל הקבצים והספריות מתוך קובץ ה-Zip אל המחשב שלנו ונפתח את קובץ ה-Solution המרכזי: LinqSamples.sln

נוודא כי הפרוייקט WinFormsDataBinding קיים:

ניצור ממנו קובץ אסמבלי , ע"י Build של הפרוייקט – כפתור F6

זהו, יש לנו כעת קובץ Exe עליו נרצה לעשות בדיקות, הקובץ יושב כאן: LinqSample\WinFormsDataBinding\bin\Debug\WinFormsDataBinding.exe

ואם נפתח אותו נראה שהוא מכיר טבלה בה שמות עובדים , מספרי עובדים והמנהלים שלהם:

המטרה שלנו היא לדמות משתמש אשר רוצה לפתוח את הטבלה, לשנות בה ערכים, לשלוח אותם ל-DataBase , ולקרוא ממנה נתונים.

אז תחילה נרצה לפתוח את הטבלה (ה-Form), כשכל מה שיש לרשותינו כעת זה קובץ Exe על הדיסק שנבנה במעמד הקומפילציה של פרוייקט WinFormsDataBinding .

בכדיי שנוכל להיכנס לקרביים של הקובץ נצטרך לעשות לו תהליך שנקרה סיריליזציה, בו נטען את האסמבלי אל הזיכרון של של המחשב.
vstring fileName = Path.Combine(Application.StartupPath, "WinFormsDataBinding.exe");
Assembly winFormsDataBinding = Assembly.LoadFile(fileName);

פתיחת ה-Form (טבלה)

ה-Form הראשי מיוצג על ידי WinFormsDataBinding.EmployeeForm באסמבלי של ה-WinFormsDataBinding, לכן תחילה נטען אותו מהאסמבלי:

Type formType = winFormsDataBinding.GetType("WinFormsDataBinding.EmployeeForm", true, true);

כעת כשיש לנו את ה-Type של ה-Form , נוכל ליצור Instance שלו (למעשה ניצור Form) על ידי המתודה הבאה:

Form ShowForm(Type formType) {
      Form newForm = Activator.CreateInstance(formType) as Form;

      newForm.Show();
      newForm.Refresh();
      Application.DoEvents();
      return newForm;
 }

השתמשנו פה ב-System.Activator.CreateInstance בכדיי ליצור את ה-Form , אח"כ נציגו על המסך.

שינוי תא בטבלה

בואו נסתכל על המתודה הבאה:

void SetCellText(Type formType, string newName) {
            Form newForm = ShowForm(formType);
            DataGridView dataGrid = FindDataGrid(formType, newForm);
            DataGridViewCell cell = FindCell(dataGrid);
            cell.Value = newName;
            PushSubmitChangesButton(formType, newForm);
            CloseForm(newForm);
        }

תחילה קראנו ל ShowForm ליצירת Instance של ה-Form והצגתו אח"כ קראנו לעוד שתי מתודות.

האחת מוצאת את ה-DataGrid והשנייה מוצאת את התא בתוכו. נציגם כ-קוד:

DataGridView FindDataGrid(Type formType, Form newForm) {
            FieldInfo dataGridField = formType.GetField(
                "employeeDataGridView", 
                BindingFlags.NonPublic | BindingFlags.Instance);
            DataGridView dataGrid = dataGridField.GetValue(newForm) as DataGridView;
            return dataGrid;
        }

השתמשנו פה במתודת GetField  בכדיי למצוא אזור ספציפי בטבלה שנקרא: employeeDataGridView. אח"כ הוצאנו את הערך שלו אל תוך אובייקט DataGrid.

כעת נמצא את התא בטבלה:

DataGridViewCell FindCell(DataGridView dataGrid) {
            int rowNum = FindRow(dataGrid);
            DataGridViewCell cell = dataGrid.Rows[rowNum].Cells[2];
            return cell;
        }

והמימוש של FindRow מתואר כאן:

int FindRow(DataGridView dataGrid) {
            for (int i = 0; i < dataGrid.Rows.Count - 1; i++)
            {
                if (dataGrid.Rows[i].Cells[0].Value.ToString() == "1")
                {
                    return i;
                }
            }
            return 0;
        }

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

cell.Value = newName;

שליחת השינויים ל-DataBase

בכדיי לעשות פעולה זו, עלינו כמובן להקיש באופן אוטומטי על כפתור ה-Submit Form שבתחתית הטבלה, וכשכפתור זה נלחץ, מאחורי הקלעים נפעיל את המתודה: PushSubmitChangesButton

void PushSubmitChangesButton(Type formType, Form newForm) {
            MethodInfo submitChanges_Click = formType.GetMethod(
                "submitChanges_Click", 
                BindingFlags.Instance | BindingFlags.NonPublic);
            submitChanges_Click.Invoke(newForm, new object[] { null, null });
        }

שוב השתמשנו פה ב-reflection בכדיי "לדוג" את המתודה מה-Form Type , אח"כ הפעלנו את המתודה על אובייקט חדש: newForm.

קריאת נתונים מתא בטבלה

מתודת ה-GetCellText דומה מאוד למתודת SetCellText שבה כבר השתמשנו:

string GetCellText(Type formType) {
            Form newForm = ShowForm(formType);
            DataGridView dataGrid = FindDataGrid(formType, newForm);
            DataGridViewCell cell = FindCell(dataGrid);
            string result = cell.Value.ToString();
            CloseForm(newForm);
            return result;
        }

ולסיום:

הנה התוכנית המלאה (ה-Main של התוכנית):

string fileName = Path.Combine(Application.StartupPath, "WinFormsDataBinding.exe");
Assembly winFormsDataBinding = Assembly.LoadFile(fileName);

Type formType = winFormsDataBinding.GetType("WinFormsDataBinding.EmployeeForm", true, true);
            
SetCellText(formType, "Smith");
string newName = GetCellText(formType);
if (newName != "Smith") {
   throw new Exception("It didn't rename");
}
SetCellText(formType, "Davolio");
Scroll to Top
דילוג לתוכן