หลังจากพักสำหรับการเขียน Blog ของการเรียน Python มานาน เพื่อไปเตรียมสอบ MCSD ตอนนี้ได้เวลามาอัพ Blog ต่อแล้วครับ สำหรับใน Week 4 เน้นไป 4 เรื่อง ดังนี้
- การทดสอบ(Testing)
- การติดตามพฤติกรรมของโปรแกรม (Debug)
- การจัดการกับข้อผิดพลาด(Exception)
- การยืนยัน(Assertions)
การทดสอบ(Testing)
- ที่มาของ ฺBug มันมีที่มาจากแมลงจริงๆ ที่ดันเข้าไปติดในคอมพิวเตอร์สมัยก่อนครับ ตอนแรกDev เองนึกว่า Code ผิด แต่ป่าวมีแมลงไปติดที่วงจรซะงั้น
- ในเรื่องนี้ผู้สอน เค้าได้นำเสนอแนวคิด Defensive Programming ครับ
- เมื่อไหร่ถึง Test หละ
- ทำงานเสร็จระดับนึง
- Code Complile ผ่าน ไม่มี Syntax Error
- มี Test Case คือ ชุดทดสอบที่มีการกำหนดข้อมูลนำเข้า(Input) และผลลัพธ์ที่ได้(Exprected Result) แล้ว
- ทุกอย่างมันอยู่ในมือเราแล้ว ทำอย่างไรให้มัน Testing และ Debug ง่ายหละ
- Decomposition - แบ่งให้มันเป็นชิ้นเล็กที่ทุกสุดเท่าที่จะทำได้สิ ^__^
- Document Constraints - ผมมองว่าเป็น Test Case
- Document Assumptions - ตอนนี้ทำไมถึงใช้ Algorithm นี้หละ แต่ปัญหาหลักที่เจอ คือ แม้แต่พระเจ้า ท่านไม่รู้หรอกว่า สิ่งที่เราทำไม เมื่อเดือนที่แล้ว เพราะอะไร จดไว้บาก็ดีครับ
- Class of Tests
- Unit Testing - Test ในจุดที่เล็กที่สุด ยิ่งแรกแบ่งส่วนของงานได้เล็กลงเท่าไหร่ เราสามารถเจาะเข้าไปได้ ให้มันได้ละเอียดมากขึ้นครับ
- Regression Testing - Test ซ้ำเพิ่มจากตัว Unit Test เพื่อให้มั่นใจว่า แก้ ฺBug อันใหม่ไปแล้ว Bug เก่ามันไม่มาหลอกหลอน
- Integration Testing - Test โดยเอาหลายส่วนมาทำงานร่วมกัน
- Testing Approach
- Black Box Testing - โยน Input เข้าไป ดูผลลัพธ์ที่ออกมา
- จุดเด่น - เราสามารถเอา Test Case มา Reused ได้ แม้ว่าไส้ข้างใน มีการปรับแก้ไข และป้องกัน Dev ที่ทำลำเอียง
- จุดด้อย - คน Test เข้าใจสิ่งที่ทำพอสมควรเลย อาจจะเป็น Business Domain เพื่อกำหนด Test Case ได้อย่างเหมาะสม
- การกำหนด Test Case สำหรับ Black Box คือ เรื่อง Boundary Condition เอาง่ายพวกขอบเขต เช่น
- Min/Max
- List เป็น null
- List ไม่มีข้อมูล เป็นต้น
- Glass Box Testing - เรียกยังไงดี บอกว่ามันดีจากภายใน เพราะ การ Test เราต้อง Design Test Cases มาจาก Code ที่เขียน
- จุดที่ Code ที่ต้องสนใจมีหลายจุด เช่น
- เงื่อนไข - IF / Switch
- การทำซ้ำ - For Loop / While Loop เป็นต้น
- จุดเด่น - ทดสอบได้ลึก และครอบคลุมกว่า Black Box สิ่งที่บอกว่าครบคลุมนั้น เราต้อง Test ทุกทางที่เป็นไปได้ เช่น IF - ELSE มี 2 สองให้ ทดสอบแล้ว
- จุดด้อย - ใช้เวลานาน อาจจะมี Test หลุด ไม่ครบทุกเส้นทางได้
- การกำหนด Test Case อย่าลืมเรื่อง Boundary Condition
- จุดที่ Code ที่ต้องสนใจมีหลายจุด เช่น
- Black Box Testing - โยน Input เข้าไป ดูผลลัพธ์ที่ออกมา
การติดตามพฤติกรรมของโปรแกรม (Debug)
- Runtime Bug
- Overt(บึ้มไปเลย) กับ Covert(ทำงานได้ ผลลัพธ์ผิด)
- Persistent(มี Step การบึ้มที่แน่นนอน) กับ Intermittent(อยู่ๆ ก็พัง)
- นิยามของคำว่า Debug = การติดตามพฤติกรรมของโปรแกรม อันนี้ผมเขียนขึ้นมาเองนะ จริงๆ การ Debug มันเหมือนว่าตัว Dev เอากำลังสวมบทบาทเป็นคุณหมอวินิจฉัย และผ่าตัดเอาสิ่งที่ผิดปกติออกจากโปรแกรมที่เขียนขึ้นครับ
- แนวทาง Debug เพื่อจับ Bug
- แบบง่าย Runtime มันด่าเราได้ ว่า Error อะไร อันนี้ดูได้ง่าย
- แบบยาก มันทำงานได้ แต่ค่าผิด L0gical Error
- ต้องรู้ว่า Code ของเราทำอะไร
- ถ้ามันไม่บอก Print มันออกมา
- พยายามตัดปัญหาได้ได้แบบ Bi-Section Search ตัดขอบเขตของปัญหาลงเรื่อยๆ
- Note: นำแนวคิดของ Defense Programming มาใช้ แต่ถ้าเหนื่อยไปก็พักบ้าง บางครั้งเราอาจจะเจอปัญหาแบบเส้นผมบังภูเขานะ
- ในเรื่องนี้ผมตลกกับคนสอนนะ แกบอกว่า Dev มักพูดคุณกับตุ๊กตาเป็ด พอกลับมามองดุตัวเอง เอ๊ะ เราพูด หรือป่าวนะ 5555
การจัดการกับข้อผิดพลาด(Exception)
- ทำไมต้องมี Exception ?
- เราอยากรู้ว่าโปรแกรม มันบอกอะไรไหม บึ้มเงียบๆ (Fail silently) มันไม่ค่อยดีนะ แล้วเราสามารถช่วยจัดการไหม เช่น กรอกเลขผิด ปล่อยยาวไปจนผิดไปอีกหลาย Process ทั้งที่จริงๆ เราดักได้ตั้งแต่แรก
- Exception มีอะไรบ้าง เช่น
- Exception มีอีกหลายแบบนะ เช่น ZeroDivisionError, IOError, AttributeError เป็นต้น
- Pattern ของการดัก Exception ตาม Code เลยครับ
data = [] file_name = input("Provide a name of a file of data ") try: #ลอง Execute Code ที่เลี่ยงเกิดข้อผิดพลาด ในที่นี้ลองเปิดไฟล์ บางครั้งไฟล์อาจจะถูกใช้ หรือ ไม่มีอยู่จริง fh = open(file_name, 'r') except IOError: #ดัก Exception ที่อาจจะเกิด print('cannot open', file_name) else: #ถ้าไม่มี Error ก็ทำงานต่อไป for new in fh: if new != '\n': addIt = new[:-1].split(',') #remove trailing \n data.append(addIt) finally: #ไม่ว่าจะ Error หรือทำสำเร็จ ก็ต้องทำ Section นี้เสมอ fh.close() # close file even if fail
- try - ลอง Execute Code ที่เลี่ยงเกิดข้อผิดพลาด
- except - ดัก Exception ที่อาจจะเกิด ถ้ามีหลายตัวเรียงความสำคัญจากเล็ก ไป ใหญ่ เช่น
#some code here except ValueError: print("Could not convert to a number.”) except ZeroDivisionError: print("Can't divide by zero”) except: print("Something went very wrong.”) #some code here
- else - ถ้าไม่มี Error ก็ทำงานต่อไป
- finally - ไม่ว่าจะ Error หรือทำสำเร็จ ก็ต้องทำ Section นี้เสมอ
- อยากโยน Exception เอา ทำได้นะ Pattern ดังนี้เลย
การยืนยัน(Assertions)
- มันต่างกับ Exception อย่างไร?
- Assertions ป้องกันก่อน Execute
- Exception แสดงข้อผิดพลาดหลัง Execute
- ตัว Assertion มันช่วยเรื่อง Defensive Programming ด้วยนะ ในสถานการณ์นี้ ถ้ามีอะไรผิดพลาด มีการ Raise Exception ออกมา
- ก่อนเข้า Pre - Process : check inputs
- ระหว่าง Process : ให้หยุดการทำงาน(Execution halts)ถ้าค่ามันผิดแปลกไป
- หลังจบจาก Post-Process : check outputs ก่อนคืนค่าออกมา
- ตัวอย่าง Assertions
def avg(grades): assert not len(grades) == 0, #no grades data //ตรวจว่า input มีข้อมูลที่ส่งเข้ามาจริงไหม ? return sum(grades)/len(grades)
- ตัว Assertion เน้นเอาไปวางใน Unit Test ด้วย
Blog ยาวเลยครับ สำหรับเรื่องนี้ สะสมที่ดองมานานหลาย Week ครับ และสุดท้ายของแถม Quote อันนึง
"Source Code always tells the truth"
PingkungA
Discover more from naiwaen@DebuggingSoft
Subscribe to get the latest posts sent to your email.