การสร้างโค้ดภายใน แต่ใช้สำหรับการทดสอบหน่วยจากโครงการอื่น


129

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


คำตอบ:


205

หากคุณใช้. NET แอตทริบิวต์แอสเซมบลีInternalsVisibleToช่วยให้คุณสร้างแอสเซมบลี "เพื่อน" แอสเซมบลีเหล่านี้มีชื่อเฉพาะอย่างยิ่งที่ได้รับอนุญาตให้เข้าถึงคลาสภายในและสมาชิกของแอสเซมบลีอื่น

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

ตัวอย่าง:

[assembly: InternalsVisibleTo("NameAssemblyYouWantToPermitAccess")]
namespace NameOfYourNameSpace
{

23
ฉันขอแนะนำให้ใส่ #if DEBUG รอบแอตทริบิวต์แล้วทดสอบหน่วยในการแก้ปัญหา ด้วยวิธีนี้คุณจะมั่นใจได้ว่าแอตทริบิวต์นั้นไม่ได้ตั้งค่าไว้ในรหัสเผยแพร่
สตีฟทำอาหาร

นี่เป็นเพียงความคิดฉันไม่ทราบ .... วิธีการเกี่ยวกับ: #if DEBUG คลาสสาธารณะ IniReader #else ภายในคลาส IniReader #endif อาจไม่แนะนำใช่หรือไม่ ทำไม?
กรกฎาคม

4
ทำไมการ จำกัด การทดสอบเพื่อแก้ไขข้อบกพร่องในการสร้าง
Marco Mp

2
ยิ่งไปกว่านั้น Nitpick ไม่จำเป็นต้องมีชุดประกอบ "เพื่อน" ที่ชื่อ (อาจเป็นวิธีปฏิบัติที่ดี - แม้ว่ารสนิยมส่วนตัวของฉันจะบอกเป็นอย่างอื่น แต่ไม่ใช่ข้อบังคับ)
Marco Mp

9
ไม่เห็นด้วย ถ้าฉันกำลังสร้างองค์ประกอบที่ซับซ้อนด้วย API สาธารณะที่บางมากมันไม่ได้ผลและไม่สมจริงที่จะทดสอบผ่าน API สาธารณะเท่านั้น คุณจะจบลงด้วยลูกบอลโคลนที่ไม่มีใครรักษา แต่ฉันจะกำหนดหน่วยภายในและทดสอบแยกต่างหาก ดังที่เจเรมีดี. มิลเลอร์พูดค่อนข้างบ่อย: "ทดสอบเล็ก ๆ ก่อนที่คุณจะทดสอบใหญ่"
Dennis Doomen

6

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

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

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

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


2
รายละเอียดการปฏิบัติควรใช้เป็นส่วนหนึ่งของการทดสอบที่ครอบคลุม อย่ามองตัวแปรส่วนตัว ... ทดสอบพฤติกรรมที่คาดหวัง หากการทดสอบนั้นถูกต้อง .. การประปาและสายไฟภายในทั้งหมดควรได้รับการทดสอบซึ่งเป็นส่วนหนึ่งของมัน โหวตขึ้น
Gishu

69
i dont จำเป็นต้องเห็นด้วยกับเรื่องนี้เป็นชั้นเรียนเหล่านี้เป็น "สาธารณะ" เพื่อการเรียนอื่น ๆ ภายใน DLL ที่และการทำงานของชั้นควรจะทดสอบ indepdently
leora

27
ฉันไม่เห็นด้วย หน่วยเป็นหน่วยและจะต้องทดสอบแยก
Sentinel

5
หลักเกณฑ์ทั่วไปที่ดี แต่อย่าให้เชื่อมั่นเลย การทดสอบมีจุดประสงค์หลักสองประการ: 1) การถดถอย - ทำให้แน่ใจว่าคุณไม่ได้ทำลายอะไรเลย 2) การเพิ่มความเร็วของการพัฒนา ถ้าฉันต้องยืนขึ้นบริการขนาดใหญ่ทุกครั้งที่ฉันต้องการเขียนบรรทัดของรหัสฉันจะขัดขวางการพัฒนา ถ้าฉันมีชิ้นส่วนภายในที่ซับซ้อนฉันต้องการที่จะสามารถพัฒนาและทดสอบแยกได้ ในทางกลับกันฉันไม่ต้องการทุกสิ่งที่ผ่านการทดสอบแยก (เช่นลดค่าการทดสอบการถดถอยหรือทำซ้ำรหัสทดสอบ) แต่บางรหัสแสดงเหตุผลว่ามีการแยก บางทีกุญแจสำคัญคือการทำให้มันเป็นแอสเซมบลีที่แยกต่างหาก
Lee Jensen

2
OP ที่นี่ถูกต้อง คุณกำลังทดสอบการใช้งานภายในของคุณ ดังนั้นทีมของคุณจึงเป็นทาสในการแก้ไขการทดสอบและการเยาะเย้ยรหัสที่น่ากลัว ทดสอบพับลิก API เท่านั้นไม่ว่าจะเป็น lib API หรือ API ที่เปิดเผยของเครือข่าย รหัสทั้งหมดควรออกกำลังกายผ่านประตูหน้า การใช้งานการทดสอบเป็นสาเหตุที่ TDD เสียชีวิตเป็นส่วนใหญ่มันเป็นเพียง PITA ขนาดใหญ่ที่จะทดสอบตัวอักษรทุกคลาสของแอพทั้งหมดแทนที่จะมุ่งเน้นไปที่การทำให้มั่นใจพฤติกรรมของระบบ ฉันคิดว่าเกือบทุกคนรวมถึงหนังสือ "เผด็จการ" มีความผิดหรือไม่ชัดเจน
Luke Puplett

4

เพื่อวัตถุประสงค์ด้านเอกสาร

หรือคุณสามารถสร้างอินสแตนซ์ของคลาสภายในโดยใช้Type.GetTypeวิธีการ

ตัวอย่าง

//IServiceWrapper is public class which is 
//the same assembly with the internal class 
var asm = typeof(IServiceWrapper).Assembly;
//Namespace.ServiceWrapper is internal
var type = asm.GetType("Namespace.ServiceWrapper");
return (IServiceWrapper<T>)Activator
    .CreateInstance(type, new object[1] { /*constructor parameter*/ });

สำหรับประเภททั่วไปมีกระบวนการที่แตกต่างกันเป็นร้อง:

var asm = typeof(IServiceWrapper).Assembly;
//note the name Namespace.ServiceWrapper`1
//this is for calling Namespace.ServiceWrapper<>
var type = asm.GetType("Namespace.ServiceWrapper`1");
var genType = type.MakeGenericType(new Type[1] { typeof(T) });
return (IServiceWrapper<T>)Activator
     .CreateInstance(genType, new object[1] { /*constructor parameter*/});

-5

ชั้นเรียนสามารถเป็นได้ทั้งสาธารณะและปิดผนึก

แต่อย่าทำอย่างนั้น

คุณสามารถสร้างเครื่องมือเพื่อสะท้อนชั้นเรียนภายในและปล่อยคลาสใหม่ที่เข้าถึงทุกสิ่งผ่านการสะท้อนกลับ MSTest ทำเช่นนั้น

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


1
รออะไร? คุณกำลังพูดว่าอย่าทำpublic sealed classอะไร เหตุผลของคุณสำหรับอัญมณีนั้นคืออะไร?
บดขยี้

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