บทนำสู่การทดสอบ

เผยแพร่แล้ว: 2021-06-16

ยินดีต้อนรับสู่บล็อกชุดใหม่ของเราเกี่ยวกับ ทุกสิ่งที่เกี่ยวข้องกับการทดสอบ หวังว่า บล็อกโพสต์เหล่านี้จะทำให้คุณมีความคิดทั่วไปว่าเราเขียนมันที่ Mediatoolkit ได้อย่างไร และทำไม ก่อนที่เราจะลงลึกในตัวอย่าง เรามาเริ่มด้วยคำจำกัดความพื้นฐาน แนวคิด และประโยชน์ของการทดสอบการเขียนกันก่อน

มีการทดสอบประเภทใดบ้าง?

การทดสอบทั้งหมดไม่ได้ถูกสร้างขึ้นมาเท่ากัน มีการทดสอบหลายประเภทเพื่อจุดประสงค์ที่แตกต่างกัน แม้ว่าโพสต์นี้จะเน้นที่ การทดสอบหน่วย เป็นหลัก เราควรตระหนักถึงความแตกต่างและเข้าใจประโยชน์ของแต่ละรายการ

1. การทดสอบด้วยตนเอง

การทดสอบด้วยตนเองเกี่ยวข้องกับการทดสอบที่ดำเนินการโดยผู้ทดสอบด้วยตนเอง สิ่งเหล่านี้มักจะทำโดย Quality Assurance หรือโดยนักพัฒนาเอง QA ควรถูกมองว่าเป็นแนวป้องกันสุดท้าย ไม่ใช่ผู้ทดสอบแอปพลิเคชันหลัก (QA ทำการทดสอบด้วยตนเองเนื่องจากวิธีหลักสำหรับการทดสอบ UI เป็นข้อยกเว้นที่ถูกต้องสำหรับกรณีนี้)

นักพัฒนามักจะเขียนการทดสอบด้วยตนเองเมื่อต้องการ เรียกใช้บางสิ่งในพื้นที่ y และ ดูว่าระบบทำงานอย่างไร โดยไม่ต้องกดโค้ดใดๆ ไปที่เฟสการจัดเตรียม หรือพระเจ้าช่วยคุณประหยัดในการผลิต อย่างไรก็ตาม เป้าหมายของการทดสอบเหล่านี้ไม่ใช่ความทนทานของโค้ด บทบาทเดียวของการทดสอบเหล่านี้คือการตรวจ จับจุดบกพร่องและรับรองคุณภาพของผลิตภัณฑ์

2. การทดสอบบูรณาการ

ระบบประกอบด้วยองค์ประกอบหลายอย่าง หรืออย่างน้อยก็ควรจะเป็น การทดสอบการรวมจะ ตรวจสอบ API สาธารณะ ของส่วนประกอบเหล่านี้ ไม่ใช่แค่ REST API แต่ API ที่เปิดเผยต่อสาธารณะ เช่น การสื่อสารหัวข้อ Kafka ของคุณ

หากองค์ประกอบ "พูดว่า" จะแสดงข้อความในหัวข้อในรูปแบบใดรูปแบบหนึ่ง นั่นคือ API สาธารณะหรือสัญญา การทดสอบการรวมเป็นการตรวจสอบว่าส่วนประกอบที่พัฒนาขึ้นเองหลายรายการสามารถสื่อสารได้หรือไม่ และสัญญาของพวกเขาตรงกันเป็นกลุ่มหรือไม่

หากคุณทดสอบส่วนประกอบเหล่านี้ทีละส่วน พฤติกรรมของส่วนประกอบอาจทำงานได้อย่างถูกต้อง แต่เมื่อคุณพยายามเชื่อมโยงส่วนประกอบเหล่านี้ในกลุ่มที่มากขึ้น คุณจะ พบว่าสัญญาของส่วนประกอบนั้นแตกต่างกัน

3. การทดสอบแบบ end-to-end (E2E)

การทดสอบแบบ end-to-end ช่วยให้มั่นใจได้ถึงการไหลของประสบการณ์ผู้ใช้ของผลิตภัณฑ์ขั้นสุดท้าย ความซับซ้อนของระบบในปัจจุบันนั้นยากที่จะครอบคลุมด้วยการทดสอบ หลายระบบพึ่งพาซึ่งกันและกัน และการทดสอบ E2E ช่วยให้มั่นใจว่า ผลิตภัณฑ์ทำในสิ่งที่คาดหวัง QA จะตรวจสอบความถูกต้องของผลิตภัณฑ์ของคุณโดยทำตามขั้นตอนของผู้ใช้ปลายทางและตรวจสอบว่าระบบทั้งหมดทำงานตามที่คาดไว้หรือไม่

4. การทดสอบหน่วย

การทดสอบหน่วยเป็น หัวใจสำคัญของซอฟต์แวร์ที่เชื่อถือ ได้ พวกเขาสร้างพื้นฐานสำหรับการทดสอบอื่นๆ พวกเขาทดสอบแต่ละหน่วย

นักพัฒนาอาจเข้าใจผิดคำจำกัดความของหน่วยด้วยวิธีเดียว การทดสอบหน่วยควรทดสอบพฤติกรรมของส่วนประกอบที่อาจประกอบด้วยหลายคลาสที่แตกต่างกัน ต้องทดสอบวิธีการสาธารณะที่ส่วนประกอบอื่น ๆ สามารถเข้าถึงได้ อย่าทดสอบวิธีการหรือคลาสที่ได้รับการป้องกัน เป็นรายละเอียดการใช้งาน ไม่ใช่ส่วนหนึ่งของ API สาธารณะของคุณ

ทำไมเราต้องทดสอบด้วย?

หากเราเขียนการทดสอบที่ดีและเขียนบ่อยๆ เราก็สามารถรับประกันคุณภาพของผลิตภัณฑ์ของเราได้ก่อนที่แสงจะมาถึง

เมื่อระบบของเรามีอายุมากขึ้น ประโยชน์ของการทดสอบก็ชัดเจนขึ้นเรื่อยๆ สภาพแวดล้อมของเรามีความน่าเชื่อถือ ช่วยประหยัดเวลาในการพัฒนาและดึงผมจำนวนมากเมื่อมีสิ่งผิดปกติเกิดขึ้นอย่างหลีกเลี่ยงไม่ได้ เพื่อนร่วมงานของคุณจะรู้สึกขอบคุณเมื่อได้ดูการทดสอบของคุณและ ทำความเข้าใจว่าโค้ดของคุณควรและไม่ควรทำอะไรโดยไม่ต้องดำเนินการด้วยตนเอง

รหัสที่แข็งแกร่ง

ก่อนที่เราจะพูดถึง “รหัสที่แข็งแกร่ง” เราควรนิยามมันอย่างไร? อะไรทำให้โค้ดแข็งแกร่ง นั่นหมายความว่าเราควรตั้งโปรแกรมป้องกันและคิดว่านักพัฒนารายอื่นอาจใช้โค้ดของเราในทางที่ผิดได้อย่างไร

รหัสที่แข็งแกร่งนั้นเรียบง่ายและสะอาดอยู่เสมอ ความเรียบง่ายนั้นแข็งแกร่ง ความซับซ้อนนั้นเปราะบาง เราควรจัดการกับอินพุตที่ไม่ถูกต้อง แต่นั่นไม่ได้หมายความว่าเราจำเป็นต้องตั้งโปรแกรมป้องกันและไม่ไว้วางใจทีมของเรา

กฎของกัลล์ : ระบบที่ซับซ้อนที่ใช้งานได้นั้นมักจะพบว่ามีวิวัฒนาการมาจากระบบธรรมดาที่ใช้งานได้จริง

ข้อเสนอผกผันก็ดูเหมือนจะเป็นจริงเช่นกัน: ระบบที่ซับซ้อนที่ออกแบบมาตั้งแต่เริ่มต้นจะไม่ทำงานและไม่สามารถทำงานได้ คุณต้องเริ่มต้นใหม่โดยเริ่มจากระบบที่ใช้งานง่าย

ปรับโครงสร้างใหม่ได้อย่างปลอดภัย

เมื่อรหัสของคุณครอบคลุมโดยการทดสอบ คุณจะไม่ต้องกลัวการเปลี่ยนรหัสที่มีอยู่อีกต่อไป หลังจากการเปลี่ยนแปลงแต่ละครั้ง คุณสามารถทำการทดสอบและตรวจดูให้แน่ใจว่าไม่ได้ทำสิ่งใดเสียหาย เมื่อคุณมีการทดสอบ คุณไม่จำเป็นต้องตั้งโปรแกรมป้องกัน

การปรับโครงสร้างใหม่โดยไม่มีการทดสอบกำลังดำเนินไปในทางลาดที่ลื่นซึ่งจะจบลงในคืนที่นอนไม่หลับและทำงานในวันอาทิตย์ หัวข้อนี้กว้างเกินไปที่จะกล่าวถึงในที่นี้ และสมควรได้รับการโพสต์ในบล็อกของตัวเองในอนาคต

เราจะไม่เขียนแบบทดสอบได้อย่างไร?

การ รู้ว่าจะไม่เขียนการทดสอบหน่วย เป็นสิ่งสำคัญพอๆ กัน เนื่องจากเป็นวิธีการเขียน

  • การเขียนการทดสอบที่พิมพ์ผลลัพธ์ของการเรียกใช้เมธอดไม่ใช่การทดสอบเนื่องจาก เราไม่ได้ตรวจสอบผลลัพธ์ที่ต้องการ
  • หากการทดสอบของคุณอ่านข้อมูลจากไฟล์ในโฟลเดอร์เอกสาร แสดง ว่าไม่ใช่การทดสอบหน่วยจริง เนื่องจากการทดสอบไม่ควรขึ้นอยู่กับสภาพแวดล้อม
  • นักพัฒนาซอฟต์แวร์ทุก คนควรตรวจสอบโค้ดของคุณและทำการทดสอบได้สำเร็จโดยไม่ต้องดำเนินการใดๆ
  • การทดสอบทุกหน่วยควรเป็น อิสระจากการทดสอบอื่นๆ นั่นก็หมายความว่าลำดับการดำเนินการของการทดสอบของคุณก็ไม่สำคัญเช่นกัน
  • การเรียกใช้การทดสอบหลายครั้งควร จบลงด้วยผลลัพธ์เดียวกัน เสมอ หากเราไม่เปลี่ยนรหัสใดๆ
  • การทดสอบหน่วยควรทดสอบพฤติกรรม ไม่ใช่การเรียกใช้เมธอดแต่ละรายการ ไม่ใช่ว่าทุกคลาสและทุกวิธีจะต้องมีการทดสอบ

พฤติกรรม คือสิ่งที่สร้างมูลค่าที่แท้จริงที่ผู้ใช้ระบบของคุณต้องการ ผู้ใช้ของคุณต้องรู้ว่า productFactory.create() สร้างอ็อบเจ็กต์เดียวกันเมื่อถูกเรียกสองครั้งหรือว่าที่เก็บของคุณถูกเรียกด้วยพารามิเตอร์บางอย่างหรือไม่? อาจไม่ใช่ แต่นักพัฒนาหลายคนยังคงเขียนการทดสอบประเภทนี้อย่างแน่นอน

หากการทดสอบของคุณเป็นแบบนั้น แสดงว่าการทดสอบนั้นควบคู่ไปกับการใช้งานของคุณอย่างแน่นหนา แต่ละครั้งที่คุณต้องการเปลี่ยนรายละเอียดการนำไปใช้ คุณจะต้องอัปเดตการทดสอบ แม้ว่าลักษณะการทำงานจะเหมือนกันก็ตาม การทดสอบของคุณควร เปลี่ยนเมื่อพฤติกรรมเปลี่ยนไปเท่านั้น ไม่ใช่รายละเอียดการนำไปใช้ กล่าวคือ ทดสอบ ว่า โค้ดของคุณทำอะไร ไม่ใช่ ว่า มันทำงานอย่างไร

เราจะเขียนแบบทดสอบได้อย่างไร?

การทดสอบของเราต้อง เป็นไปตามแนวทางปฏิบัติของโค้ดที่ดีที่สุด ซึ่งต้อง ไม่ขึ้นกับสภาพแวดล้อม และ ต้องดำเนินการอย่างรวดเร็ว

สิ่งสำคัญคือต้องรักษาเวลาในการดำเนินการทดสอบให้สั้นที่สุด การทดสอบแต่ละครั้งไม่ควรเกิน สองสามมิลลิวินาที เมื่อการทดสอบใช้เวลานานเกินไปในการดำเนินการ ผู้คนมักจะข้ามการทดสอบเหล่านี้และเพียงแค่พึ่งพาเซิร์ฟเวอร์ CI ของตน เช่น Jenkins เพื่อสร้างความยุ่งยากเมื่อไม่สามารถสร้างโปรแกรมเรียกทำงานการปรับใช้

การทดสอบแต่ละครั้งประกอบด้วย 3 ส่วน 'A' (รูปแบบ AAA) :

  1. จัด
  2. กระทำ
  3. ยืนยัน

1. จัด

ในส่วนการจัดเตรียมการทดสอบ เราตรวจสอบให้แน่ใจว่า ระบบของเราอยู่ในสถานะเฉพาะก่อนที่จะเรียกใช้พฤติกรรมที่เราต้องการทดสอบ 'ระบบ' อาจเป็นวัตถุที่เราจำเป็นต้องตั้งค่าในลักษณะเฉพาะเพื่อสร้างพฤติกรรม การสร้างไฟล์ชั่วคราวหรือสิ่งของในลักษณะนั้น

รหัสในส่วนนี้มักจะ ใหญ่กว่าอีกสองรหัสรวมกัน

รูปแบบการออกแบบหนึ่งที่ควรพิสูจน์ว่ามีประโยชน์อย่างยิ่งในการทำให้ส่วนนี้มีขนาดเล็กคือ Object Mother รูปแบบการออกแบบนี้คล้ายกับ Factory มาก แต่มีวิธีการเฉพาะมากกว่าที่สร้างวัตถุที่กำหนดค่าไว้ล่วงหน้าสำหรับคุณ แม้ว่า Factory มาตรฐานจะมีวิธีการเช่น createCar(carDescription) แต่ ObjectMother จะมีวิธีการอย่าง createRedFerrari() , createBlackTesla() หรือ createBrokenYugo()

2. พระราชบัญญัติ

ส่วนนี้ของการทดสอบของคุณต้องมี หนึ่งบรรทัด บรรทัดนี้ดำเนินการพฤติกรรมภายใต้การทดสอบ หากคุณพบว่าตัวเองเขียนมากกว่าหนึ่งบรรทัดสำหรับส่วนนี้ แสดงว่าคุณอาจ ไม่มีข้อสรุปที่ถูกต้องเกี่ยวกับพฤติกรรมของคุณ ลูกค้าของคุณไม่ควรเรียกวิธีการหลายวิธีของวัตถุของคุณในลำดับเฉพาะ เหตุใดจึงต้องทำการทดสอบของคุณ

บรรทัดนี้เป็นเมธอดที่เราต้องการทดสอบ หากวิธีนี้ส่งกลับผลลัพธ์ คุณควรเก็บค่านั้นไว้ในตัวแปรเพื่อตรวจสอบว่าเป็นค่าที่คาดหวังในขั้นตอนยืนยันหรือไม่

3. ยืนยัน

หลังจากที่เราได้เตรียมระบบในส่วนการจัดการและดำเนินการภายใต้การทดสอบในส่วนพระราชบัญญัติแล้ว เราจำเป็นต้องตรวจสอบผลลัพธ์ของการดำเนินการ เรามักจะตรวจสอบผลลัพธ์ของวิธีการที่นี่ แต่บางครั้ง วิธีการของเราไม่คืนค่า แต่ยังคงสร้างผลข้างเคียง หากโค้ดของเราถูกคาดหวังให้เปลี่ยนสถานะของอ็อบเจ็กต์ สร้างไฟล์ หรือลบบางสิ่งออกจาก List เราควรตรวจสอบว่าโค้ดนั้นทำอย่างนั้นจริงหรือไม่

สตับส์ vs ม็อคส์

นักพัฒนาส่วนใหญ่ใช้คำว่า mock และ stub แทนกันได้ แต่มีความแตกต่างกัน

ต้นขั้วไม่สามารถล้มเหลวในการทดสอบได้ หุ่นจำลองสามารถ

สตับเป็น แบบ state-based ซึ่งจะคืนค่าแบบฮาร์ดโค้ด “ฉันได้ผลลัพธ์อะไร”

การเยาะเย้ยเป็น ไปตามพฤติกรรม คุณใช้สิ่งเหล่านี้เพื่อตรวจสอบว่าพฤติกรรมของคุณผ่านมันไปอย่างไร “ฉันได้ผลลัพธ์อย่างไร”

หากการทดสอบหน่วยของคุณเต็มไปด้วยภาพจำลอง การทดสอบของคุณจะเปราะบางมาก หมายความว่าทุกครั้งที่คุณเปลี่ยนรายละเอียดการใช้งานรายการใดรายการหนึ่ง คุณต้องอัปเดตการเรียกจำลองทั้งหมด

ทดสอบเพียงสิ่งเดียวเท่านั้น

เราต้องสามารถ แยกพฤติกรรมหนึ่งๆ และพิสูจน์ว่ามันใช้ได้ผล หากพฤติกรรมนั้นควรทำงานแตกต่างกันด้วยอินพุตที่ต่างกัน เราจำเป็นต้องเขียนการทดสอบใหม่สำหรับแต่ละพฤติกรรมเหล่านั้น เป็นเรื่องยากที่จะรู้ว่าเหตุใดการทดสอบของเราจึงล้มเหลว หากเรามีการทดสอบขนาดใหญ่ที่ทดสอบหลายสิ่งพร้อมกัน ยิ่งกว่านั้น การลบคุณสมบัติที่เราไม่ต้องการอีกต่อไปนั้นยากขึ้น และดูว่าคุณสมบัติใดที่เราใช้งานไม่ได้เมื่อเราเพิ่มโค้ดใหม่

เป็นเรื่องปกติที่จะมีการยืนยันหลายครั้งในส่วนสุดท้ายของการทดสอบของคุณ ตราบใดที่คุณไม่จำเป็นต้องเรียกพฤติกรรมภายใต้การทดสอบหลายครั้ง หากเป็นกรณีนี้ จะเป็นการยากที่จะระบุพฤติกรรมที่ผิดพลาดและแก้ไข

เมื่อเรายืนยันพฤติกรรมหลายอย่างในการทดสอบครั้งเดียว เราจะไม่เห็นภาพที่ชัดเจนว่าสิ่งใดที่ไม่ทำงาน เนื่องจากการทดสอบจะรายงานเฉพาะความล้มเหลวครั้งแรกและส่วนที่เหลือจะถูกข้ามไป ยากขึ้นมากที่จะเข้าใจว่าการเปลี่ยนแปลงใดที่จำเป็นและมีกี่สิ่งที่ไม่ได้ผลตามที่คาดไว้

การทดสอบการตั้งชื่อ

สิ่งหนึ่งที่แยกการทดสอบที่ดีออกจากการทดสอบที่ยอดเยี่ยมคือชื่อการทดสอบ การทดสอบไม่ควรแค่บอกเราว่าพวกเขาทำอะไร แต่ทำเมื่อใด

มีรูปแบบการตั้งชื่อที่ดีมากมายที่คุณสามารถใช้ได้ ดังนั้นให้เลือกรูปแบบที่คุณคิดว่าสื่อความหมายได้ดีที่สุดและปฏิบัติตาม ต่อไปนี้คือตัวอย่างบางส่วนของชื่อทดสอบที่ยอดเยี่ยม:

  • RegisterServiceShould.createNewAccountWhenEmailIsNotTaken
  • RegisterServiceTest. whenEmailIsFree_createNewAccount
  • RegisterServiceTest.if_freeEmail_ when_userCreatesAccount_then_create

เมื่อคุณเขียนการทดสอบหน่วย การ สื่อสารสิ่งที่คุณกำลังทดสอบมี ความสำคัญมากกว่าการปฏิบัติตามแนวทางการตั้งชื่อวิธีการที่ดีที่สุด ตัวอย่างเช่น ใน Java เราใช้ camelCase เมื่อเขียนเมธอด แต่ควรใช้ขีดล่าง (_) เพื่อแยกสถานะออกจากการดำเนินการในชื่อทดสอบของคุณ

การทดสอบที่สะอาด

การทดสอบที่คุณเขียนควรเป็นไปตามแนวทางปฏิบัติที่สะอาดหมดจดทั้งหมดที่คุณใช้กับโค้ดของคุณ การทดสอบไม่ใช่พลเมืองชั้นสอง และคุณต้องใช้ความระมัดระวังในระดับเดียวกับที่คุณทำกับรหัสที่เหลือเพื่อให้สามารถอ่านได้

คำจำกัดความของการทำซ้ำโค้ดในการทดสอบมีความสำคัญมาก หลักการ DRY (อย่าทำซ้ำตัวเอง) ใช้กับการแยกพฤติกรรมที่เปลี่ยนแปลงด้วยเหตุผลเดียวกัน การทดสอบเปลี่ยนไปด้วยเหตุผลที่แตกต่างกัน ดังนั้นควรแยกสิ่งต่าง ๆ ออกจากการทดสอบของคุณหากการทดสอบไม่ได้เปลี่ยนแปลงไปด้วยเหตุผลเดียวกัน สปอยล์เตือนก็มักจะไม่

if คำสั่งไม่อยู่ในการทดสอบ คำสั่ง if บอกเราว่าการทดสอบของเราทำอย่างน้อยสองสิ่งที่แตกต่างกัน และเราจะดีกว่าถ้าเราเขียนการทดสอบของเราเป็นการทดสอบที่แตกต่างกันสองแบบ เมื่อตั้งชื่ออย่างถูกต้อง จะเข้าใจได้ง่ายขึ้นว่าการทดสอบทำอะไร และพฤติกรรมที่แตกต่างกันทั้งหมดมีอะไรบ้าง

เราควรเขียนแบบทดสอบเมื่อใด

ตามหลักการของ TDD เราควรเขียนการทดสอบ ก่อนที่จะเขียนโค้ดใหม่

เมื่อเราต้องการเพิ่มคุณสมบัติใหม่ ก่อนอื่นเราจะอธิบายพฤติกรรมที่ต้องการว่าเป็นการทดสอบใหม่ เราทำการเปลี่ยนแปลงน้อยที่สุดที่จำเป็นเพื่อให้ผ่านการทดสอบนั้นโดยไม่ทำลายส่วนอื่น

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

นอกจากนี้ การทดสอบมีความซับซ้อนมากขึ้น เนื่องจากตอนนี้เรามีโค้ดให้ทดสอบมากขึ้น และสิ่งที่มักจะเกิดขึ้นก็คือนักพัฒนาปรับการทดสอบให้เป็นโค้ด นั่นหมายความว่าเราปรับพฤติกรรมเพื่อให้ตรงกับสิ่งที่โค้ดของเราทำ แทนที่จะทำอย่างอื่น

เมื่อเราเขียนการทดสอบตั้งแต่เนิ่นๆ คำจำกัดความของปัญหาจะเล็กลงและง่ายต่อการสรุปประเด็นดังกล่าวมากกว่าพฤติกรรมทั่วไปและซับซ้อน

ความคิดสุดท้าย

แม้ว่าการทดสอบการเขียนอาจดูเหมือนเป็นทางเลือกที่ต้องทำ แต่สิ่งสำคัญคือต้องเริ่มต้นด้วยพื้นฐานที่ดี การเข้ารหัสนั้นยากอยู่แล้ว ทำให้ตัวคุณเองและเพื่อนร่วมทีมง่ายขึ้นด้วย การเขียนโค้ดที่ทดสอบได้ซึ่งอ่าน ทำความเข้าใจ และบำรุงรักษา ได้ง่ายขึ้น สุดท้ายนี้ หากคุณมีปัญหาในการทำแบบทดสอบของคุณให้เป็นไปตามความต้องการของคุณ ปัญหาน่าจะอยู่ในรหัสของคุณมากกว่าในการทดสอบของคุณ

สนใจที่จะทำงานกับโค้ดที่สะอาดและทดสอบได้หรือไม่
ตรวจสอบตำแหน่งที่เปิดของเราสำหรับ Senior Backend Developer
หรือส่ง ใบสมัครแบบเปิดถึง เรา !