ควร savePeople () ทดสอบเป็นหน่วย
ใช่มันควรจะเป็น แต่พยายามเขียนเงื่อนไขการทดสอบของคุณในลักษณะที่เป็นอิสระจากการใช้งาน ตัวอย่างเช่นการเปลี่ยนตัวอย่างการใช้งานของคุณเป็นการทดสอบหน่วย:
function testSavePeople() {
myDataStore = new Store('some connection string', 'password');
myPeople = ['Joe', 'Maggie', 'John'];
savePeople(myDataStore, myPeople);
assert(myDataStore.containsPerson('Joe'));
assert(myDataStore.containsPerson('Maggie'));
assert(myDataStore.containsPerson('John'));
}
การทดสอบนี้ทำหลายสิ่ง:
- มันตรวจสอบสัญญาของฟังก์ชั่น
savePeople()
- มันไม่สนใจเกี่ยวกับการดำเนินการของ
savePeople()
- มันบันทึกตัวอย่างการใช้งานของ
savePeople()
โปรดทราบว่าคุณยังสามารถเยาะเย้ย / สตับ / ปลอมที่เก็บข้อมูล ในกรณีนี้ฉันจะไม่ตรวจสอบการเรียกใช้ฟังก์ชันที่ชัดเจน แต่สำหรับผลลัพธ์ของการดำเนินการ วิธีนี้จัดทำแบบทดสอบของฉันสำหรับการเปลี่ยนแปลงในอนาคต
ตัวอย่างเช่นการดำเนินการจัดเก็บข้อมูลของคุณอาจมีsaveBulkPerson()
วิธีการในอนาคต - ตอนนี้การเปลี่ยนแปลงการใช้งานของsavePeople()
การใช้saveBulkPerson()
จะไม่ทำลายการทดสอบหน่วยตราบใดที่saveBulkPerson()
ทำงานตามที่คาดไว้ และถ้าsaveBulkPerson()
อย่างใดไม่ทำงานตามที่คาดไว้การทดสอบหน่วยของคุณจะตรวจจับนั้น
หรือจำนวนการทดสอบดังกล่าวเพื่อทดสอบโครงสร้างภาษาสำหรับแต่ละภาษาหรือไม่
ดังที่กล่าวไว้ให้ลองทดสอบผลลัพธ์ที่คาดหวังและอินเทอร์เฟซของฟังก์ชันไม่ใช่เพื่อการนำไปใช้ (เว้นแต่ว่าคุณกำลังทำการทดสอบการรวม - จากนั้นอาจใช้การเรียกฟังก์ชั่นเฉพาะ) หากมีหลายวิธีในการใช้ฟังก์ชั่นพวกเขาทั้งหมดควรทำงานกับการทดสอบหน่วยของคุณ
เกี่ยวกับการอัปเดตคำถามของคุณ:
ทดสอบการเปลี่ยนแปลงสถานะ! เช่นจะใช้แป้งบางส่วน ตามการใช้งานของคุณยืนยันว่าจำนวนการใช้งานที่dough
พอดีpan
หรือยืนยันว่าการdough
ใช้หมดแล้ว ยืนยันว่าpan
มีคุกกี้หลังจากการเรียกใช้ฟังก์ชัน ยืนยันว่าoven
ว่าง / อยู่ในสถานะเดียวกับเมื่อก่อน
สำหรับการทดสอบเพิ่มเติมตรวจสอบกรณีขอบ: จะเกิดอะไรขึ้นถ้าoven
ไม่ว่างเปล่าก่อนที่จะโทร? เกิดอะไรขึ้นถ้ามีอยู่ไม่เพียงพอdough
? ถ้าpan
เต็มแล้ว?
คุณควรจะสามารถอนุมานข้อมูลที่จำเป็นทั้งหมดสำหรับการทดสอบเหล่านี้จากวัตถุโดแป้งและเตาอบเอง ไม่จำเป็นต้องจับสายการทำงาน รักษาฟังก์ชั่นราวกับว่าการใช้งานจะไม่สามารถใช้ได้กับคุณ!
ในความเป็นจริงผู้ใช้ TDD ส่วนใหญ่เขียนการทดสอบก่อนที่พวกเขาจะเขียนฟังก์ชันดังนั้นพวกเขาจึงไม่ได้ขึ้นอยู่กับการใช้งานจริง
สำหรับการเพิ่มล่าสุดของคุณ:
เมื่อผู้ใช้สร้างบัญชีใหม่สิ่งต่าง ๆ ต้องเกิดขึ้น: 1) ต้องมีการสร้างบันทึกผู้ใช้ใหม่ในฐานข้อมูล 2) ต้องส่งอีเมลต้อนรับ 3) ที่อยู่ IP ของผู้ใช้ต้องถูกบันทึกไว้เนื่องจากการฉ้อโกง วัตถุประสงค์
ดังนั้นเราต้องการสร้างวิธีการที่เชื่อมโยงทุกขั้นตอน "ผู้ใช้ใหม่":
function createNewUser(validatedUserData, emailService, dataStore) {
userId = dataStore.insertUserRecord(validateduserData);
emailService.sendWelcomeEmail(validatedUserData);
dataStore.recordIpAddress(userId, validatedUserData.ip);
}
สำหรับฟังก์ชั่นเช่นนี้ฉันจะเยาะเย้ย / ต้นขั้ว / ปลอม (สิ่งที่ดูเหมือนทั่วไปมากขึ้น) dataStore
และemailService
พารามิเตอร์ ฟังก์ชั่นนี้ไม่ได้ทำการเปลี่ยนสถานะใด ๆ กับพารามิเตอร์ใด ๆ ด้วยตัวเองมันจะมอบหมายให้กับวิธีการบางอย่างของพวกเขา ฉันจะพยายามตรวจสอบว่าการเรียกใช้ฟังก์ชันทำ 4 สิ่ง:
- มันแทรกผู้ใช้ลงในแหล่งข้อมูล
- มันส่ง (หรืออย่างน้อยเรียกว่าวิธีการที่เกี่ยวข้อง) อีเมลต้อนรับ
- มันบันทึก IP ของผู้ใช้ลงในแหล่งข้อมูล
- มันมอบหมายข้อยกเว้น / ข้อผิดพลาดที่พบ (ถ้ามี)
การตรวจสอบ 3 ครั้งแรกสามารถทำได้ด้วย mocks, stubs หรือ fakes of dataStore
และemailService
(คุณไม่ต้องการส่งอีเมลเมื่อทำการทดสอบ) เนื่องจากฉันต้องค้นหาความคิดเห็นบางส่วนสิ่งเหล่านี้จึงแตกต่าง:
- ของปลอมเป็นวัตถุที่มีพฤติกรรมเหมือนกับต้นฉบับและอยู่ในขอบเขตที่แยกไม่ออก รหัสของมันสามารถนำกลับมาใช้ซ้ำได้ตลอดการทดสอบ เช่นนี้สามารถเป็นฐานข้อมูลในหน่วยความจำง่ายสำหรับ wrapper ฐานข้อมูล
- ต้นขั้วเพียงดำเนินการเท่าที่จำเป็นเพื่อตอบสนองการดำเนินงานที่จำเป็นของการทดสอบนี้ ในกรณีส่วนใหญ่ต้นขั้วนั้นมีความเฉพาะเจาะจงกับการทดสอบหรือกลุ่มการทดสอบที่ต้องการวิธีการดั้งเดิมเพียงเล็กน้อย ในตัวอย่างนี้มันอาจจะเป็น
dataStore
ที่เพิ่งดำเนินการรุ่นที่เหมาะสมและinsertUserRecord()
recordIpAddress()
- การเยาะเย้ยเป็นวัตถุที่ช่วยให้คุณตรวจสอบวิธีการใช้งาน (ส่วนใหญ่มักให้คุณประเมินการโทรไปยังวิธีการ) ฉันพยายามใช้พวกเขาเท่าที่จำเป็นในการทดสอบหน่วยเนื่องจากใช้พวกคุณจริง ๆ แล้วลองทดสอบการใช้งานฟังก์ชั่นไม่ใช่การยึดติดกับส่วนต่อประสาน แต่มันก็ยังคงมีการใช้งานอยู่ เฟรมเวิร์กจำลองจำนวนมากมีอยู่เพื่อช่วยคุณสร้างเฉพาะจำลองที่คุณต้องการ
โปรดทราบว่าหากวิธีใดวิธีหนึ่งเหล่านี้เกิดข้อผิดพลาดเราต้องการให้ข้อผิดพลาดเกิดฟองขึ้นกับรหัสการโทรเพื่อให้สามารถจัดการข้อผิดพลาดได้ตามที่เห็นสมควร ถ้ามันถูกเรียกโดยรหัส API มันอาจแปลข้อผิดพลาดเป็นรหัสการตอบสนอง HTTP ที่เหมาะสม ถ้ามันถูกเรียกโดยเว็บอินเตอร์เฟสมันอาจแปลข้อผิดพลาดเป็นข้อความที่เหมาะสมเพื่อแสดงต่อผู้ใช้และอื่น ๆ ประเด็นก็คือฟังก์ชั่นนี้ไม่รู้วิธีจัดการกับข้อผิดพลาดที่อาจเกิดขึ้น
ข้อยกเว้น / ข้อผิดพลาดที่คาดไว้เป็นกรณีทดสอบที่ถูกต้อง: คุณยืนยันว่าในกรณีที่เหตุการณ์ดังกล่าวเกิดขึ้นฟังก์ชันจะทำงานตามที่คุณคาดหวัง สามารถทำได้โดยการปล่อยให้วัตถุจำลอง / ปลอม / ต้นขั้วที่เกี่ยวข้องโยนทิ้งเมื่อต้องการ
สาระสำคัญของความสับสนของฉันคือการทดสอบหน่วยฟังก์ชั่นดังกล่าวดูเหมือนว่าจำเป็นต้องทำซ้ำการใช้งานที่แน่นอนในการทดสอบตัวเอง (โดยระบุว่าวิธีการที่เรียกว่าบน mocks ในลำดับที่แน่นอน) และดูเหมือนว่าผิด
บางครั้งสิ่งนี้จะต้องทำ (แม้ว่าคุณส่วนใหญ่สนใจในเรื่องนี้ในการทดสอบการรวม) บ่อยครั้งมีวิธีอื่น ๆ ในการตรวจสอบผลข้างเคียง / การเปลี่ยนแปลงสถานะที่คาดหวัง
การตรวจสอบการเรียกใช้ฟังก์ชันที่แน่นอนทำให้การทดสอบหน่วยเปราะบาง: การเปลี่ยนแปลงเพียงเล็กน้อยกับฟังก์ชั่นดั้งเดิมทำให้พวกเขาล้มเหลว สิ่งนี้สามารถเป็นที่ต้องการหรือไม่ แต่มันต้องการการเปลี่ยนแปลงการทดสอบหน่วยที่เกี่ยวข้องเมื่อใดก็ตามที่คุณเปลี่ยนฟังก์ชั่น (ไม่ว่าจะเป็นการปรับโครงสร้างการเพิ่มประสิทธิภาพการแก้ไขข้อบกพร่อง ... )
น่าเศร้าที่ในกรณีนั้นการทดสอบหน่วยสูญเสียความน่าเชื่อถือบางส่วน: เนื่องจากมีการเปลี่ยนแปลงจึงไม่ยืนยันฟังก์ชันหลังจากการเปลี่ยนแปลงนั้นทำแบบเดียวกับที่เคยเป็นมา
ยกตัวอย่างเช่นลองพิจารณาว่ามีคนเพิ่มการเรียกไปที่oven.preheat()
(การเพิ่มประสิทธิภาพ!) ในตัวอย่างการอบคุกกี้ของคุณ:
- หากคุณล้อเลียนวัตถุเตาอบมันจะไม่คาดหวังว่าการโทรและการทดสอบล้มเหลวแม้ว่าพฤติกรรมที่สังเกตได้ของวิธีการจะไม่เปลี่ยนแปลง (คุณยังคงมีคุกกี้อยู่จำนวนหนึ่งหวังว่า)
- ต้นขั้วอาจหรือไม่ล้มเหลวขึ้นอยู่กับว่าคุณเพิ่มวิธีการที่จะทดสอบหรืออินเทอร์เฟซทั้งหมดด้วยวิธีจำลองบางอย่างเท่านั้น
- ของปลอมไม่ควรล้มเหลวเพราะมันควรจะใช้วิธีการ (ตามอินเทอร์เฟซ)
ในการทดสอบหน่วยของฉันฉันพยายามเป็นคนทั่วไปให้มากที่สุด: หากการเปลี่ยนแปลงมีผล แต่พฤติกรรมที่มองเห็นได้ (จากมุมมองของผู้โทร) ยังคงเหมือนเดิมการทดสอบของฉันควรผ่าน เป็นการดีที่กรณีเดียวที่ฉันต้องเปลี่ยนการทดสอบหน่วยที่มีอยู่ควรจะแก้ไขข้อบกพร่อง (ของการทดสอบไม่ใช่ฟังก์ชั่นภายใต้การทดสอบ)