"עיצוב מונחה בדיקות" , זה המושג המדויק... מה הכוונה? ובכן מתוך מספר שיטות תיכנון ועיצוב תוכנה, אחת השיטה נקראת extremeprogramming ואחת מנקודות השיטה הזאת היא שאנו בונים את האפליקציה על בסיס בדיקות. אבל לא כרגיל שאנו בונים את האפליקציה ואז עושים בדיקות, אלא קודם מציבים את הבדיקות ואז בונים את האפליקציה כך שתוכל לעבור את הבדיקות! אני לא ארחיב כעת על התיאוריה וכל מה שקשור, אני רק אראה צורה מאוד נוחה, שיכולה לשמש בכל אפליקציה, לבצע בדיקות באפליקציה. זה אומנם נראה קצת מסורבל בהתחלה, אבל אחרי שמתרגלים אי אפשר לעזוב את זה :)
קודם כל צריך להוריד את תוכנית ההתקנה ל NUnit
פשוט תורידו את ההתקנה לחלונות ותריצו.
כעת ניצור פרוייקט חדש ונוסיף הפניה ל NUnit. שימו לב שבחרתי פרויקט מסוג class library, זה לא חובה אבל עדיף
כעת נבנה לנו מחלקה חדשה שתייצג ריבוע, הריבוע יודע להחזיר את השטח שלו וניתן לשנות לו את הגודל
public class Cube {
private int width;
private int height;
public Cube(int w, int h) {
if (w <= 0)
{
throw new ArgumentOutOfRangeException("width must be a positive integer");
}
if (h <= 0)
{
throw new ArgumentOutOfRangeException("height must be a positive integer");
}
this.width = w;
this.height = h;
}
public void Resize(int w, int h) {
if (w <= 0)
{
throw new ArgumentOutOfRangeException("width must be a positive integer");
}
if (h <= 0)
{
throw new ArgumentOutOfRangeException("height must be a positive integer");
}
this.width = w;
this.height = h;
} public int Size {
get { return width * height; }
}
}
כדי להשתמש ב NUnit עלינו להגדיר לה מה כאן בדיקות , לשם כך נוסיף את הסימון [Test] מעל כל פונקציה שאנו רוצים לבדוק, במקרה שלנו זה Resize ו Size
[Test]
public void Resize(int w, int h)
{
if (w <= 0)
{
throw new ArgumentOutOfRangeException("width must be a positive integer");
}
if (h <= 0)
{
throw new ArgumentOutOfRangeException("height must be a positive integer");
}
this.width = w;
this.height = h;
}
אני חייב להגיד שגם בצורת הבדיקות יש כל מיני גישות... וכמובן שאני אציג את זאת שאני חושב שהיא הכי טובה :)
כדי לבדוק את הריבוע שלי, או כל דבר אחר באפקליציה, אני אצור מחלקה שכל מטרתה הוא לבדיקות. עדיף אפילו ליצור פרוייקט נפרד באותו שם עם תוספת Test בסופו שכל מטרתו הוא לבדוק את הפרוייקט המקורי שלי. ניצור מחלקה נוספת בשם CuteTest.
במחלקה אני אצור מספר פונקציות כשכל אחת אמורה לבדוק משהו אחר בריבוע שלנו:
public class CuteTest {
public void GoodTest()
{
Cube c = new Cube(3, 4);
int s = c.Size;
Console.WriteLine("size : " + s); }
public void BadTest()
{
Cube c = new Cube(-2, 3);
int s = c.Size;
Console.WriteLine("size : " + s);
}
public void AnotherBadTest()
{
Cube c = new Cube(3, 6);
c.Resize(4, -2);
int s = c.Size;
Console.WriteLine("size : " + s);
}
public void ZeroTest()
{
Cube c = new Cube(0, 0);
int s = c.Size;
Console.WriteLine("size : " + s);
}
}
* שימו לב שחלק מהבדיקות אמורות להיכשל כי הכנסתי ערך שלילי...
*הגדרת המחלקה והפונקציות חייבת להיות public
כעת נגיד ל NUnit שאלו הבדיקות שלנו על ידי הוספת סימון [Test] לפני שם כל פונקציה
[Test]
public void ZeroTest()
{
Cube c = new Cube(0, 0);
int s = c.Size;
Console.WriteLine("size : " + s);
}
ונריץ את UNnit (בעת ההתקנה נוסף לנו פריט לתוכניות בתפריט ההתחלה)
ונטען את הפרוייקט שלנו
אנו רואים שהבדיקות שלנו מופיעות כבר. כעת רק נשאר ללחוץ על הרצה
ואנו רואים שרק בדיקה אחת עברה בהצלחה...
אבל אנחנו כן מרוצים מכך שהשאר לא עברו בהצלחה, ובשבילנו הבדיקות האלה כן מוצלחות כי ציפינו לשגיאה ואכן קיבלנו שגיאה... אז נגיד ל NUnit שהפונקציה אמורה לקבל שגיאה, ואז אם לא נקבל שגיאה אזי הבדיקה נכשלה, ואם כן נקבל שגיאה כצפוי אז הבדיקה נחשבת כמוצלחת
[Test]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void BadTest()
{
Cube c = new Cube(-2, 3);
int s = c.Size;
Console.WriteLine("size : " + s);
}
ונריץ שוב
NUnit מגיע גם שורת פקודה ולא רק עם GUI כך שניתן להריץ אותו באופן אוטומטי לאחר כל בניה של פרוייקט על ידי שינוי ההגדרות של בניית הפרוייקט בויז'ואל סטודיו.
יש ב NUnit עוד הרבה אפשרויות כמו למשל Assert שדומה למחלקה Debug שיש בדוט נט. אצלנו הפונקציות היו פשוטות ורק בדקתי שאכן הריבוע שלי מוכן לקבל ערכים חוקיים בלבד, אך איך לבדוק שהגודל שהוא מחזיר לי הוא אכן תקין?
לשם כך נשתמש ב Assert
[Test]
public void GoodTest()
{
Cube c = new Cube(3, 4);
int s = c.Size;
Assert.AreEqual(s, 3 * 4);
Console.WriteLine("size : " + s);
}
כעת אם התוצאה לא תהיה נכונה אזי הבדיקה תיכשל
אפשר להמשיך עוד הרבה אבל אני אשאיר לכם לגלות לבד את עולם הבדיקות האוטומטיות, העיקר שהתקנתם והתחלתם...
להורדת הפרוייקט
Technorati Tags:
NUnit,
UnitTesting
Be the first to rate this post
- Currently 0/5 Stars.
- 1
- 2
- 3
- 4
- 5
הפעם נדבר קצת על טרדים (Thread, נראה לי שקוראים לזה תהליכון בעברית, אבל אל תסמכו עלי בזה...)
יש כמה דברים בתיכנות שידע מעמיק הוא ממש חובה, אם יש כמה מחלקות שאני לא מכיר זאת לא בעיה בדרך כלל, חיפוש קטן בגוגל והכל בסדר, אבל אפליקציית MultiThreading לא כל כך לבנות, וכל פרט קטן עלול לשבש את כל האפליקציה, עדיף להשקיע קצת בהבנה ואולי יום אחד זה יועיל הרבה
אחד מהדברים שיכולים לשנות אפליקציה מקצה לקצה... אומנם הם בדרך כלל שימושיים באפליקציות שולחניות , אך גם בווב עשיתי בהם שימוש מדי פעם.
מה זה Thread? זה בעצם תהליך נוסף, מקביל, בתוכנית שלנו. בדרך כלל התוכנית רצה שורה שורה, מסיימת אחת ממשיכה לבאה, אבל מה קורה אם שורה זו פעולה ארוכה מאוד, ואני רוצה לשעשע את המשתמש בינתיים...? מה קורה אם בתוכנית יש הרבה פניות לאינטרנט, והסיבה שזה לוקח זמן זה לא בגלל שהמעבד עסוק אלא פשוט לשרת בצד השני של העולם לוקח זמן להגיב? למה לא לעשות דברים אחרים בינתיים? בשביל זה המציאו Threadים נוספים...
בואו נראה דוגמה קטנה
static void Main(string[] args)
{
Console.WriteLine("please wait...");
Thread t = new Thread(new ThreadStart(DoSomethingLong));
t.Start();
while (t.ThreadState != System.Threading.ThreadState.Stopped && t.ThreadState!= ThreadState.Aborted)
{
Console.WriteLine("still waiting..."+DateTime.Now);
Thread.Sleep(500);
}
Console.WriteLine("finished!");
}
private static void DoSomethingLong()
{
Thread.Sleep(5000);
}
אז מה יש לנו כאן... קודם כל אנו רואים שיש פונקציה שלוקח לה זמן לרוץ DoSomethingLong. בפונקציה הראשית אני יוצר אובייקט מסוג Thread, בקונסטרוקטור שלו אני מעביר את שם הפונקציה שאני רוצה שתרוץ. עד כאן שום דבר לא קרה, אך בשורה הבאה אנו נותנים לאובייקט פקודה לרוץ, ובאותו רגע נוצר בעצם תהליך חדש בתוכנית שלנו, שלא עוצר את התהליך הראשי- עובדה שאנו ממשיכים לשורה הבאה ומציגים למשתמש כל חצי שניה הודעה שעדיין אנו ממתינים שהפונקציה תסתיים, אם היינו עושים את זה ללא Thread נוסף אז לא היינו ממשיכים הלאה עד שהפונקציה היתה מסתיימת. השתמשתי בשיטה מסורבלת ולא נכונה כדי לבדוק מה מצב ה Thread הנוסף שלי- בדקתי אם הוא כבר עצר או נעצר כדי להחליט שהוא כבר סיים, אך יש שיטות יותר נכונות ויעילות שנראה תיכף.
*השתמשתי כאן בפונקציה הסטטית Thread.Sleep(5000) שגורמת לעצירה של התהליך הנוכחי למספר אלפיות השניה שאנו מעבירים לה.
בואו נתקן מעט את הפונקציה. לאחר הרצת ה Thread הנוסף, אני אמשיך לעשות מה שאני רוצה, ובסוף הפעולות שלי אני אמתין ל Thread הנוסף עד שיסתיים מבלי לרוץ בלולאה ומבלי לבדוק באופן ספציפי באיזה מצב הוא נמצא:
t.Join();
הפקודה בעצם עוצרת את Thread המרכזי שלנו, וממתינה ש t יסיים את כל מה שהוא צריך לעשות ורק אז נעבור לשורה הבאה
אם ארצה בכוח לעצור את ה Thread הנוסף ניתן להשתמש ב
t.Abort();
רק אזהרה קטנה - אל תעשו את זה!! אלא אם כן אתם חייבים לסיים בדחיפות את התוכנית ומוכנים לקחת את הסיכון...
הדבר עשוי לגרום ל"תוצאות בלתי צפויות" ולהשחתה של נתונים... בכל מקרה תשימו גם try catch מסביב לפקודה הזאת. יש דרכים יותר טובות להודיע לו שצריך לסיים בקרוב...
הדרך הטובה היא להשתמש במשתנה בוליאני, שאותה יבדוק ה Thread הנוסף שלנו לפני כל פעולה ארוכה או לופ שהוא הולך לבצע, ואם השתנה הערך, הוא פשוט יפסיק הכל ויחזור, וכך מהתוכנית הראשית שלנו, ברגע שנרצה לעצור את כל ה Threadים , נשנה להם את הערך של המשתנה הזה והם יבינו שצריך לעצור
שימו לב שניתן ליצור מחלקה חדשה, לתת לה נתונים ואז להריץ פונקציה ממנה בעזרת Thread אחר, וזה מה שנעשה עכשיו - נבנה מחלקה שמתחברת לאינטרנט, מורידה קובץ ושומרת אותו אצלנו במחשב. התוכנית הראשית תייצר אובייקטים מהמחלקה הזאת ,תיתן לכל אחת את הכותבת ממנה להוריד את הקובץ שלה, ותריץ אותה ב Thread נפרד
static void Main(string[] args)
{
string[] files ={
class="code_text">"http://www.someurl.com/myfile.pdf", "http://www.another.com/file.pdf","http://www.moreurl.com/morefiles.pdf"};
List<Thread> threads = new List<Thread>();
foreach (string url in files)
{
WebConnector wc = new WebConnector(url);
Thread t = new Thread(new ThreadStart(wc.Connect));//select the method to run
threads.Add(t);
t.Start();
}
foreach (Thread t in threads)
{
t.Join();
}
}
וזאת המחלקה שלנו שמבצעת את העבודה בפועל:
public class WebConnector
{
private string _url;
public WebConnector(string url)
{
this._url = url;
}
public void Connect()
{
try
{
WebClient wc = new WebClient();
string localFileName = Path.Combine(Directory.GetLogicalDrives()[0], Path.GetFileName(this._url));
wc.DownloadFile(this._url, localFileName);
}
catch { }
}
}
עכשיו בואו נניח שאנחנו רוצים לדעת בכל רגע מה קורה עם ה Threadים שלנו. ניתן ליצור פונקציה סטטית בתוכנית הראשית (או לא סטטית ולהעביר לכל Thread הפניה לתוכנית הראשית) ואז הם יוכלו לקרוא לפונקציה הזאת כדי להודיע מה קורה איתם. כמובן שיש לדאוג שהם לא ידרסו נתונים אחד של השני... יש עוד הרבה לדבר על MultiThreading אז נמשיך בחלק אחר
להורדת הקוד
Be the first to rate this post
- Currently 0/5 Stars.
- 1
- 2
- 3
- 4
- 5
יש פרוייקט קוד פתוח נחמד שמבצע את המשימה של שיכתוב נתיבים. נניח שיש לי בלוג, או רשימת מוצרים, כל קישור מן הסתם מכיל את שם הדף ועוד איזה 20 פרמטרים כמו מספר קטגוריה, מספר פריט וכו,. אני לא רוצה להראות כזה נתיב מעצבן עם כל הפרמטרים ששמתי לעצמי בתור מתכנת, אני רוצה שהנתיב יראה כמו שם רגיל של דף במערכת. למה? קודם כל מנועי חיפוש, מה לעשות שרוב החברות מנסות לטפס ברנקינג של גוגל...? אז מנועי חיפוש נותנים יותר ניקוד לדפים עם שם "רגיל" דבר שני זה לא אסטטי לפעמים (נחמד אה...) , לא באמת, לפעמים אני מעביר 7 פרמטרים לדף הבא, אני לא רוצה שהמשתמש יראה אותם. אז מה עושים? שיכתוב URL... אני לוקח את הפרמטרים שלי, בונה נתיב שנראה טוב עם סלשים בין הפרמטרים למשל, מגדיר חוקים איך לנתב את הפניות האלה ואז כל פניה כזאת מגיעה לדף המקורי שרציתי עם הפרמטרים שרציתי בצורה הרגילה (Request.Params)
בואו נראה. קודם כל נוריד את UrlRewriter.NET ושם נבחר כמובן בגירסה 2 לדוט נט ( אלא אם כן יש חדשה יותר כבר)
נפתח את הזיפ ונלך לתיקיית bin/release, ושם ניקח את הקובץ Intelligencia.UrlRewriter.dll ונשים אותו בתיקית bin של האתר שלנו (או Add Reference אם בא לכם)
כעת בזהירות ממש נלך ל Web.config ונוסיף כמה שורות
<configSections>
<section name="rewriter" requirePermission="false" type="Intelligencia.UrlRewriter.Configuration.RewriterConfigurationSectionHandler, Intelligencia.UrlRewriter"/>
</configSections>
ועוד חלק:
<system.web>
<httpModules>
<add name="UrlRewriter" type="Intelligencia.UrlRewriter.RewriterHttpModule, Intelligencia.UrlRewriter"/>
</httpModules>
ואחרון חביב והכי מעניין בשבילנו:
<rewriter>
<rewrite url="~/products/(\d+)/(\d+)/.+" to="~/products/Details.aspx?categoryid=$1&productid=$2"/>
</rewriter>
</configuration>
שימו לב... נחמד? כאן אנו בעצם מגדירים בעזרת Regex את הכתובת שאותה הכניס הלקוח ומה המיפוי האמיתי שלה
ומה הגדרתי פה? כל פניה שבהתחלה שלה כתוב products, אחר כך /, אחר כך יופיע מספר, אחר כך / אחר כך שוב מספר ו /, ואז איזשהו שם, להעביר לדף Details.aspx עם המספר הראשון בתור פרמטר בשם categoryid והמספר השני בשם productid. יותר מזה, אפילו אם הדף פיזית לא קיים במערכת, למיפוי שלנו זה לא משנה, הוא מתעלם מכל מה שכתוב אחרי ה / האחרון
נניח שהלקוח הקיש על לינק : http://www.myhost.com/products/2008/02/ProductDetails.aspx
השיכתוב שהכנסתי יעביר אותו לדף http://www.myhost.com/products/ProductDetails.aspx?categoryid=2008&productid=02
זה יכול לעזור לפעמים...
דרך אגב, יש אתר שנקרא http://tinyurl.com שמספק שירות דומה, אתם מכניסים איזה נתיב באורך כמה מאות תווים והוא מוציא לכם נתיב קצר שמפנה אליו בעצם, כשמישהו לוחץ על הלינק הם מפענחים אותו ומעבירים אותו ללינק המקורי,
להורדת פרוייקט דוגמה
Technorati Tags:
UrlRewriter
Be the first to rate this post
- Currently 0/5 Stars.
- 1
- 2
- 3
- 4
- 5
לפעמים אנו רוצים להפעיל אפליקציה אחרת מהתוכנית שלנו, למטרה זאת ניתן להשתמש במחלקה Process. המחלקה מקבלת את הנתיב לתוכנית שאנו רוצים להריץ, ופרמטרים שונים כגון
ProcessStartInfo psI = new ProcessStartInfo("myprog.exe");
Process mainForm = new Process();
psI.Arguments = "a=3";
psI.UseShellExecute = false;
psI.WindowStyle = ProcessWindowStyle.Normal;
mainForm.StartInfo = psI;
mainForm.Start();
הפרמטר Arguments יקבע איזה מידע יקבל התהליך מאיתנו כשהוא יופעל - כמו שמריצים תוכנה בדוס ונותנים פרמטרים לאחר שם התוכנית. אם אנו רוצים להעביר מספר פרמטרים לתהליך זוהי הדרך המהירה והקלה.
הפרמטר CreateNoWindow מציין האם אנו רוצים שהתהליך ירוץ עם חלון או ללא חלון
פרמטר נוסף שקשור הוא WindowStyle שבעצם אומר באיזה מצב להתחיל את החלון - מוגדל/מוקטן וכו.
את הפרמטר UseShellExecute עדיף לשים על false, אלא אם כן אתם מתכוונים להריץ מסמך כלשהו או קובץ שאינו exe בעצמו אלא משתמש בקובץ אחר כדי לפתוח אותו (כמו קובץ pdf וכדומה).
אנו יכולים גם לקרוא/לכתוב לקלט/פלט של התהליך הזה על ידי הפרמטר RedirectStandardOutput , שאומר בעצם שהקובץ יקבל נתונים מאיפה שאנחנו נגיד לא ולא מהקונסול לדוגמא, שזו ברירת המחדל של כל תוכנית קונסול )Console.ReadLine לדוגמה, יקרא את המידע ממה שאנחנו נכתוב לו, ולא ממה שכותבים במסך השחור הקטן שנפתח שם...) ואז לאחר שהתחלנו את התהליך יש לנו בעצם את הקלט/פלט שלו וניתן להסיט אותם על ידי הפקודה mainForm.StandardOutput/ mainForm.StandardInput
StreamWriter sw= mainForm.StandardInput;
ולסיום, אם אנו רוצים לסגור את התהליך בעצמנו, ולא לחכות שהמשתמש יסגור אותו או שהוא יסגר בסיום המלאכה, למשל אם הרצתי חלון חדש ואני רוצה לסגור אותו מהתוכנית הראשית, נעשה זאת בצורה פשוטה ביותר:
mainForm.Kill();
עדיף להקיף ב try catch ליתר ביטחון...
לסיכום, לאחר כל הנתונים המשעממים, אם אנחנו רק רוצים להריץ דפדפן לאיזה דף באינטרט נריץ את הפקודה הבאה וזהו:
Process.Start("IExplore.exe", "www.google.com/search/q=life");
Be the first to rate this post
- Currently 0/5 Stars.
- 1
- 2
- 3
- 4
- 5