ตัวดัดแปลงการเข้าถึง C #“ ภายใน” เมื่อทำการทดสอบหน่วย


469

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


1
คุณควรหลีกเลี่ยงความต้องการหน่วยทดสอบวิธีการภายในของคุณโดยใช้System.Diagnostics.Debug.Assert()วิธีการต่าง ๆ ด้วยตนเอง
Mike Marynowski

คำตอบ:


1195

คลาสภายในต้องได้รับการทดสอบและมีแอตทริบิวต์ assemby:

using System.Runtime.CompilerServices;

[assembly:InternalsVisibleTo("MyTests")]

Properties\AssemblyInfo.csเพิ่มไปยังแฟ้มข้อมูลโครงการเช่น


70
เพิ่มไปยังโครงการภายใต้การทดสอบ (เช่นใน Properties \ AssemblyInfo.cs) "MyTests" จะเป็นชุดทดสอบ
EricSchaefer

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

7
มีการพูดคุยกันอย่างมากระหว่าง @DerickBailey และ Dan Tao เกี่ยวกับความแตกต่างทางความหมายระหว่างภายในและส่วนตัวและความต้องการในการทดสอบส่วนประกอบภายใน คุ้มค่ากับการอ่าน
Kris McGinnes

31
ห่อและ#if DEBUG, #endifบล็อกจะใช้ตัวเลือกนี้เฉพาะในการแก้ปัญหาการสร้าง
The Edward Cullen ตัวจริง

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

127

หากคุณต้องการทดสอบวิธีการส่วนตัวให้ดูที่PrivateObjectและPrivateTypeในMicrosoft.VisualStudio.TestTools.UnitTestingเนมสเปซ พวกเขาเสนอห่อหุ้มที่ใช้งานง่ายรอบ ๆ รหัสการสะท้อนที่จำเป็น

เอกสาร: PrivateType , PrivateObject

สำหรับ VS2017 & 2019 คุณสามารถค้นหาสิ่งเหล่านี้ได้ด้วยการดาวน์โหลด MSTest.TestFramework nuget


15
เมื่อลงคะแนนเสียงกรุณาแสดงความคิดเห็น ขอบคุณ
Brian Rasmussen

35
คำตอบนี้โง่มาก มันชี้ไปที่ทางออกใหม่และดีจริง ๆ ไม่ได้กล่าวถึงก่อน
Ignacio Soler Garcia

เห็นได้ชัดว่ามีปัญหาบางอย่างที่ใช้ TestFramework สำหรับการกำหนดเป้าหมายแอป. net2.0 หรือใหม่กว่า: github.com/Microsoft/testfx/issues/366
Johnny Wu

41

เมื่อเพิ่มคำตอบของ Eric คุณสามารถกำหนดค่านี้ในcsprojไฟล์:

<ItemGroup>
    <AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
      <_Parameter1>MyTests</_Parameter1>
    </AssemblyAttribute>
</ItemGroup>

หรือถ้าคุณมีหนึ่งโครงการทดสอบต่อโครงการที่จะทดสอบคุณสามารถทำสิ่งนี้ในDirectory.Build.propsไฟล์ของคุณ:

<ItemGroup>
    <AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
      <_Parameter1>$(MSBuildProjectName).Test</_Parameter1>
    </AssemblyAttribute>
</ItemGroup>

ดู: https://stackoverflow.com/a/49978185/1678053
ตัวอย่าง: https://github.com/gldraphael/evlog/blob/master/Directory.Build.props#L5-L12


2
นี่ควรเป็นคำตอบที่ดีที่สุด คำตอบอื่น ๆ ทั้งหมดล้าสมัยมากเนื่องจาก. net กำลังเคลื่อนห่างจากแอสเซมบลีข้อมูลและย้ายฟังก์ชันการทำงานเป็นคำจำกัดความ csproj
mBrice1024

12

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

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

สิ่งหนึ่งที่ควรพิจารณาอาจเป็นคำต่อท้าย "ForTest":

internal void DoThisForTest(string name)
{
    DoThis(name);
}

private void DoThis(string name)
{
    // Real implementation
}

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


2
หากวิธีนี้เป็นแบบภายในสิ่งนี้จะไม่ขัดขวางการใช้งานจากชุดทดสอบหรือไม่?
Ralph Shillington

7
ฉันใช้ForTestวิธีการเป็นครั้งคราวแต่ฉันพบว่ามันน่าเกลียดอยู่เสมอ (การเพิ่มโค้ดซึ่งไม่ได้ให้คุณค่าที่แท้จริงในแง่ของตรรกะทางธุรกิจการผลิต) ฉันมักจะพบว่าฉันต้องใช้วิธีการเพราะการออกแบบเป็นสิ่งที่โชคร้าย (เช่นต้องรีเซ็ตอินสแตนซ์เดี่ยวระหว่างการทดสอบ)
ChrisWue

1
ล่อลวงให้โหวตสิ่งนี้ - อะไรคือความแตกต่างระหว่างการแฮ็กนี้กับการทำให้ชั้นเรียนเป็นแบบภายในแทนที่จะเป็นแบบส่วนตัว อย่างน้อยก็มีเงื่อนไขการรวบรวม จากนั้นมันจะยุ่งจริงๆ
CAD bloke

6
@CADbloke: คุณหมายถึงการทำให้วิธีการภายในเป็นแบบส่วนตัวหรือเปล่า? ความแตกต่างคือชัดเจนว่าคุณต้องการให้เป็นส่วนตัว รหัสใด ๆ ภายใน codebase การผลิตของคุณซึ่งเรียกวิธีการที่มีForTestเป็นอย่างเห็นได้ชัดที่ไม่ถูกต้องในขณะที่ถ้าคุณเพียงแค่ทำให้วิธีการที่ภายในดูเหมือนว่ามันอย่างดีกับการใช้งาน
Jon Skeet

2
@CADbloke: คุณสามารถยกเว้นแต่ละวิธีในการสร้างรีลีสได้อย่างง่ายดายในไฟล์เดียวกับการใช้คลาส IMO บางส่วน และถ้าคุณทำอย่างนั้นแสดงว่าคุณไม่ได้ทำการทดสอบกับงานสร้างของคุณซึ่งฟังดูไม่ดีสำหรับฉัน
Jon Skeet

11

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

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

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


3

ฉันใช้Dotnet 3.1.101และส่วน.csprojเพิ่มเติมที่ใช้งานได้สำหรับฉันคือ:

<PropertyGroup>
  <!-- Explicitly generate Assembly Info -->
  <GenerateAssemblyInfo>true</GenerateAssemblyInfo>
</PropertyGroup>

<ItemGroup>
  <AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleToAttribute">
  <_Parameter1>MyProject.Tests</_Parameter1>
  </AssemblyAttribute>
</ItemGroup>

หวังว่านี่จะช่วยให้ใครบางคนที่นั่น!


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