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

หลังจากพักสำหรับการเขียน 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

การติดตามพฤติกรรมของโปรแกรม (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.