הי,

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

import cgi
cgi.FieldStorage = get_params

(כאשר אתה עושה import mytestcgi המודול cgi לא יאותחל מחדש, כי הוא כבר ב-
sys.modules).

2) טעינת מודול (import) לא אמורה להריץ קוד ביצועי, אלא לבצע פעולות
איתחול בלבד. במודולים שרוצים שיהיה אפשר להשתמש בהם גם בתור סקריפט, נהוג
לכתוב משהו כמו:
if __name__ == "__main__": run_main()
(כאשר הקוד הביצועי נמצע בפונקציה run_main).
ולמה אני אומר את זה? כי בדוגמה שלך נראה כאילו פעולת import mytestcgi
אמורה להדפיס את פלט ה- cgi.

אילו mytestcgi היה כתוב לפי הנוהג שציינתי לעיל אז החישובים וההדפסות
נעשים לא בזמן ה- import, אלא כאשר קוראים ל- run_main. במקרה כזה אפשר
לבצע את הדריכה על המשתנים שבסע' 1 בצורה קצת יותר ממוקדת, או אפילו להחליף
את cgi באוביקט משלך מבלי להתעסק עם sys.modules בכלל:

‪import mytestcgi # I assume mytestcgi does "import cgi"
mytestcgi.cgi = my_cgi_object # see comment below
mytestcgi.run_main()
‬
(הערה: אם mytestcgi עושה ‪"from cgi import ..."‬ ולא import cgi, אז צריך
לדרוך נקודתית על שמות הפונקציות שנטענו במקום על השם cgi)


3) סתם rant לגבי דריכות על מודולים : יש חבילות - למשל Pmw, שמחליפות (ב-
sys.modules) מודולים באוביקט משלהן, שאמור להתנהג כמו מודול.
מכיוון שהחיקוי אינו מלא זה גורם לצרות צרורות, וכל מיני דברים מפסיקים
לעבוד. נניח כאשר מנסים לעבור גרסת פיתון או להשתמש ב- import hooks.
בקיצור: לקוד פנימי, בדיקות ודיבג הכל טוב ויפה, אבל כאשר רוצים להפיץ
חבילת תוכנה למשתמשים כדאי לנסות לעבוד בצורה פשוטה ולהמנע מהתחכמויות מיותרות.


Shlomi Fish wrote:
> שלום לכולם!
> 
> אחד האלמנטים המרכזיים בתאוריה של בדיקות קוד היא זו של חיקוי (Mocking). הכוונה 
> היא ליצור אלמנט תכנותי שעבור הקוד הנבדק מתנהג כמו האלמנט הרגיל, אך למעשה אנו 
> שולטים בו, ומכתיבים מה הוא יחזיר, ועוקבים אחריו.
> 
> כעת נניח שיש לנו קוד קיים שטוען מודול וקורא לאובייקטים ופונקציות ממנו: איך 
> נוכל לעשות חיקוי של המודול מבלי לשנות את הקוד של המודול הנבדק?
> 
> אני מימשתי קוד כזה בפרל כאן:
> 
> http://search.cpan.org/src/SHLOMIF/SVN-RaWeb-Light-0.6.0/SVN/RaWeb/Light/Mock/CGI.pm
> 
> השורה המשמעותית היא:
> 
> BEGIN
> {
>     $INC{'CGI.pm'} = "/usr/lib/perl5/site_perl/5.8.6/CGI.pm",
> }
> 
> שגורמת למפרש לחשוב שהמודול כבר נטען.
> 
> תהיתי איך אפשר לעשות זאת בפייתון. קראתי על כך שהפקודה import רושמת את המודול 
> במשתנה sys.modules ולכן חיפשתי דרך לרשום שם מודול משלי. אחרי מספר נסיונות 
> ודיבורים עם אנשים ב-IRC, הצלחתי לכתוב קוד הוכחת-היתכנות שמחקה את מודול cgi:
> 
> <<<<<<<<<<<<
> import sys
> import new
> from StringIO import StringIO
> 
> class cgi_param:
>     def __init__(self, value):
>         self.value = value
>     def value(self):
>         return self.value
>     
> def get_params():
>     return {"hello":cgi_param("ShlomifTest")}
> 
> cgi_mod = new.module("cgi");
> cgi_mod.FieldStorage = get_params
> sys.modules['cgi'] = cgi_mod
> 
> save_stdout = sys.stdout
> sys.stdout = StringIO()
> import mytestcgi
> 
> buffer = sys.stdout.getvalue()
> 
> sys.stdout = save_stdout
> 
> if (buffer == "Content-Type: text/html\n\n\nShlomifTest\n"):
>     print "ok"
> else:
>     print "not ok: buffer == " + buffer
> 
> 
> 
> מה שקורה הוא שכאשר טוענים את המודול "mytestcgi" אשר מכיל את הקוד, אז הוא קולט 
> את המשתנים שאנו נותנים לו, ולא את מה שהמודול cgi האמיתי (שכלל לא נטען) נותן 
> לו.
> 

בברכה,
   עמית

לענות