ทำให้วิธีส่วนตัวเป็นแบบสาธารณะเพื่อทดสอบหน่วย…เป็นความคิดที่ดี?


301

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


บางครั้งฉันพบว่าตัวเองต้องการวิธีการแบบส่วนตัวในที่สาธารณะในชั้นเรียนเพื่อเขียนการทดสอบหน่วยบางอย่างสำหรับมัน

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

คนอื่นคิดว่าตัวเองกำลังทำสิ่งนี้อยู่หรือเปล่าเพราะฉันไม่ชอบทำมันจริงเหรอ? โดยส่วนตัวแล้วฉันคิดว่าโบนัสนั้นมีค่ามากกว่าปัญหาในการทำให้วิธีการเป็นสาธารณะซึ่งไม่ได้ให้บริการใด ๆ นอกห้องเรียน ...

UPDATE

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

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


2
"การเปลี่ยนระบบเป็น 'สนับสนุนเครื่องมือสนับสนุน' หากคุณต้องการจะเป็นการฝึกฝนที่ไม่ดีอย่างเห็นได้ชัด" ก็ใช่แล้ว แต่ OTOH ถ้าระบบของคุณไม่ทำงานได้ดีกับเครื่องมือที่จัดตั้งขึ้นบางอย่างอาจจะเป็นอย่างผิดปกติกับระบบ
Thilo

1
stackoverflow.com/questions/105007/do-you-test-private-methodไม่ซ้ำกันอย่างไร
Sanghyun Lee

1
ไม่ใช่คำตอบ แต่คุณสามารถเข้าถึงได้โดยไตร่ตรอง
Zano

33
ไม่คุณไม่ควรเปิดเผย แต่คุณควรทดสอบว่าคลาสของคุณมีพฤติกรรมเช่นเดียวกับที่ควรทำผ่าน API สาธารณะ และถ้าการเปิดเผย internals เป็นทางเลือกเดียวเท่านั้น (ซึ่งฉันสงสัย) อย่างน้อยคุณควรทำให้ชุด accessors ได้รับการป้องกันเพื่อให้ชั้นเรียนในแพ็คเกจเดียวกันเท่านั้น (ตามการทดสอบของคุณ) สามารถเข้าถึงได้
JB Nizet

4
ใช่แล้ว. เป็นที่ยอมรับอย่างสมบูรณ์ในการแสดงวิธีการแบบแพคเกจส่วนตัว (ค่าเริ่มต้น) เพื่อให้สามารถเข้าถึงได้จากการทดสอบหน่วย ไลบรารีอย่าง Guava ยังให้@VisibileForTestingคำอธิบายประกอบเพื่อสร้างวิธีดังกล่าว - ฉันขอแนะนำให้คุณทำเช่นนี้เพื่อให้เหตุผลสำหรับวิธีการที่ไม่ได้รับการprivateบันทึกอย่างถูกต้อง
Boris the Spider

คำตอบ:


186

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

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

ตัวอย่างเช่นลองใช้ pseudo-code นี้:

public class NavigationTest {
    private Navigation nav;

    @Before
    public void setUp() {
        // Set up nav so the order is page1->page2->page3 and
        // we've moved back to page2
        nav = ...;
    }

    @Test
    public void testFirst() {
        nav.first();

        assertEquals("page1", nav.getPage());

        nav.next();
        assertEquals("page2", nav.getPage());

        nav.next();
        assertEquals("page3", nav.getPage());
    }

    @Test
    public void testLast() {
        nav.last();

        assertEquals("page3", nav.getPage());

        nav.previous();
        assertEquals("page2", nav.getPage());

        nav.previous();
        assertEquals("page1", nav.getPage());
    }
}

8
ฉันเห็นด้วยกับคำตอบของคุณอย่างสมบูรณ์ นักพัฒนาหลายคนจะถูกล่อลวงให้ใช้ตัวแก้ไขการเข้าถึงเริ่มต้นและพิสูจน์ว่าเฟรมเวิร์กโอเพนซอร์สที่ดีใช้กลยุทธ์นี้ดังนั้นจึงต้องถูกต้อง เพียงเพราะคุณไม่สามารถหมายความว่าคุณควร +1
CKing

6
แม้ว่าคนอื่นจะให้ข้อมูลเชิงลึกที่มีประโยชน์ แต่ฉันต้องบอกว่านี่เป็นวิธีแก้ปัญหาที่ฉันคิดว่าเหมาะสมที่สุดสำหรับปัญหานี้ มันทำให้การทดสอบไม่เชื่อเรื่องพระเจ้าอย่างสมบูรณ์ว่าพฤติกรรมที่ตั้งใจจะทำได้ภายใน 10!
Gitahi Ng'ang'a

อย่างไรก็ตามมันเป็นเรื่องที่ดีในตัวอย่างที่ได้รับจาก @Mureinik ที่นี่วิธีการทดสอบจะครอบคลุมมากกว่าวิธีการทดสอบจริงหรือไม่? ตัวอย่างเช่น testFirst () เรียก next () ซึ่งไม่ใช่วิธีการทดสอบที่นี่จริงๆแรก () คือ มันจะไม่สะอาดและชัดเจนกว่าแค่มีวิธีทดสอบ 1 วิธีซึ่งครอบคลุมวิธีการนำทางทั้ง 4 วิธีหรือไม่
Gitahi Ng'ang'a

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

1
การสร้างผู้ได้รับเพียงเพื่อการทดสอบหน่วย? สิ่งนี้ขัดกับธัญพืชของOOP / การคิดวัตถุในส่วนที่ควรเปิดเผยพฤติกรรมไม่ใช่ข้อมูล
IntelliData

151

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

หากคุณต้องการทดสอบวิธีการแยกส่วนตัวจริงๆใน Java คุณสามารถใช้Easymock / Powermockเพื่อให้คุณทำเช่นนี้ได้

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

'ฟังการทดสอบ ' - ถ้าเป็นการยากที่จะทดสอบนั่นเป็นการบอกคุณเกี่ยวกับการออกแบบของคุณหรือไม่ คุณสามารถกำหนดโครงสร้างของการทดสอบสำหรับวิธีนี้ได้อีกเล็กน้อยและครอบคลุมโดยการทดสอบผ่าน API สาธารณะหรือไม่

นี่คือสิ่งที่ Michael Feathers ต้องพูดใน 'การทำงานอย่างมีประสิทธิภาพด้วยรหัสมรดก "

"หลายคนใช้เวลามากมายในการพยายามหาวิธีแก้ปัญหานี้ ... คำตอบที่แท้จริงคือถ้าคุณอยากทดสอบวิธีส่วนตัววิธีนั้นไม่ควรเป็นส่วนตัวถ้าทำให้วิธีนั้นเป็นแบบสาธารณะ ทำให้คุณรำคาญใจโอกาสเป็นเพราะมันเป็นส่วนหนึ่งของความรับผิดชอบที่แยกจากกันมันควรจะอยู่ในชั้นเรียนอื่น " [ ทำงานอย่างมีประสิทธิภาพด้วยรหัสดั้งเดิม (2005) โดย M. Feathers]


5
+1 สำหรับ Easymock / Powermock และไม่เคยเปิดเผยวิธีการสาธารณะใด ๆ เลย
Leonard Brünings

51
+1 สำหรับ "ฟังการทดสอบ" หากคุณรู้สึกว่าจำเป็นต้องทดสอบวิธีส่วนตัวโอกาสที่ดีที่ตรรกะในวิธีนั้นซับซ้อนมากจนควรอยู่ในระดับผู้ช่วยแยกต่างหาก จากนั้นคุณสามารถทดสอบระดับผู้ช่วย
ฟิล

2
'ฟังการทดสอบ' ดูเหมือนจะไม่ทำงาน นี่คือลิงค์แคช: webcache.googleusercontent.com/…
Jess Telford

1
ฉันเห็นด้วยกับ @Phil (โดยเฉพาะการอ้างอิงหนังสือ) แต่ฉันมักจะพบว่าตัวเองอยู่ในสถานการณ์ไก่ / ไข่ด้วยรหัสดั้งเดิม ฉันรู้ว่าวิธีการนี้อยู่ในชั้นเรียนอื่น แต่ฉันไม่ได้ทำการรีแฟคเตอร์ที่สะดวกสบาย 100% โดยไม่มีการทดสอบตั้งแต่แรก
เควิน

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

67

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

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


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

แต่การแยกคลาสและการทดสอบเท่านั้นซึ่งหมายความว่าการทดสอบของคุณสูญเสียความรู้ว่าคลาสดั้งเดิมนั้นใช้รหัสที่แยก คุณแนะนำให้เติมช่องว่างนี้ในการทดสอบอย่างไร และถ้าคำตอบคือแค่ทดสอบอินเทอร์เฟซสาธารณะของคลาสดั้งเดิมในลักษณะที่ทำให้เกิดการใช้ตรรกะที่แยกออกมาทำไมถึงต้องแยกมันออกมาในตอนแรก? (ฉันไม่ได้ตั้งใจจะฟังการเผชิญหน้าที่นี่ btw - ฉันแค่สงสัยอยู่เสมอว่าผู้คนสร้างความสมดุลในการแลกเปลี่ยนเช่นนี้)
Mal Ross

@mal ฉันต้องการทดสอบการทำงานร่วมกัน กล่าวอีกนัยหนึ่งการเยาะเย้ยเพื่อตรวจสอบชั้นใหม่ที่ถูกเรียกอย่างถูกต้อง ดูคำตอบของฉันด้านล่าง
Finglas

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

1
@sholsinger: ใน C # คลาสสาธารณะอาจไม่สืบทอดจากคลาสภายใน
Eric Lippert

45

คำตอบมากมายแนะนำให้ทดสอบเฉพาะส่วนต่อสาธารณะ แต่ IMHO นี้ไม่สมจริง - หากวิธีการทำบางอย่างที่มี 5 ขั้นตอนคุณจะต้องทดสอบห้าขั้นตอนแยกจากกันไม่ใช่ทั้งหมดเข้าด้วยกัน สิ่งนี้ต้องการการทดสอบทั้งห้าวิธีซึ่งอาจเป็นอย่างอื่น (นอกเหนือจากการทดสอบ)privateมิฉะนั้นอาจจะมี

วิธีปกติในการทดสอบวิธีการ "ส่วนตัว" คือการให้อินเตอร์เฟซของตัวเองทุกคลาสและสร้างวิธี "ส่วนตัว" publicแต่ไม่รวมไว้ในอินเทอร์เฟซ ด้วยวิธีนี้พวกเขายังคงสามารถทดสอบได้ แต่พวกเขาจะไม่ขยายส่วนต่อประสาน

ใช่สิ่งนี้จะส่งผลให้ไฟล์ - และ class-bloat

ใช่นี่จะทำให้ตัวระบุpublicและตัวprivateระบุซ้ำซ้อน

ใช่นี่เป็นความเจ็บปวดในตูด

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


มีบางคนที่จะแย้งว่าแต่ละขั้นตอนเหล่านั้นควรเป็นคลาสของตัวเองแต่ฉันไม่เห็นด้วย - ถ้าพวกเขาทุกคนแบ่งปันสถานะไม่มีเหตุผลที่จะสร้างห้าชั้นแยกต่างหากที่ห้าวิธีจะทำ ยิ่งแย่ไปกว่านั้นสิ่งนี้ส่งผลให้ไฟล์ - และ class-bloat บวกนี้ยังติด API สาธารณะของโมดูลของคุณ - คลาสเหล่านั้นทั้งหมดจะต้องเป็นpublicถ้าคุณต้องการทดสอบจากโมดูลอื่น(ไม่ว่าจะหรือรวมรหัสทดสอบในโมดูลเดียวกันซึ่งหมายถึงการจัดส่งรหัสทดสอบพร้อมกับผลิตภัณฑ์ของคุณ) .


2
คำตอบที่ดีสำหรับครึ่งแรก
cmcginty

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

1
คำตอบที่ดีมาก คุณมีการลงคะแนนที่อบอุ่นของฉัน ฉันเพิ่มคำตอบ อาจสนใจคุณ
davidxxx

29

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


+1 และคำตอบที่แท้จริง: stackoverflow.com/questions/4603847/…
Raedwald

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

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

6
@Casey: หากวิธีส่วนตัวไม่ใช้โดยส่วนต่อประสานสาธารณะทำไมมันถึงมีอยู่?
Thilo

2
@DaSilva_Ireland: มันคงเป็นเรื่องยากที่จะรักษากรณีทดสอบในอนาคต กรณีใช้งานจริงเท่านั้นที่ใช้วิธีสาธารณะ คือรหัสอื่น ๆ ทั้งหมดสามารถใช้คลาสผ่าน API สาธารณะเท่านั้น ดังนั้นหากคุณต้องการเปลี่ยนการใช้งานและกรณีทดสอบวิธีส่วนตัวล้มเหลวก็ไม่ได้หมายความว่าคุณกำลังทำอะไรผิดพลาดยิ่งไปกว่านั้นถ้าคุณกำลังทดสอบเฉพาะส่วนตัว แต่ไม่ทดสอบสาธารณะอย่างเต็มรูปแบบมันจะไม่ครอบคลุมข้อบกพร่องที่อาจเกิดขึ้น . กรณีทดสอบควรจะครอบคลุมกรณีที่เป็นไปได้ทั้งหมดและเฉพาะกรณีของการใช้งานจริงของคลาส
kan

22

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

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


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

20

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

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

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


+1 ถ้าคุณต้องการทดสอบสิทธิ์ส่วนตัวของคุณคุณอาจจะไม่มีคลาส
jk

+1 สำหรับชั้นเรียนที่ขาดหายไป ดีใจที่ได้เห็นคนอื่น ๆ อย่ากระโดดบน bandwagon "ทำเครื่องหมายภายใน" ทันที
Finglas

บอยฉันต้องไปไกลเพื่อค้นหาแพ็คเกจคำตอบส่วนตัว
Bill K

17

อัปเดต : ฉันได้เพิ่มคำตอบให้กับคำถามนี้ในที่อื่น ๆ อีกมากมาย สามารถพบได้ในบล็อกของฉันมีที่นี้สามารถพบได้บนบล็อกของฉัน

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

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

ตัวอย่างเช่น:

public class SystemUnderTest
{
   public void DoStuff()
   {
      // Blah
      // Call Validate()
   }

   private void Validate()
   {
      // Several lines of complex code...
   }
}

ทำซ้ำสิ่งนี้โดยการแนะนำวัตถุตัวตรวจสอบ

public class SystemUnderTest
{
    public void DoStuff()
    {
       // Blah
       validator.Invoke(..)
    }
}

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


1
โดยส่วนตัวแล้วฉันคิดว่า SRP สามารถนำมาใช้ได้ไกลเกินไปตัวอย่างเช่นถ้าฉันเขียนบริการบัญชีที่เกี่ยวข้องกับการอัปเดต / สร้าง / ลบบัญชีในแอปพลิเคชันคุณบอกฉันหรือไม่ว่าคุณควรสร้างคลาสแยกต่างหากสำหรับการดำเนินการแต่ละครั้ง และสิ่งที่เกี่ยวกับการตรวจสอบเช่นมันคุ้มมูลค่าการแยกตรรกะการตรวจสอบไปยังชั้นเรียนอื่นเมื่อมันจะไม่ถูกใช้ที่อื่น? IMO ที่ทำให้โค้ดอ่านน้อยลงกระชับน้อยลงและบรรจุแอปพลิเคชันของคุณด้วยคลาสฟุ่มเฟือย!
jcvandan

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

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

4
@dormisher เกี่ยวกับบริการบัญชีของคุณคิดว่า SRP เป็น "เหตุผลเดียวในการเปลี่ยนแปลง" หากการดำเนินงาน CRUD ของคุณจะเปลี่ยนไปด้วยกันตามธรรมชาติแล้วพวกเขาก็จะเข้ากันกับ SRP โดยปกติคุณจะต้องเปลี่ยนแปลงสิ่งเหล่านี้เนื่องจากสคีมาฐานข้อมูลอาจเปลี่ยนแปลงซึ่งจะส่งผลต่อการแทรกและอัปเดตตามปกติ (แม้ว่าอาจจะไม่ได้ลบ)
Anthony Pegram

@finglas สิ่งที่คุณคิดเกี่ยวกับเรื่องนี้: stackoverflow.com/questions/29354729/... ฉันไม่รู้สึกอยากทดสอบวิธีส่วนตัวดังนั้นบางทีฉันไม่ควรแยกมันออกเป็นชั้นเรียน แต่มันถูกใช้ในวิธีสาธารณะอีก 2 วิธีดังนั้นฉันจะสิ้นสุดการทดสอบตรรกะเดียวกันสองครั้ง
Rodrigo Ruiz

13

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


11

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

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


10

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


10

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

คุณมีสองทางเลือกถ้าคุณต้องการทดสอบ

  • ใช้การสะท้อนกลับ
  • ให้วิธีการเข้าถึงแพ็คเกจ

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

มีบทความที่ดีมากที่นี่ในนี้


1
ความคิดเห็นที่เป็นธรรม เป็นเพียงตัวเลือกบางทีอาจเป็นเรื่องไม่ดี
adamjmarkham

@Finglas เหตุใดการทดสอบรหัสส่วนตัวโดยใช้การสะท้อนความคิดที่ไม่ดีจึงเป็นเช่นนั้น
Anshul

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

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

1
@Finglas ทดสอบรายละเอียดการใช้งานไม่ผิด นั่นเป็นจุดยืนของคุณในเรื่องนี้ แต่เป็นหัวข้อถกเถียงกันอย่างกว้างขวาง การทดสอบภายในนั้นมีข้อดีเล็กน้อยแม้ว่าคุณอาจไม่ได้รับความคุ้มครองเต็มรูปแบบก็ตาม การทดสอบของคุณมีความสำคัญมากกว่าเพราะคุณกำลังทดสอบวิธีการส่วนตัวโดยตรงแทนที่จะต้องผ่าน API สาธารณะซึ่งอาจทำให้การทดสอบของคุณสับสน การทดสอบเชิงลบทำได้ง่ายขึ้นเนื่องจากคุณสามารถทำให้สมาชิกส่วนตัวของคุณใช้งานไม่ได้และทำให้แน่ใจว่า public API จะส่งกลับข้อผิดพลาดที่เหมาะสมเพื่อตอบสนองต่อสถานะภายในที่ไม่สอดคล้องกัน มีตัวอย่างเพิ่มเติม
Anshul

9

หากคุณใช้ C # คุณสามารถสร้างวิธีการภายใน ด้วยวิธีนี้คุณจะไม่สร้างมลพิษให้กับ API สาธารณะ

จากนั้นเพิ่มแอตทริบิวต์ให้กับ dll

[ชุดประกอบ: InternalsVisibleTo ("MyTestAssembly")]

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


7

ใช้การสะท้อนเพื่อเข้าถึงตัวแปรส่วนตัวหากคุณต้องการ

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


คุณจะทำอย่างไรกับวิธีการโมฆะ?
IntelliData

@IntelliData ใช้การสะท้อนเพื่อเข้าถึงตัวแปรส่วนตัวหากคุณต้องการ
Daniel Alexiuc

6

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

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


1
ถ้าชั้นเรียนมีการดำเนินการตามสัญญาการให้บริการและมีการใช้งานผ่านทางส่วนต่อประสาน - ด้วยวิธีนี้แม้ว่าวิธีการส่วนตัวได้รับการเผยแพร่สู่สาธารณะแล้วจะไม่ถูกเรียกใช้ต่อไปเนื่องจากไม่สามารถมองเห็นได้
jcvandan

ฉันยังต้องการทดสอบคลาสโดยใช้ public API (อินเตอร์เฟส) เพราะมิฉะนั้นหากคุณปรับโครงสร้างห้องเรียนใหม่เพื่อแยกวิธีการภายในคุณต้องเปลี่ยนการทดสอบซึ่งหมายความว่าคุณจะต้องใช้จ่ายบางส่วน ความพยายามเพื่อให้มั่นใจว่าการทดสอบใหม่ทำงานเช่นเดียวกับการทดสอบเก่า
beny23

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

@dormisher - หากมีการใช้งานคลาสผ่านอินเทอร์เฟซคุณจะต้องทดสอบว่าวิธีการเชื่อมต่อสาธารณะทำงานได้อย่างถูกต้องเท่านั้น หากวิธีการสาธารณะทำในสิ่งที่พวกเขาตั้งใจทำคุณจะสนใจเกี่ยวกับวิธีการส่วนตัวอย่างไร
Andrzej Doyle

@Andrzej - ฉันคิดว่าฉันไม่ควรจริง ๆ แต่มีสถานการณ์ที่ฉันเชื่อว่ามันอาจถูกต้องเช่นวิธีการส่วนตัวที่ตรวจสอบสถานะของแบบจำลองมันอาจคุ้มค่าที่จะทดสอบวิธีการเช่นนี้ - แต่วิธีการเช่นนี้ควรจะทดสอบผ่าน อินเทอร์เฟซสาธารณะต่อไป
jcvandan

6

ในแง่ของการทดสอบหน่วยคุณไม่ควรเพิ่มวิธีการมากขึ้น ฉันเชื่อว่าคุณควรทำการทดสอบเกี่ยวกับfirst()วิธีการของคุณซึ่งจะถูกเรียกก่อนการทดสอบแต่ละครั้ง จากนั้นคุณสามารถเรียกหลายครั้ง - next(), previous()และlast()เพื่อดูว่าผลที่ตรงกับความคาดหวังของคุณ ฉันเดาว่าถ้าคุณไม่เพิ่มวิธีการเพิ่มเติมในชั้นเรียนของคุณ (เพียงเพื่อจุดประสงค์ในการทดสอบ) คุณจะยึดหลักการ "กล่องดำ" ของการทดสอบ


5

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


5

ในการอัปเดตของคุณคุณบอกว่าเป็นการดีที่จะทดสอบโดยใช้ API สาธารณะ ที่นี่มีโรงเรียนสองแห่งจริง ๆ

  1. การทดสอบกล่องดำ

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

  2. การทดสอบกล่องสีขาว

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

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


4

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


4

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

ลองดูที่วิดีโอนี้เพื่อดูหัวข้อที่น่าสนใจ


4

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

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

ตัวอย่างเช่น (รหัสหลอก bod ที่นี่);

   public int books(int a) {
     return add(a, 2);
   }
   private int add(int a, int b) {
     return a+b;
   } 

ไม่มีเหตุผลที่จะทดสอบ "เพิ่ม" คุณสามารถทดสอบ "หนังสือ" แทนได้

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


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

เพื่อความชัดเจนฉันยอมรับว่าคุณไม่ควรเปลี่ยนการออกแบบ API ภายนอกของไลบรารีตัวอย่างเช่น แต่คุณอาจต้อง refactor เพื่อให้สามารถทดสอบได้มากขึ้น
Alfred Armstrong

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

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

วิธีการโมฆะทำอะไรบางอย่างหรือพวกเขาไม่จำเป็นต้องมีอยู่ ตรวจสอบสิ่งที่พวกเขาทำ
coteyr

2

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

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


2

โดยทั่วไปฉันเก็บคลาสการทดสอบในโครงการ / ชุดเดียวกันกับคลาสที่อยู่ภายใต้การทดสอบ
วิธีนี้ฉันต้องการเท่านั้นinternalการมองเห็นเพื่อให้ฟังก์ชัน / คลาสทดสอบได้

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

หลักสูตรนี้ใช้ได้กับส่วน C # / .NET ของคำถามของคุณเท่านั้น


2

ผมมักจะเพิ่มวิธีการที่เรียกว่าสิ่งที่ชอบvalidate, verify,checkฯลฯ ในชั้นเรียนเพื่อที่จะสามารถได้รับการเรียกตัวไปทดสอบรัฐภายในของวัตถุ

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


2

Guava มีคำอธิบายประกอบ @VisibleForTesting สำหรับการทำเครื่องหมายวิธีที่มีขอบเขตขยาย (แพคเกจหรือสาธารณะ) ที่พวกเขาจะทำอย่างอื่น ฉันใช้คำอธิบายประกอบแบบ @Private สำหรับสิ่งเดียวกัน

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

เมื่อไหร่:

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

ดูเหมือนว่าศาสนาคือวิศวกรรมที่กล้าหาญ


2

ฉันมักจะปล่อยให้วิธีการเหล่านั้นเป็นprotectedและวางการทดสอบหน่วยในแพคเกจเดียวกัน (แต่ในโครงการอื่นหรือโฟลเดอร์แหล่งที่มา) ที่พวกเขาสามารถเข้าถึงวิธีการป้องกันทั้งหมดเพราะ class loader จะวางไว้ใน namespace เดียวกัน


2

ไม่เพราะมีวิธีที่ดีกว่าในการสร้างแมวที่น่าสนใจ

ชุดทดสอบหน่วยควบคุมบางตัวพึ่งพามาโครในคำจำกัดความของคลาสซึ่งขยายโดยอัตโนมัติเพื่อสร้าง hooks เมื่อสร้างขึ้นในโหมดทดสอบ สไตล์ C มาก แต่ใช้งานได้

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

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

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


2

ใน. Net มีคลาสพิเศษที่เรียกว่า PrivateObjectออกแบบมาโดยเฉพาะเพื่อให้คุณสามารถเข้าถึงวิธีการส่วนตัวของคลาสได้

ดูเพิ่มเติมเกี่ยวกับMSDNหรือที่นี่ในStack Overflow

(ฉันสงสัยว่ายังไม่มีใครพูดถึงมัน)

มีบางสถานการณ์ที่ไม่เพียงพอซึ่งในกรณีนี้คุณจะต้องใช้การไตร่ตรอง

ฉันยังคงปฏิบัติตามคำแนะนำทั่วไปที่จะไม่ทดสอบวิธีส่วนตัว แต่ตามปกติมีข้อยกเว้นอยู่เสมอ


1

ตามที่ระบุไว้อย่างกว้างขวางโดยความคิดเห็นของผู้อื่นการทดสอบหน่วยควรมุ่งเน้นไปที่ API สาธารณะ อย่างไรก็ตามข้อดีข้อเสียและเหตุผลนอกเหนือคุณสามารถเรียกวิธีการส่วนตัวในการทดสอบหน่วยโดยใช้การสะท้อน แน่นอนคุณจะต้องตรวจสอบให้แน่ใจว่าการรักษาความปลอดภัย JRE ของคุณอนุญาต การเรียกวิธีการแบบส่วนตัวเป็นสิ่งที่ Spring Framework ใช้กับReflectionUtils (ดูที่makeAccessible(Method)วิธีการ)

นี่คือคลาสตัวอย่างขนาดเล็กที่มีวิธีการอินสแตนซ์ส่วนตัว

public class A {
    private void doSomething() {
        System.out.println("Doing something private.");
    }
}

และคลาสตัวอย่างที่ดำเนินการวิธีการอินสแตนซ์ส่วนตัว

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class B {
    public static final void main(final String[] args) {
        try {
            Method doSomething = A.class.getDeclaredMethod("doSomething");
            A o = new A();
            //o.doSomething(); // Compile-time error!
            doSomething.setAccessible(true); // If this is not done, you get an IllegalAccessException!
            doSomething.invoke(o);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        }
    }
}

การดำเนินการ B จะพิมพ์Doing something private. หากคุณต้องการจริงๆสามารถใช้การสะท้อนกลับในการทดสอบหน่วยเพื่อเข้าถึงวิธีการอินสแตนซ์ส่วนตัว


0

ส่วนตัวฉันมีปัญหาเดียวกันเมื่อทดสอบวิธีการส่วนตัวและนี่เป็นเพราะเครื่องมือทดสอบบางอย่างมีข้อ จำกัด การออกแบบของคุณไม่ดีหากใช้เครื่องมือ จำกัด หากไม่ตอบสนองต่อความต้องการของคุณให้เปลี่ยนเครื่องมือไม่ใช่การออกแบบ เนื่องจากคุณขอ C # ฉันไม่สามารถเสนอเครื่องมือทดสอบที่ดี แต่สำหรับ Java มีเครื่องมือที่ทรงพลังสองอย่าง: TestNG และ PowerMock และคุณสามารถค้นหาเครื่องมือทดสอบที่สอดคล้องกันสำหรับแพลตฟอร์ม. NET

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