เหตุใดฉันจึงต้องการการทดสอบหน่วยสำหรับวิธีการทดสอบที่เก็บ?


19

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

สมมติว่าฉันได้สร้างการทดสอบหน่วยต่อไปนี้:

[TestMethod]
public void GetOrderByIDTest()
{
   //Uses Moq for dependency for getting order to make sure 
   //ID I set up in 'Arrange' is same one returned to test in 'Assertion'
}

ดังนั้นถ้าฉันตั้งค่าOrderIdExpected = 5และวัตถุจำลองของฉันจะกลับมา5เป็น ID ที่การทดสอบของฉันจะผ่าน ฉันเข้าใจแล้ว ฉันหน่วยทดสอบรหัสเพื่อให้แน่ใจว่าสิ่งที่รหัส preforms ของฉันคืนวัตถุและ ID ที่คาดหวังและไม่ใช่อย่างอื่น

อาร์กิวเมนต์ที่ฉันจะได้รับคือ:

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

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

ความช่วยเหลือใด ๆ ในคำถามนี้ชื่นชมอย่างมากขอบคุณ!


2
ผมบอกว่าส่งตรงไปยังผู้ใช้ ... การทดสอบพวกเขากำลังจะมีการเปลี่ยนแปลงความต้องการ แต่อย่างใด ...
นาธานทุ่งหญ้า

+1 สำหรับการเสียดสีเพื่อให้สิ่งต่าง ๆ แสงเป็นครั้งคราว
atconway

2
คำตอบง่ายๆก็คือแต่ละวิธีอาจมีจำนวนกรณีขอบ x (สมมุติว่าคุณต้องการทดสอบ ID เชิงบวก, ID ของ 0 และ ID เชิงลบ) หากคุณต้องการทดสอบฟังก์ชันการทำงานบางอย่างซึ่งใช้วิธีนี้ซึ่งมี 3 กรณีขอบคุณจะต้องเขียนเคสทดสอบ 9 กรณีเพื่อทดสอบแต่ละชุดของเคสขอบ โดยการแยกพวกเขาคุณจะต้องเขียน 6 นอกจากนี้การทดสอบให้ความคิดที่เฉพาะเจาะจงมากขึ้นว่าทำไมบางสิ่งบางอย่างแตก บางทีที่เก็บของคุณคืนค่า null เมื่อล้มเหลวและข้อยกเว้น null ถูกส่งลงหลายร้อยบรรทัด
Rob

ฉันคิดว่าคุณเข้มงวดเกินไปกับคำจำกัดความของการทดสอบ "หน่วย" "หน่วยงาน" สำหรับชั้นเก็บข้อมูลคืออะไร?
Caleb

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

คำตอบ:


20

การทดสอบหน่วยและการทดสอบการรวมมีวัตถุประสงค์ที่แตกต่างกัน

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

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

  1. เขียนยากโดยรวม
  2. จะเปราะบางมากขึ้นเนื่องจากการพึ่งพาที่จำเป็นทั้งหมดและ
  3. เสนอความคุ้มครองรหัสน้อย

Bottom line: ใช้การทดสอบการรวมเพื่อตรวจสอบว่าการเชื่อมต่อระหว่างวัตถุทำงานอย่างถูกต้อง แต่ต้องอาศัยการทดสอบหน่วยก่อนเพื่อตรวจสอบข้อกำหนดการใช้งาน


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


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

1
หาก SP เพิ่งส่งคืนผลลัพธ์จากฐานข้อมูลการทดสอบการรวมอาจเพียงพอ หากมันมีตรรกะที่ไม่น่ารำคาญในนั้นการทดสอบหน่วยจะถูกระบุ ดูเพิ่มเติมที่stackoverflow.com/questions/1126614/…และmsdn.microsoft.com/en-us/library/aa833169(v=vsv1.100).aspx (เฉพาะเซิร์ฟเวอร์ SQL)
Robert Harvey

12

ฉันอยู่ฝ่ายปฏิบัตินิยม อย่าทดสอบรหัสของคุณสองครั้ง

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

  1. พวกเขาสามารถทดแทนการทดสอบหน่วยเมื่อทำ TDD ในขณะที่มีโค้ดทดสอบสำเร็จรูปจำนวนหนึ่งที่ต้องเขียนก่อนที่คุณจะสามารถเริ่มต้นด้วยรหัสจริงมันทำงานได้เป็นอย่างดีด้วยวิธีการสีแดง / เขียว / refactor เมื่อทุกอย่างเข้าที่
  2. พวกเขาทดสอบรหัสจริงของที่เก็บ - รหัสที่มีอยู่ในสตริง SQL หรือคำสั่ง ORM สิ่งสำคัญคือการตรวจสอบว่าแบบสอบถามถูกต้องมากกว่าที่จะตรวจสอบว่าคุณส่งสตริงไปยัง StatementExecutor จริง ๆ
  3. พวกเขายอดเยี่ยมในการทดสอบการถดถอย หากพวกเขาล้มเหลวก็มักจะเกิดจากปัญหาจริงเช่นการเปลี่ยนแปลงในสคีมาที่ไม่ได้รับการพิจารณา
  4. สิ่งเหล่านี้จะขาดไม่ได้เมื่อเปลี่ยนสกีมาฐานข้อมูล คุณสามารถมั่นใจได้ว่าคุณไม่ได้เปลี่ยนสคีมาในลักษณะที่ทำให้ใบสมัครของคุณแตกหักตราบใดที่ผ่านการทดสอบ (การทดสอบหน่วยไม่มีประโยชน์ในกรณีนี้เนื่องจากเมื่อขั้นตอนการจัดเก็บไม่มีอยู่การทดสอบหน่วยจะยังคงผ่านไป)

ในทางปฏิบัติมากแน่นอน ฉันเคยประสบกับกรณีที่ฐานข้อมูลในหน่วยความจำจริงทำงานแตกต่างกัน (ไม่ใช่ในแง่ของประสิทธิภาพ) กับฐานข้อมูลจริงของฉันเพราะ ORM ที่แตกต่างกันเล็กน้อย ดูแลในการทดสอบการรวมของคุณ
Marcel

1
@ Marcel ฉันได้พบว่า ฉันแก้ไขได้โดยบางครั้งใช้การทดสอบทั้งหมดกับฐานข้อมูลจริง
Winston Ewert

4

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

นอกจากนี้รวมถึงการทดสอบหน่วยมีประโยชน์ดังต่อไปนี้:

  1. การทดสอบหน่วยช่วยให้คุณสามารถย่อยสลายการทดสอบการรวมที่ล้มเหลวได้อย่างรวดเร็ว (อาจเป็นเพราะข้อบกพร่องการถดถอย) และระบุสาเหตุ นอกจากนี้สิ่งนี้จะสื่อสารปัญหาไปยังทีมทั้งหมดได้เร็วกว่าไดอะแกรมหรือเอกสารอื่น ๆ
  2. การทดสอบหน่วยสามารถใช้เป็นตัวอย่างและเอกสารประกอบ (ประเภทของเอกสารที่รวบรวมได้จริง ) พร้อมกับรหัสของคุณ ไม่เหมือนเอกสารอื่น ๆ ที่คุณจะรู้ได้ทันทีเมื่อมันล้าสมัย
  3. การทดสอบหน่วยสามารถใช้เป็นตัวบ่งชี้ประสิทธิภาพพื้นฐานเมื่อพยายามแยกย่อยปัญหาประสิทธิภาพการทำงานที่ใหญ่กว่าในขณะที่การทดสอบการรวมมักจะต้องใช้เครื่องมือจำนวนมากเพื่อค้นหาปัญหา

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


4

การทดสอบมีประโยชน์เมื่อหยุดพัก: เชิงรุกหรือการทดสอบซ้ำ

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

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

ตัวอย่างเช่นหากเรากำลังสร้างระบบ ETL การทดสอบที่เกี่ยวข้องที่สุดจะอยู่ที่ข้อมูล (staged, faked หรือ live) ระบบเดียวกันจะมีการทดสอบหน่วย แต่มีเพียงการตรวจสอบความถูกต้องการกรองและอื่น ๆ

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

การทดสอบหน่วยสามารถทดสอบวิธีการทำงานของที่เก็บ การทดสอบการรวมสามารถทดสอบสิ่งที่เกิดขึ้นจริงเมื่อที่เก็บถูกเรียก

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

ดังนั้นเวลาส่วนใหญ่ฉันเพิ่งพบว่าดีพอที่จะเขียนการทดสอบหน่วยบนวัตถุที่ใช้ที่เก็บ วิธีนี้เราจะทดสอบว่าวิธีการเก็บข้อมูลที่เหมาะสมได้รับการเรียกและผลการเยาะเย้ยที่เหมาะสมจะถูกส่งกลับ


ฉันชอบสิ่งนี้: "ตอนนี้" สิ่งที่เกิดขึ้นจริง ๆ "อาจดูเหมือนล่อลวงมาก แต่สิ่งนี้อาจมีราคาแพงมากสำหรับการทำงานอย่างต่อเนื่องและซ้ำ ๆ "
atconway

2

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

ดังนั้น (นี่เป็นตัวอย่างที่ง่ายมาก):

interface IOrderRepository
{
    Order GetOrderByID(Guid id);
}

class OrderRepository : IOrderRepository
{
    private readonly ISqlExecutor sqlExecutor;
    public OrderRepository(ISqlExecutor sqlExecutor)
    {
        this.sqlExecutor = sqlExecutor;
    }

    public Order GetOrderByID(Guid id)
    {
        var sql = "SELECT blah, blah FROM Order WHERE OrderId = @p0";
        var dataset = this.sqlExecutor.Execute(sql, p0 = id);
        var result = this.orderFromDataset(dataset);
        return result;
    }
}

จากนั้นเมื่อทำการทดสอบOrderRepositoryให้ส่งผ่านการเยาะเย้ยISqlExecutorและตรวจสอบว่าวัตถุภายใต้การทดสอบผ่านใน SQL ที่ถูกต้อง (นั่นคือหน้าที่ของมัน) และส่งคืนOrderวัตถุที่เหมาะสมซึ่งให้ชุดข้อมูลผลลัพธ์บางส่วน น่าจะเป็นวิธีเดียวที่จะทดสอบSqlExecutorคลาสคอนกรีตได้ด้วยการทดสอบการรวมกลุ่มที่ยุติธรรมพอ แต่นั่นเป็นคลาส wrapper ที่บางและแทบจะไม่เคยเปลี่ยนเลย

คุณยังต้องทดสอบหน่วยการจัดเก็บของคุณด้วย


0

ฉันสามารถลดสิ่งนี้ลงในแนวความคิดที่เรียบง่ายมาก: ชอบการทดสอบหน่วยเพราะช่วยในการค้นหาข้อบกพร่อง และทำสิ่งนี้อย่างสม่ำเสมอเพราะความไม่ลงรอยกันทำให้เกิดความสับสน

เหตุผลเพิ่มเติมมาจากกฎเดียวกันกับแนวทางการพัฒนา OO เนื่องจากใช้กับการทดสอบ ตัวอย่างเช่นหลักการความรับผิดชอบเดียว

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

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.