[MITx: 6.00.1x] Introduction to Computer Science and Programming Using Python (Week 4)

หลังจากพักสำหรับการเขียน Blog ของการเรียน Python มานาน เพื่อไปเตรียมสอบ MCSD ตอนนี้ได้เวลามาอัพ Blog ต่อแล้วครับ สำหรับใน Week 4 เน้นไป 5 เรื่อง ดังนี้

การทดสอบ(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

การติดตามพฤติกรรมของโปรแกรม (Debug)

🐞Runtime Bug

  • Overt(บึ้มไปเลย) กับ Covert(ทำงานได้ ผลลัพธ์ผิด)
  • Persistent(มี Step การบึ้มที่แน่นนอน) กับ Intermittent(อยู่ๆ ก็พัง)

นิยามของคำว่า Debug  = การติดตามพฤติกรรมของโปรแกรม อันนี้ผมเขียนขึ้นมาเองนะ จริงๆ การ Debug มันเหมือนว่าตัว Dev เอากำลังสวมบทบาทเป็นคุณหมอวินิจฉัย และผ่าตัดเอาสิ่งที่ผิดปกติออกจากโปรแกรมที่เขียนขึ้นครับ

🐞 แนวทาง Debug เพื่อจับ Bug

  • แบบง่าย Runtime มันด่าเราได้ ว่า Error อะไร อันนี้ดูได้ง่าย
  • แบบยาก มันทำงานได้ แต่ค่าผิด L0gical Error
    • ต้องรู้ว่า Code ของเราทำอะไร
    • ถ้ามันไม่บอก Print มันออกมา
    • พยายามตัดปัญหาได้ได้แบบ Bi-Section Search ตัดขอบเขตของปัญหาลงเรื่อยๆ
Defense Programming

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.