คุณจะจำลองระบบไฟล์ใน C # สำหรับการทดสอบหน่วยได้อย่างไร


149

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


1
ลักษณะเช่นนี้ซ้ำกับคนอื่น ๆ หลายแห่งรวมถึง: stackoverflow.com/questions/664277/...
จอห์นแซนเดอ

อาจจะลองมองเข้าไปใน PEX ( research.microsoft.com/en-us/projects/pex/filesystem.pdf )
Tinus

2
@ Mitch: ส่วนใหญ่แล้วก็เพียงพอที่จะวางข้อมูลในระบบไฟล์และให้การทดสอบหน่วยรันหลักสูตรของพวกเขา อย่างไรก็ตามฉันพบวิธีการที่ใช้งานการดำเนินการ IO หลายอย่างและการตั้งค่าสภาพแวดล้อมการทดสอบสำหรับวิธีการดังกล่าวนั้นง่ายขึ้นมากโดยใช้ระบบไฟล์จำลอง
Steve Guidi

ฉันเขียนgithub.com/guillaume86/VirtualPathเพื่อจุดประสงค์นั้น (และอื่น ๆ ) มันยังคงเป็น WIP และ API จะเปลี่ยนแปลงอย่างแน่นอน แต่ก็ใช้งานได้แล้วและมีการทดสอบบางอย่างรวมอยู่ด้วย
Guillaume86

คำตอบ:


154

แก้ไข: System.IO.Abstractionsการติดตั้งแพคเกจ

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

คุณสามารถทำได้โดยสร้างอินเทอร์เฟซ:

interface IFileSystem {
    bool FileExists(string fileName);
    DateTime GetCreationDate(string fileName);
}

และการสร้างการใช้งาน 'ของจริง' ซึ่งใช้ System.IO.File.Exists () ฯลฯ จากนั้นคุณสามารถจำลองอินเตอร์เฟสนี้โดยใช้กรอบการทำงานที่เยาะเย้ย ผมขอแนะนำขั้นต่ำ

แก้ไข: ใครบางคนทำแบบนี้และโพสต์ออนไลน์กรุณาที่นี่

ฉันใช้วิธีนี้เพื่อเยาะเย้ย DateTime.UtcNow ในอินเทอร์เฟซ IClock (มีประโยชน์จริง ๆ สำหรับการทดสอบของเราเพื่อให้สามารถควบคุมการไหลของเวลา!)

อีกวิธีหนึ่งอาจใช้TypeMockซึ่งจะช่วยให้คุณสามารถสกัดกั้นการโทรไปยังคลาสต่างๆ สิ่งนี้จะมีค่าใช้จ่ายและจะต้องติดตั้งบนพีซีทั้งหมดของทีมของคุณและเซิร์ฟเวอร์บิลด์ของคุณเพื่อให้ทำงานได้เช่นกันดูเหมือนว่ามันจะไม่ทำงานสำหรับ System.IO.File เนื่องจากมันไม่สามารถ stub mscorlibได้

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


1
ในความคิดของฉันการสร้างอินเทอร์เฟซตามที่ Matt อธิบายที่นี่เป็นวิธีที่จะไป ฉันยังได้เขียนเครื่องมือที่สร้างอินเทอร์เฟซดังกล่าวให้คุณซึ่งมีประโยชน์เมื่อพยายามที่จะจำลองชั้นเรียนแบบคงที่และ / หรือปิดผนึกหรือวิธีการที่ไม่ได้กำหนดไว้ล่วงหน้า ดูjolt.codeplex.comสำหรับข้อมูลเพิ่มเติม
Steve Guidi

ดูเหมือนว่าธุรกรรมซื้อคืนในบทความที่อ้างอิงถูกลบ / ย้ายโดยไม่ต้องแจ้งให้ทราบล่วงหน้า อย่างไรก็ตามดูเหมือนจะมีชุดของความพยายามของมันที่นี่: nuget.org/packages/mscorlib-mock
Mike-E

Typemock มีข้อ จำกัด เกี่ยวกับประเภทที่สามารถปลอมแปลงได้ (อย่างน้อยในรุ่นปัจจุบัน ณ เดือนตุลาคม 2017) คุณสามารถปลอมคลาส File static ได้อย่างแน่นอน ฉันเพิ่งยืนยันตัวเอง
Ryan Rodemoyer

คุณสามารถสรุปชุดทดสอบการรวมบางส่วนได้หรือไม่
Ozkan

83

Install-Package System.IO.Abstractions

ไลบรารีจินตภาพนี้มีอยู่ในขณะนี้มีแพ็คเกจ NuGet สำหรับSystem.IO.Abstractionsซึ่งย่อส่วนเนมสเปซ System.IO

นอกจากนี้ยังมีชุดของผู้ช่วยทดสอบ System.IO.Abstractions การทดสอบผู้ช่วยซึ่ง - ในขณะที่เขียน - จะดำเนินการเพียงบางส่วนเท่านั้น แต่เป็นจุดเริ่มต้นที่ดีมาก


3
ฉันคิดว่าการทำให้เป็นมาตรฐานรอบสิ่งที่เป็นนามธรรมที่สร้างไว้แล้วนี้เป็นทางออกที่ดีที่สุด ไม่เคยได้ยินเกี่ยวกับห้องสมุดนี้เลยขอบคุณมากสำหรับหัวขึ้น
julealgon

PM ย่อมาจากตัวจัดการแพคเกจ .. เพื่อเปิด ... เครื่องมือ> ตัวจัดการแพคเกจ NuGet> คอนโซลตัวจัดการแพคเกจ
thedanotto

11

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

ตัวอย่าง:

interface IFileWrapper { bool Exists(String filePath); }

class FileWrapper: IFileWrapper
{
    bool Exists(String filePath) { return File.Exists(filePath); }        
}

class FileWrapperStub: IFileWrapper
{
    bool Exists(String filePath) 
    { return (filePath == @"C:\myfilerocks.txt"); }
}

5

คำแนะนำของฉันคือการใช้http://systemwrapper.codeplex.com/ เนื่องจากมีการห่อสำหรับประเภทที่ใช้ส่วนใหญ่ใน System namespace


ขณะนี้ฉันใช้ห้องสมุดนี้และตอนนี้ฉันได้ค้นพบว่า abstractions ของสิ่งต่าง ๆ เช่น FileStream ไม่รวม IDisposable ฉันกำลังมองหาการทดแทน หากห้องสมุดไม่อนุญาตให้ฉันจัดการกระแสข้อมูลอย่างเหมาะสมฉันไม่สามารถแนะนำ (หรือใช้) เพื่อจัดการกับการดำเนินการเหล่านั้นได้
James Nail

1
IFileStreamWrap ของ SystemWrapper ใช้ IDisposable ทันที
tster

systemwrapper เป็น. net framework เท่านั้นจะทำให้เกิดปัญหาแปลก ๆ หากใช้กับ. netcore
Adil H. Raza

3

ฉันเจอวิธีแก้ไขปัญหาต่อไปนี้:

  • เขียนการทดสอบการรวมเข้าด้วยกันไม่ใช่การทดสอบหน่วย ในการทำงานคุณต้องมีวิธีง่ายๆในการสร้างโฟลเดอร์ที่คุณสามารถถ่ายโอนข้อมูลโดยไม่ต้องกังวลเกี่ยวกับการทดสอบอื่น ๆ ที่รบกวน ฉันมีคลาสTestFolderง่าย ๆซึ่งสามารถสร้างโฟลเดอร์วิธีทดสอบที่ไม่ซ้ำกันต่อการใช้งาน
  • เขียนคำเยาะเย้ย System.IO.File นั่นคือการสร้างIFile.cs ฉันพบว่าการใช้สิ่งนี้มักจะจบลงด้วยการทดสอบที่เพียงพิสูจน์ว่าคุณสามารถเขียนคำสั่งการเยาะเย้ย แต่จะใช้เมื่อการใช้งาน IO มีขนาดเล็ก
  • ตรวจสอบเลเยอร์ abstraction ของคุณและแตกไฟล์ IO ออกจากคลาส สร้างอินเตอร์เฟสสำหรับสิ่งนี้ ส่วนที่เหลือจะใช้การทดสอบการรวม (แต่จะมีขนาดเล็กมาก) สิ่งนี้แตกต่างจากด้านบนแทนที่จะทำไฟล์อ่านว่าคุณเขียนเจตจำนงแล้วพูดว่า ioThingie.loadSettings ()
  • System.IO.Abstractions ฉันยังไม่ได้ใช้มัน แต่เป็นสิ่งที่ฉันตื่นเต้นที่สุดเกี่ยวกับการเล่นด้วย

ฉันใช้วิธีทั้งหมดข้างต้นขึ้นอยู่กับสิ่งที่ฉันเขียน แต่ส่วนใหญ่ฉันคิดว่านามธรรมเป็นสิ่งที่ผิดเมื่อฉันเขียนการทดสอบหน่วยที่ตี IO


4
ลิงก์ไปยัง IFile.cs ใช้งานไม่ได้
Mike-E

3

โดยใช้System.IO.AbstractionsและSystem.IO.Abstractions.TestingHelpersเช่นนั้น:

public class ManageFile {
   private readonly IFileSystem _fileSystem;
   public ManageFile(IFileSystem fileSystem){

      _fileSystem = fileSystem;
   }

   public bool FileExists(string filePath){}
       if(_fileSystem.File.Exists(filePath){
          return true;
       }
       return false;
   }
}

ในคลาสทดสอบของคุณคุณใช้ MockFileSystem () เพื่อจำลองไฟล์และคุณได้ติดตั้ง ManageFile เช่น:

var mockFileSysteme = new MockFileSystem();
var mockFileData = new MockFileData("File content");
mockFileSysteme.AddFile(mockFilePath, mockFileData );
var manageFile = new ManageFile(mockFileSysteme);

2

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

ขั้นแรกสร้างแอสเซมบลีปลอมสำหรับ System.dll - หรือแพคเกจอื่น ๆ จากนั้นจำลองที่คาดว่าจะได้รับผลตอบแทนเช่นเดียวกับใน:

using Microsoft.QualityTools.Testing.Fakes;
...
using (ShimsContext.Create())
{
     System.IO.Fakes.ShimFile.ExistsString = (p) => true;
     System.IO.Fakes.ShimFile.ReadAllTextString = (p) => "your file content";

      //Your methods to test
}

1

มันจะเป็นการยากที่จะเยาะเย้ยระบบไฟล์ในการทดสอบเนื่องจาก API ไฟล์. NET นั้นไม่ได้ขึ้นอยู่กับอินเทอร์เฟซหรือคลาสที่สามารถขยายได้ที่อาจถูกเยาะเย้ย

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

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


1

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

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


1

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

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

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

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


1

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


0

วิธีแก้ปัญหาทั่วไปคือการใช้ API ระบบไฟล์นามธรรม (เช่นApache Commons VFSสำหรับ Java): แอปพลิเคชันตรรกะทั้งหมดใช้ API และการทดสอบหน่วยสามารถจำลองระบบไฟล์จริงด้วยการใช้งานสตับ (การจำลองในหน่วยความจำหรืออะไรทำนองนั้น)

สำหรับ C # มี API ที่คล้ายกัน: NI.Vfsซึ่งคล้ายกับ Apache VFS V1 มาก มันมีการใช้งานเริ่มต้นทั้งสำหรับระบบไฟล์ในท้องถิ่นและระบบไฟล์ในหน่วยความจำ (ล่าสุดสามารถใช้ในการทดสอบหน่วยจากกล่อง)


-1

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


-2

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

อีกวิธีหนึ่งคือการห่อวิธีการเสริมด้วยสิ่งที่เยาะเย้ย เช่นสร้างคลาสที่เรียกว่า FileWrapper ที่อนุญาตให้เข้าถึงวิธีการของไฟล์ แต่เป็นสิ่งที่คุณสามารถเยาะเย้ยได้

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