คุณจะทดสอบวิธีการส่วนตัวได้อย่างไร


479

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

วิธีที่ถูกต้องในการทำเช่นนี้คืออะไร?


3
ฉันอาจจะหายไปบางสิ่งบางอย่างหรืออาจเป็นเพียงว่าคำถามนี้คือ ... pre-historicในแง่ของปีอินเทอร์เน็ต แต่การทดสอบหน่วยของวิธีการส่วนตัวตอนนี้ทั้งง่ายและตรงไปตรงมากับ Visual Studio ผลิตคลาส accessor ที่จำเป็นเมื่อต้องการและ เติมตรรกะการทดสอบล่วงหน้าด้วยตัวอย่างเล็ก ๆ น้อย ๆ ที่ใกล้เคียงกับสิ่งที่เราอาจต้องการสำหรับการทดสอบการทำงานที่เรียบง่าย ดูตัวอย่าง msdn.microsoft.com/en-us/library/ms184807%28VS.90%29.aspx
mjv

3
นี้ดูเหมือนว่าซ้ำใกล้stackoverflow.com/questions/34571/...
Raedwald

ผู้ถามอาจไม่ได้ใช้ Visual Studio
Dave

3
อย่าทดสอบหน่วยภายใน: blog.ploeh.dk/2015/09/22/unit-testing-internals
Mark Seemann

คำตอบ:


122

หากคุณกำลังใช้ .net, คุณควรใช้InternalsVisibleToAttribute


86
yuck สิ่งนี้จะถูกรวบรวมไว้ในชุดประกอบที่คุณปล่อย
Jay

14
@Jay - ไม่สามารถใช้หนึ่ง#if DEBUGรอบInternalsVisibleToแอตทริบิวต์ที่จะทำให้มันไม่ได้นำไปใช้กับรหัสการเปิดตัว?
mpontillo

20
@ ไมค์คุณทำได้ แต่จากนั้นคุณสามารถเรียกใช้การทดสอบหน่วยในรหัสการดีบักเท่านั้นไม่ใช่รหัสการปล่อย เนื่องจากรหัสการอนุมัติได้รับการปรับให้เหมาะสมที่สุดคุณอาจเห็นพฤติกรรมที่แตกต่างกันและการกำหนดเวลาที่แตกต่าง ในรหัสแบบมัลติเธรดนี่หมายถึงการทดสอบหน่วยของคุณจะไม่ตรวจจับสภาพการแข่งขันอย่างเหมาะสม ดีกว่ามากคือการใช้การสะท้อนผ่านคำแนะนำของ @ AmazedSaint ด้านล่างหรือใช้ PrivateObject / PrivateType ในตัว สิ่งนี้ช่วยให้คุณเห็นเอกชนใน Release builds โดยสมมติว่าชุดทดสอบของคุณกำลังทำงานด้วยความเชื่อมั่นเต็มที่ (MSTest ที่ทำงานในเครื่อง)
Jay

121
ฉันพลาดอะไรไป เหตุใดจึงเป็นคำตอบที่ยอมรับได้เมื่อไม่ได้ตอบคำถามเฉพาะของการทดสอบวิธีการส่วนตัว InternalsVisibleTo แสดงวิธีการทำเครื่องหมายว่าเป็นภายในเท่านั้นและไม่ใช่วิธีที่ทำเครื่องหมายว่าเป็นส่วนตัวตามที่ร้องขอโดย OP (และเหตุผลที่ฉันลงจอดที่นี่) ฉันเดาว่าฉันจำเป็นต้องใช้ PrivateObject ต่อไปตอบโดย Seven
Darren Lewis

6
@Jay ฉันรู้ว่านี้เป็นบิตปลายมา แต่ทางเลือกหนึ่งคือการใช้สิ่งที่ต้องการ#if RELEASE_TESTไปรอบ ๆเหมือนไมค์แสดงให้เห็นและทำสำเนาของการสร้างการกำหนดค่าการปล่อยของคุณที่กำหนดInternalsVisibleTo RELEASE_TESTคุณจะได้รับการทดสอบรหัสการปล่อยด้วยการปรับให้เหมาะสม แต่เมื่อคุณสร้างเพื่อการปล่อยจริงการทดสอบของคุณจะถูกตัดออก
Shaz

349

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

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

38
ตัวเลือกที่ 2 ทำให้การทดสอบหน่วยต้องมีความรู้เกี่ยวกับการใช้งานฟังก์ชัน ฉันไม่ชอบทำเช่นนั้น โดยทั่วไปฉันคิดว่าการทดสอบหน่วยควรทดสอบฟังก์ชั่นโดยไม่ต้องคิดอะไรเกี่ยวกับการใช้งาน
Herms

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

30
การทดสอบควรจะแตกถ้าคุณเปลี่ยนการใช้งาน TDD จะหมายถึงการเปลี่ยนการทดสอบก่อน
sleske

39
@sleske - ฉันไม่เห็นด้วย หากฟังก์ชั่นยังไม่เปลี่ยนแปลงการทดสอบก็ไม่น่าจะเกิดขึ้นเพราะการทดสอบควรเป็นการทดสอบพฤติกรรม / สถานะไม่ใช่การติดตั้ง นี่คือความหมายของ jtr ที่ทำให้การทดสอบของคุณเปราะบาง ในโลกอุดมคติคุณควรจะสามารถปรับโครงสร้างรหัสของคุณใหม่และให้การทดสอบของคุณยังคงผ่านการตรวจสอบว่าการปรับโครงสร้างของคุณไม่ได้เปลี่ยนการทำงานของระบบของคุณ
Alconja

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

118

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

Microsoft จัดเตรียมสองกลไกสำหรับสิ่งนี้:

accessors

  • ไปที่ซอร์สโค้ดของการกำหนดคลาส
  • คลิกขวาที่ชื่อของคลาส
  • เลือก "สร้างการเข้าถึงส่วนตัว"
  • เลือกโครงการที่จะสร้าง accessor => คุณจะจบด้วยคลาสใหม่ที่มีชื่อ foo_accessor คลาสนี้จะถูกสร้างขึ้นแบบไดนามิกระหว่างการรวบรวมและทำให้สมาชิกทุกคนในที่สาธารณะว่าง

อย่างไรก็ตามกลไกบางครั้งค่อนข้างยากเมื่อพูดถึงการเปลี่ยนแปลงอินเทอร์เฟซของคลาสดั้งเดิม ดังนั้นส่วนใหญ่ฉันหลีกเลี่ยงการใช้นี้

คลาส PrivateObject วิธีอื่นคือใช้ Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject

// Wrap an already existing instance
PrivateObject accessor = new PrivateObject( objectInstanceToBeWrapped );

// Retrieve a private field
MyReturnType accessiblePrivateField = (MyReturnType) accessor.GetField( "privateFieldName" );

// Call a private method
accessor.Invoke( "PrivateMethodName", new Object[] {/* ... */} );

2
คุณจะเรียกใช้วิธีการคงที่ส่วนตัวได้อย่างไร
StuperUser

18
accessors เอกชนจะเลิกใช้ใน Visual Studio 2012
Ryan Gates

3
วิธีการเข้าถึงของการทดสอบวิธีการส่วนตัวถูกเลิกใช้แล้วตั้งแต่ VS 2011 เป็นต้นไป blogs.msdn.com/b/visualstudioalm/archive/2012/03/08/…
Sanjit Misra

1
การอ่านเอกสารที่พบในเว็บไซต์ของ Microsoft ที่นี่ฉันไม่เห็นการกล่าวถึงคลาส PrivateObject ที่เลิกใช้แล้ว ฉันใช้ MSVS 2013 และทำงานได้ตามที่คาดไว้
stackunderflow

3
@RyanGates โซลูชันแรกบนไซต์ที่คุณอ้างถึงว่าวิธีการเข้าถึงสมาชิกส่วนตัวคือ"ใช้คลาส PrivateObject เพื่อช่วยในการเข้าถึง API ภายในและส่วนตัวในรหัสของคุณซึ่งพบได้ใน Microsoft.VisualStudio.QualityTools.UnitTestFramework แอสเซมบลี dll "
stackunderflow

78

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

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


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

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

9
@ พื้นฐาน: ตรรกะที่ผิดอย่างสิ้นเชิงในคำตอบนี้ กรณีคลาสสิกเมื่อคุณต้องการวิธีการส่วนตัวคือเมื่อคุณต้องการรหัสบางส่วนที่จะนำมาใช้ใหม่โดยวิธีสาธารณะ คุณใส่รหัสนี้ใน PrivMethod บางอย่าง วิธีการนี้ไม่ควรเปิดเผยต่อสาธารณะ แต่ต้องมีการทดสอบเพื่อให้มั่นใจว่าวิธีการสาธารณะที่ใช้ PrivMethod สามารถพึ่งพาได้จริง
Dima

6
@Dima แน่นอนถ้ามีปัญหากับPrivMethodการทดสอบPubMethodที่โทรPrivMethodควรเปิดเผยหรือไม่ จะเกิดอะไรขึ้นเมื่อคุณเปลี่ยนSimpleSmtpServiceไปGmailService? ทันทีที่การทดสอบส่วนตัวของคุณชี้ไปที่โค้ดที่ไม่มีอยู่อีกต่อไปหรืออาจทำงานแตกต่างกันและอาจล้มเหลวแม้ว่าแอปพลิเคชันอาจทำงานได้อย่างสมบูรณ์ตามที่ออกแบบไว้ หากมีการประมวลผลที่ซับซ้อนที่จะนำไปใช้กับผู้ส่งอีเมลทั้งสองอาจเป็นสิ่งที่ควรEmailProcessorนำมาใช้กับทั้งสองและทดสอบแยกต่างหาก
พื้นฐาน

2
@miltonb เราอาจจะมองสิ่งนี้จากรูปแบบการพัฒนาที่แตกต่างกัน WRT internals ฉันไม่ได้มีแนวโน้มที่จะทดสอบหน่วยพวกเขา หากมีปัญหา (ตามที่ระบุโดยการทดสอบอินเตอร์เฟส) อาจเป็นเรื่องง่ายที่จะติดตามโดยการแนบดีบักเกอร์หรือคลาสนั้นซับซ้อนเกินไปและควรแยกออก (ด้วยส่วนต่อประสานสาธารณะของหน่วยคลาสที่ทดสอบใหม่) IMHO
พื้นฐาน

51

ในกรณีที่ไม่ค่อยเกิดขึ้นฉันต้องการทดสอบฟังก์ชั่นส่วนตัวฉันมักจะแก้ไขมันเพื่อปกป้องแทนและฉันก็เขียน subclass ด้วยฟังก์ชั่น wrapper สาธารณะ

ห้องเรียน:

...

protected void APrivateFunction()
{
    ...
}

...

คลาสย่อยสำหรับการทดสอบ:

...

[Test]
public void TestAPrivateFunction()
{
    APrivateFunction();
    //or whatever testing code you want here
}

...

1
คุณสามารถใส่คลาสลูกนั้นลงในไฟล์ทดสอบหน่วยของคุณแทนการทำให้ห้องเรียนยุ่งเหยิง +1 สำหรับไหวพริบ
Tim Abell

ฉันมักจะใส่รหัสที่เกี่ยวข้องกับการทดสอบทั้งหมดในโครงการทดสอบหน่วยถ้าเป็นไปได้ นี่เป็นเพียงรหัส psuedo
Jason Jackson

2
ฟังก์ชั่นนี้ไม่ได้เป็นส่วนตัวได้รับการป้องกันผลสุทธิ ... คุณทำให้รหัสของคุณปลอดภัยน้อยลง / สัมผัสกับเด็กประเภทการทำงานส่วนตัว
War

22

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

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

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

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

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

แน่นอนการทดสอบวิธีการส่วนตัวโดยตรงอาจเป็นทางเลือกสุดท้ายหากคุณมีแอปพลิเคชันแบบดั้งเดิม แต่ฉันต้องการให้รหัสเดิมนั้นได้รับการปรับโครงสร้างใหม่เพื่อให้การทดสอบดีขึ้น Michael Feathers ได้เขียนหนังสือยอดเยี่ยมเกี่ยวกับเรื่องนี้มาก http://www.amazon.co.uk/Working-Effectively-Legacy-Robert-Martin/dp/0131177052


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

เหมาะสมในระหว่างการพัฒนาเริ่มต้น แต่คุณต้องการทดสอบวิธีส่วนตัวในชุดการถดถอยมาตรฐานหรือไม่? ถ้าเป็นเช่นนั้นหากการใช้งานมีการเปลี่ยนแปลงก็สามารถทำลายชุดทดสอบ OTOH หากการทดสอบการถดถอยของคุณมุ่งเน้นเฉพาะวิธีการสาธารณะที่มองเห็นจากภายนอกเท่านั้นหากวิธีส่วนตัวหยุดพักในภายหลังชุดการถดถอยควรตรวจพบข้อผิดพลาด จากนั้นหากจำเป็นคุณสามารถปัดฝุ่นการทดสอบส่วนตัวแบบเก่าหากจำเป็น
Alex Blakemore

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

@Big Kahuna - หากคุณคิดว่าไม่มีกรณีที่คุณจำเป็นต้องทดสอบหน่วยวิธีส่วนตัวคุณไม่เคยทำงานกับโครงการขนาดใหญ่ / ซับซ้อนพอ หลายครั้งที่ฟังก์ชั่นสาธารณะเช่นการตรวจสอบความถูกต้องเฉพาะลูกค้าจบลงด้วย 20 บรรทัดเพียงเรียกวิธีการส่วนตัวที่ง่ายมากที่จะทำให้โค้ดอ่านได้ง่ายขึ้น แต่คุณยังต้องทดสอบวิธีการส่วนตัวของแต่ละคน การทดสอบ 20 ครั้งฟังก์ชั่นสาธารณะจะทำให้เป็นเรื่องยากมากที่จะเปิดตัวเมื่อการทดสอบหน่วยล้มเหลว
Pedro.The.Kid

1
ฉันทำงานให้กับ บริษัท FTSE 100 ฉันคิดว่าฉันเคยเห็นโครงการที่ซับซ้อนหลายครั้งในเวลาของฉันขอบคุณ หากคุณต้องการทดสอบในระดับนั้นแต่ละวิธีส่วนตัวในฐานะผู้ทำงานร่วมกันที่แยกต่างหากควรได้รับการทดสอบแยกเพราะมันหมายถึงพวกเขามีพฤติกรรมของแต่ละบุคคลที่ต้องมีการทดสอบ การทดสอบสำหรับผู้ไกล่เกลี่ยวัตถุหลักก็จะกลายเป็นการทดสอบการโต้ตอบ เพียงทดสอบว่ากลยุทธ์ที่ถูกต้องกำลังถูกเรียกใช้สถานการณ์ของคุณดูเหมือนว่าคลาสที่มีปัญหาไม่ได้ติดตาม SRP ไม่มีเหตุผลเดียวที่จะเปลี่ยน แต่ 20 => การละเมิด SRP อ่านหนังสือ GOOS หรือลุงบ๊อบ YMWV
Big Kahuna

17

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

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

ดังนั้นจึงเกิด AccessPrivateWrapper - http://amazedsaint.blogspot.com/2010/05/accessprivatewrapper-c-40-dynamic.html - มันเป็นคลาส wrapper ด่วนที่จะทำให้งานง่ายขึ้นโดยใช้คุณลักษณะและการสะท้อนแบบไดนามิกของ C # 4.0

คุณสามารถสร้างประเภทภายใน / ส่วนตัวเช่น

    //Note that the wrapper is dynamic
    dynamic wrapper = AccessPrivateWrapper.FromType
        (typeof(SomeKnownClass).Assembly,"ClassWithPrivateConstructor");

    //Access the private members
    wrapper.PrivateMethodInPrivateClass();

12

คุณสามารถทดสอบวิธีการส่วนตัวได้สองวิธี

  1. คุณสามารถสร้างอินสแตนซ์ของPrivateObjectคลาสไวยากรณ์ได้ดังนี้

    PrivateObject obj= new PrivateObject(PrivateClass);
    //now with this obj you can call the private method of PrivateCalss.
    obj.PrivateMethod("Parameters");
  2. คุณสามารถใช้การสะท้อน

    PrivateClass obj = new PrivateClass(); // Class containing private obj
    Type t = typeof(PrivateClass); 
    var x = t.InvokeMember("PrivateFunc", 
        BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Public |  
            BindingFlags.Instance, null, obj, new object[] { 5 });

คำตอบที่ดี แต่สำหรับ # 1 ไวยากรณ์ของคุณผิด คุณต้องประกาศตัวอย่างของสิ่งPrivateClassแรกและใช้สิ่งนั้น stackoverflow.com/questions/9122708/…
SharpC

10

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

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


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

9

วิธีการส่วนตัวมี 2 ประเภท วิธีการแบบสแตติกส่วนตัวและวิธีการแบบสแตติกส่วนตัว (วิธีอินสแตนซ์) บทความ 2 บทความต่อไปนี้อธิบายวิธีทดสอบหน่วยวิธีส่วนตัวพร้อมตัวอย่าง

  1. หน่วยทดสอบวิธีการส่วนตัวแบบคงที่
  2. การทดสอบหน่วยวิธีการส่วนตัวแบบไม่คงที่

ให้ตัวอย่างบางส่วนไม่เพียง แต่ให้ลิงค์
vinculis

ดูน่าเกลียด ไม่มี Intellisense ทางออกที่ไม่ดีจาก MS ฉันช็อค!
alerya

วิธีที่ง่ายที่สุดในการทดสอบวิธีการคงส่วนตัว
แกรนท์

8

MS Test มีคุณสมบัติที่ดีในตัวซึ่งทำให้สมาชิกส่วนตัวและวิธีการในโครงการสามารถสร้างไฟล์ที่เรียกว่า VSCodeGenAccessors

[System.Diagnostics.DebuggerStepThrough()]
    [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TestTools.UnitTestGeneration", "1.0.0.0")]
    internal class BaseAccessor
    {

        protected Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject m_privateObject;

        protected BaseAccessor(object target, Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType type)
        {
            m_privateObject = new Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject(target, type);
        }

        protected BaseAccessor(Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType type)
            :
                this(null, type)
        {
        }

        internal virtual object Target
        {
            get
            {
                return m_privateObject.Target;
            }
        }

        public override string ToString()
        {
            return this.Target.ToString();
        }

        public override bool Equals(object obj)
        {
            if (typeof(BaseAccessor).IsInstanceOfType(obj))
            {
                obj = ((BaseAccessor)(obj)).Target;
            }
            return this.Target.Equals(obj);
        }

        public override int GetHashCode()
        {
            return this.Target.GetHashCode();
        }
    }

ด้วยคลาสที่สืบทอดมาจาก BaseAccessor

เช่น

[System.Diagnostics.DebuggerStepThrough()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TestTools.UnitTestGeneration", "1.0.0.0")]
internal class SomeClassAccessor : BaseAccessor
{

    protected static Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType m_privateType = new Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType(typeof(global::Namespace.SomeClass));

    internal SomeClassAccessor(global::Namespace.Someclass target)
        : base(target, m_privateType)
    {
    }

    internal static string STATIC_STRING
    {
        get
        {
            string ret = ((string)(m_privateType.GetStaticField("STATIC_STRING")));
            return ret;
        }
        set
        {
            m_privateType.SetStaticField("STATIC_STRING", value);
        }
    }

    internal int memberVar    {
        get
        {
            int ret = ((int)(m_privateObject.GetField("memberVar")));
            return ret;
        }
        set
        {
            m_privateObject.SetField("memberVar", value);
        }
    }

    internal int PrivateMethodName(int paramName)
    {
        object[] args = new object[] {
            paramName};
        int ret = (int)(m_privateObject.Invoke("PrivateMethodName", new System.Type[] {
                typeof(int)}, args)));
        return ret;
    }

8
ไฟล์ gen'd มีอยู่ใน VS2005 เท่านั้น ในปี 2008 พวกเขาสร้างเบื้องหลัง และพวกมันเป็นตัวย่อ และงาน Shadow ที่เกี่ยวข้องนั้นไม่สม่ำเสมอบน build server
Ruben Bartelink

accessors ถูกเลิกใช้ใน VS2012-2013
Zephan Schroeder

5

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

คุณสามารถค้นหาบทความได้ที่นี่:

http://www.codeproject.com/KB/cs/testnonpublicmembers.aspx


4

ประกาศพวกเขาinternalแล้วใช้InternalsVisibleToAttributeเพื่ออนุญาตให้แอสเซมบลีการทดสอบหน่วยของคุณเพื่อดูพวกเขา


11
ฉันไม่ชอบใช้ InternalsVisibleTo เพราะฉันสร้างวิธีการแบบส่วนตัวด้วยเหตุผล
swilliams

4

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


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

4

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

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

ด้วยเหตุผลนี้คุณควรหลีกเลี่ยงการใช้ InternalsVisibleToAtrribute

นี่คือการพูดคุยที่ยอดเยี่ยมจาก Ian Cooper ซึ่งครอบคลุมหัวข้อนี้: Ian Cooper: TDD มันผิดพลาดที่ไหน


3

บางครั้งอาจเป็นการดีที่จะทดสอบการประกาศส่วนตัว โดยพื้นฐานแล้วคอมไพเลอร์มีวิธีพับลิกเพียงวิธีเดียวเท่านั้น: คอมไพล์ (สตริงเอาต์พุตFileName, พารามิเตอร์สตริง [] sourceSFileNames) ฉันแน่ใจว่าคุณเข้าใจว่าจะเป็นการยากที่จะทดสอบวิธีการดังกล่าวโดยไม่ต้องทดสอบประกาศ "ซ่อน" แต่ละรายการ!

นั่นเป็นเหตุผลที่เราได้สร้าง Visual T #: เพื่อทำการทดสอบง่ายขึ้น มันเป็น. NET ภาษาการเขียนโปรแกรมฟรี (เข้ากันได้กับ C # v2.0)

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

ลองดูที่เว็บไซต์ของเรา: ดาวน์โหลดมันฟรี


3

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

นอกจากนี้ในภาษาสคริปต์ (ด้วยความสามารถของ OO เช่น Python, Ruby และ PHP) คุณสามารถสร้างไฟล์ทดสอบตัวเองเมื่อทำงาน วิธีที่รวดเร็วในการตรวจสอบให้แน่ใจว่าการเปลี่ยนแปลงของคุณไม่ทำลายอะไรเลย สิ่งนี้ทำให้โซลูชันที่ปรับขนาดได้เพื่อทดสอบคลาสของคุณทั้งหมด: เพียงแค่รันมันทั้งหมด (คุณสามารถทำสิ่งนี้ในภาษาอื่นด้วยหลักโมฆะซึ่งจะทำการทดสอบเสมอ)


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

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

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

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

3

ฉันต้องการสร้างตัวอย่างรหัสที่ชัดเจนที่นี่ซึ่งคุณสามารถใช้ในชั้นเรียนใด ๆ ที่คุณต้องการทดสอบวิธีการส่วนตัว

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

  /**
   *
   * @var Class_name_of_class_you_want_to_test_private_methods_in
   * note: the actual class and the private variable to store the 
   * class instance in, should at least be different case so that
   * they do not get confused in the code.  Here the class name is
   * is upper case while the private instance variable is all lower
   * case
   */
  private $class_name_of_class_you_want_to_test_private_methods_in;

  /**
   * This uses reflection to be able to get private methods to test
   * @param $methodName
   * @return ReflectionMethod
   */
  protected static function getMethod($methodName) {
    $class = new ReflectionClass('Class_name_of_class_you_want_to_test_private_methods_in');
    $method = $class->getMethod($methodName);
    $method->setAccessible(true);
    return $method;
  }

  /**
   * Uses reflection class to call private methods and get return values.
   * @param $methodName
   * @param array $params
   * @return mixed
   *
   * usage:     $this->_callMethod('_someFunctionName', array(param1,param2,param3));
   *  {params are in
   *   order in which they appear in the function declaration}
   */
  protected function _callMethod($methodName, $params=array()) {
    $method = self::getMethod($methodName);
    return $method->invokeArgs($this->class_name_of_class_you_want_to_test_private_methods_in, $params);
  }

$ this -> _ callMethod ('_ someFunctionName', อาร์เรย์ (param1, param2, param3));

เพียงแค่ออกพารามิเตอร์ตามลำดับที่ปรากฏในฟังก์ชันส่วนตัวดั้งเดิม


3

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

public class ReflectionTools
{
    // If the class is non-static
    public static Object InvokePrivate(Object objectUnderTest, string method, params object[] args)
    {
        Type t = objectUnderTest.GetType();
        return t.InvokeMember(method,
            BindingFlags.InvokeMethod |
            BindingFlags.NonPublic |
            BindingFlags.Instance |
            BindingFlags.Static,
            null,
            objectUnderTest,
            args);
    }
    // if the class is static
    public static Object InvokePrivate(Type typeOfObjectUnderTest, string method, params object[] args)
    {
        MemberInfo[] members = typeOfObjectUnderTest.GetMembers(BindingFlags.NonPublic | BindingFlags.Static);
        foreach(var member in members)
        {
            if (member.Name == method)
            {
                return typeOfObjectUnderTest.InvokeMember(method, BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.InvokeMethod, null, typeOfObjectUnderTest, args);
            }
        }
        return null;
    }
}

จากการทดสอบจริงคุณสามารถทำสิ่งนี้:

Assert.AreEqual( 
  ReflectionTools.InvokePrivate(
    typeof(StaticClassOfMethod), 
    "PrivateMethod"), 
  "Expected Result");

Assert.AreEqual( 
  ReflectionTools.InvokePrivate(
    new ClassOfMethod(), 
    "PrivateMethod"), 
  "Expected Result");

2

MbUnit มีเสื้อคลุมที่ดีสำหรับตัวสะท้อนแสงนี้

Reflector dogReflector = new Reflector(new Dog());
dogReflector.Invoke("DreamAbout", DogDream.Food);

คุณยังสามารถตั้งค่าและรับค่าจากคุณสมบัติได้

dogReflector.GetProperty("Age");

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


4
เพียงเพื่อข้อมูลReflectorถูกแทนที่ด้วยพลังที่มากขึ้นMirrorใน Gallio / MbUnit v3.2 ( gallio.org/wiki/doku.php?id=mbunit:mirror )
Yann Trevin

2

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


2

ในความคิดของฉันคุณควรทดสอบหน่วย API สาธารณะของคลาสของคุณเท่านั้น

ทำให้วิธีการสาธารณะเพื่อทดสอบหน่วยแบ่ง encapsulation เปิดเผยรายละเอียดการใช้งาน

API สาธารณะที่ดีจะแก้เป้าหมายทันทีของรหัสลูกค้าและแก้ไขเป้าหมายนั้นได้อย่างสมบูรณ์


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

2

ฉันใช้คลาสPrivateObject แต่ดังที่กล่าวไว้ก่อนหน้านี้ดีกว่าเพื่อหลีกเลี่ยงการทดสอบวิธีการส่วนตัว

Class target = new Class();
PrivateObject obj = new PrivateObject(target);
var retVal = obj.Invoke("PrivateMethod");
Assert.AreEqual(retVal);

2
CC -Dprivate=public

"CC" เป็นคอมไพเลอร์บรรทัดคำสั่งในระบบที่ฉันใช้ ไม่เทียบเท่า-Dfoo=bar #define foo barดังนั้นตัวเลือกการรวบรวมนี้จะเปลี่ยนสิ่งส่วนตัวทั้งหมดให้เป็นสาธารณะได้อย่างมีประสิทธิภาพ


2
นี่คืออะไร? สิ่งนี้ใช้ได้กับ Visual Studio หรือไม่
YeahStu

"CC" เป็นคอมไพเลอร์บรรทัดคำสั่งในระบบที่ฉันใช้ "-Dfoo = bar" ทำหน้าที่เทียบเท่ากับ "#define foo bar" ดังนั้นตัวเลือกการรวบรวมนี้จะเปลี่ยนสิ่งส่วนตัวทั้งหมดให้เป็นสาธารณะได้อย่างมีประสิทธิภาพ ฮ่า!
Mark Harrison

ใน Visual Studio ตั้งค่ากำหนดในสภาพแวดล้อมการสร้างของคุณ
Mark Harrison

1

นี่คือตัวอย่างแรกลงนามวิธีการ:

private string[] SplitInternal()
{
    return Regex.Matches(Format, @"([^/\[\]]|\[[^]]*\])+")
                        .Cast<Match>()
                        .Select(m => m.Value)
                        .Where(s => !string.IsNullOrEmpty(s))
                        .ToArray();
}

นี่คือการทดสอบ:

/// <summary>
///A test for SplitInternal
///</summary>
[TestMethod()]
[DeploymentItem("Git XmlLib vs2008.dll")]
public void SplitInternalTest()
{
    string path = "pair[path/to/@Key={0}]/Items/Item[Name={1}]/Date";
    object[] values = new object[] { 2, "Martin" };
    XPathString xp = new XPathString(path, values);

    PrivateObject param0 = new PrivateObject(xp);
    XPathString_Accessor target = new XPathString_Accessor(param0);
    string[] expected = new string[] {
        "pair[path/to/@Key={0}]",
        "Items",
        "Item[Name={1}]",
        "Date"
    };
    string[] actual;
    actual = target.SplitInternal();
    CollectionAssert.AreEqual(expected, actual);
}

1

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


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

1

1) ถ้าคุณมีรหัสดั้งเดิมวิธีเดียวที่จะทดสอบวิธีการส่วนตัวก็คือการไตร่ตรอง

2) หากเป็นรหัสใหม่คุณจะมีตัวเลือกดังต่อไปนี้:

  • ใช้การสะท้อน (ไปที่ซับซ้อน)
  • เขียนการทดสอบหน่วยในระดับเดียวกัน (ทำให้รหัสการผลิตน่าเกลียดโดยมีรหัสการทดสอบอยู่ในนั้น)
  • Refactor และทำให้วิธีการเป็นสาธารณะในชั้นเรียน util บางชนิด
  • ใช้คำอธิบายประกอบ @VisibleForTesting และลบส่วนตัว

ฉันชอบวิธีการใส่คำอธิบายอย่างง่ายและซับซ้อนที่สุด ปัญหาเดียวคือเราเพิ่มการมองเห็นซึ่งฉันคิดว่าไม่ใช่เรื่องใหญ่ เราควรเข้ารหัสให้อินเทอร์เฟซดังนั้นถ้าเรามีอินเทอร์เฟซ MyService และการใช้งาน MyServiceImpl เราสามารถมีคลาสการทดสอบที่สอดคล้องกันนั่นคือ MyServiceTest (วิธีทดสอบอินเทอร์เฟซ) และ MyServiceImplTest (ทดสอบวิธีส่วนตัว) ลูกค้าทุกคนควรใช้อินเทอร์เฟซดังนั้นในลักษณะที่แม้ว่าการมองเห็นของวิธีส่วนตัวได้รับการเพิ่มขึ้นมันไม่ควรสำคัญ


1

คุณสามารถประกาศเป็นสาธารณะหรือภายใน (ด้วย InternalsVisibleToAttribute) ในขณะที่สร้างในโหมดดีบัก:

    /// <summary>
    /// This Method is private.
    /// </summary>
#if DEBUG
    public
#else
    private
#endif
    static string MyPrivateMethod()
    {
        return "false";
    }

มันขยายโค้ด แต่จะอยู่privateในรุ่นบิลด์


0

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

http://msdn.microsoft.com/en-us/library/bb385974.aspx


0

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


2
ไม่มีInternalsVisibleToAttributeไม่ไม่ จำเป็นต้องมีที่ประกอบคุณจะตั้งชื่ออย่างยิ่ง ขณะนี้ฉันใช้มันในโครงการที่ไม่เป็นเช่นนั้น
Cody Gray

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