Làm thế nào để bạn giả lập hệ thống tập tin trong C # để kiểm tra đơn vị?


149

Có thư viện hoặc phương pháp nào để giả lập hệ thống tệp trong C # để viết bài kiểm tra đơn vị không? Trong trường hợp hiện tại của tôi, tôi có các phương pháp kiểm tra xem một số tệp nhất định có tồn tại hay không và đọc ngày tạo. Tôi có thể cần nhiều hơn thế trong tương lai.


1
Điều này trông giống như một bản sao của một số người khác, bao gồm: stackoverflow.com/questions/664277/ ,.
John Saunders


2
@Mitch: Hầu hết thời gian, việc đặt dữ liệu vào hệ thống tệp là đủ và để các bài kiểm tra đơn vị chạy khóa học của họ. Tuy nhiên, tôi đã gặp các phương thức thực thi nhiều thao tác IO và thiết lập môi trường kiểm tra cho các phương thức đó được đơn giản hóa rất nhiều bằng cách sử dụng hệ thống tệp giả.
Steve Guidi

Tôi đã viết github.com/guillaume86/VirtualPath cho mục đích đó (và hơn thế nữa), đó vẫn là WIP và API chắc chắn sẽ thay đổi nhưng nó đã hoạt động và bao gồm một số thử nghiệm.
Guillaume86

Câu trả lời:


154

Chỉnh sửa: Cài đặt gói NuGet System.IO.Abstractions .

Gói này không tồn tại khi câu trả lời này ban đầu được chấp nhận. Câu trả lời ban đầu được cung cấp cho bối cảnh lịch sử dưới đây:

Bạn có thể làm điều đó bằng cách tạo một giao diện:

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

và tạo một triển khai 'thực' sử dụng System.IO.File.Exists (), v.v. Sau đó, bạn có thể mô phỏng giao diện này bằng khung mô phỏng; Tôi đề nghị Moq .

Chỉnh sửa: ai đó đã làm điều này và vui lòng đăng nó trực tuyến ở đây .

Tôi đã sử dụng phương pháp này để mô phỏng DateTime.UtcNow trong giao diện IClock (thực sự rất hữu ích cho thử nghiệm của chúng tôi để có thể kiểm soát dòng thời gian!), Và theo truyền thống hơn, giao diện ISqlDataAccess.

Một cách tiếp cận khác có thể là sử dụng TypeMock , điều này cho phép bạn chặn các cuộc gọi đến các lớp và loại bỏ chúng. Tuy nhiên, điều này sẽ tốn tiền và cần phải được cài đặt trên PC của cả nhóm và máy chủ xây dựng của bạn để chạy, ngoài ra, nó dường như sẽ không hoạt động cho System.IO.File, vì nó không thể làm hỏng mscorlib .

Bạn cũng có thể chấp nhận rằng các phương thức nhất định không thể kiểm tra được đơn vị và kiểm tra chúng trong bộ kiểm tra tích hợp / hệ thống chạy chậm riêng biệt.


1
Theo tôi, tạo ra một giao diện như Matt mô tả ở đây là cách để đi. Tôi thậm chí đã viết một công cụ tạo các giao diện như vậy cho bạn, rất hữu ích khi cố gắng mô phỏng các lớp tĩnh và / hoặc các lớp kín hoặc các phương thức không xác định (ví dụ: đồng hồ và trình tạo số ngẫu nhiên). Xem jolt.codeplex.com để biết thêm thông tin.
Steve Guidi

Có vẻ như repo trong bài viết được tham chiếu đã bị xóa / di chuyển mà không cần thông báo trước. Tuy nhiên, dường như có một gói nuget
Mike-E

Typemock không có hạn chế về loại nào có thể giả được nhưng (ít nhất là trong phiên bản hiện tại kể từ tháng 10 năm 2017), bạn chắc chắn có thể giả mạo lớp tĩnh Tệp. Tôi chỉ xác minh điều này bản thân mình.
Ryan Rodemoyer

Bạn có thể tổng hợp một số bộ thử nghiệm tích hợp?
Ozkan

83

Cài đặt-Gói System.IO.Abstrilities

Thư viện tưởng tượng này tồn tại, có một gói NuGet cho System.IO.Abstrilities , trừu tượng hóa không gian tên System.IO.

Ngoài ra còn có một bộ các trình trợ giúp kiểm tra, System.IO.Abstrilities.TestingHelpers mà - tại thời điểm viết bài - chỉ được thực hiện một phần, nhưng là một điểm khởi đầu rất tốt.


3
Tôi nghĩ rằng tiêu chuẩn hóa xung quanh sự trừu tượng đã được xây dựng này là đặt cược tốt nhất. Chưa bao giờ nghe nói về thư viện đó, vì vậy cảm ơn rất nhiều cho những người đứng đầu.
julealgon

PM là viết tắt của trình quản lý gói .. để mở ... Công cụ> Trình quản lý gói NuGet> Bảng điều khiển quản lý gói
thedanotto

11

Bạn có thể sẽ phải xây dựng một hợp đồng để xác định những thứ bạn cần từ hệ thống tệp và sau đó viết một trình bao bọc xung quanh các chức năng đó. Tại thời điểm đó, bạn có thể chế giễu hoặc bỏ qua việc thực hiện.

Thí dụ:

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

Đề xuất của tôi là sử dụng http://systemwrapper.codeplex.com/ vì nó cung cấp trình bao cho các loại được sử dụng chủ yếu trong không gian tên hệ thống


Tôi hiện đang sử dụng thư viện này và bây giờ tôi đã phát hiện ra rằng sự trừu tượng của nó đối với những thứ như FileStream không bao gồm IDis Dùng, tôi đang tìm kiếm một sự thay thế. Nếu thư viện không cho phép tôi xử lý luồng đúng cách, thì tôi không thể đề xuất (hoặc sử dụng) nó để xử lý các loại hoạt động đó.
James Nail

1
IFileStreamWrap của SystemWrapper triển khai IDis Dùng ngay bây giờ.
chào ngày

systemwrapper chỉ là .net framework, nó sẽ gây ra các vấn đề kỳ lạ nếu được sử dụng với .netcore
Adil H. Raza

3

Tôi đã đi qua các giải pháp sau đây:

  • Viết bài kiểm tra tích hợp, không phải bài kiểm tra đơn vị. Để làm việc này, bạn cần một cách đơn giản để tạo một thư mục nơi bạn có thể kết xuất nội dung mà không phải lo lắng về các thử nghiệm khác can thiệp. Tôi có một lớp TestFolder đơn giản có thể tạo một thư mục phương thức thử nghiệm duy nhất để sử dụng.
  • Viết một System.IO.File. Đó là tạo một IFile.cs . Tôi thấy việc sử dụng này thường kết thúc với các bài kiểm tra đơn giản chứng minh rằng bạn có thể viết các câu lệnh chế giễu, nhưng hãy sử dụng nó khi mức sử dụng IO nhỏ.
  • Kiểm tra lớp trừu tượng của bạn và trích xuất tệp IO từ lớp. Việc tạo một giao diện cho việc này. Phần còn lại sử dụng các bài kiểm tra tích hợp (nhưng điều này sẽ rất nhỏ). Điều này khác với ở trên thay vì thực hiện tập tin. Đọc bạn viết ý định, nói ioT Breathie.loadSinstall ()
  • System.IO.Phân tích . Tôi chưa sử dụng cái này, nhưng nó là thứ tôi thích nhất khi chơi cùng.

Tôi cuối cùng sử dụng tất cả các phương pháp trên, tùy thuộc vào những gì tôi đang viết. Nhưng hầu hết thời gian tôi nghĩ rằng sự trừu tượng là sai khi tôi viết các bài kiểm tra đơn vị đạt IO.


4
Liên kết đến IFile.cs bị hỏng.
Mike-E

3

Bằng cách sử dụng System.IO.AbstrilitiesSystem.IO.Abstrilities.TestingHelpers như thế:

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;
   }
}

Trong Lớp kiểm tra của bạn, bạn sử dụng MockFileSystem () để giả định tệp và bạn kích hoạt ManageFile như:

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

2

Bạn có thể làm điều đó bằng cách sử dụng Microsoft Fakes mà không cần phải thay đổi cơ sở mã của bạn chẳng hạn vì nó đã bị đóng băng.

Đầu tiên tạo một hội đồng giả cho System.dll - hoặc bất kỳ gói nào khác và sau đó giả định trả về dự kiến ​​như trong:

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

Sẽ rất khó để giả lập hệ thống tệp trong một thử nghiệm vì API tệp .NET không thực sự dựa trên giao diện hoặc các lớp có thể mở rộng có thể bị chế giễu.

Tuy nhiên, nếu bạn có lớp chức năng riêng để truy cập hệ thống tệp, bạn có thể giả định điều đó trong một bài kiểm tra đơn vị.

Thay thế cho chế độ nhạo báng, hãy xem xét việc chỉ tạo các thư mục và tệp bạn cần như một phần của thiết lập thử nghiệm và xóa chúng trong phương pháp phá bỏ của bạn.


1

Tôi không chắc chắn làm thế nào bạn sẽ giả lập hệ thống tập tin. Những gì bạn có thể làm là viết một thiết lập lịch thi đấu tạo thư mục, v.v. với cấu trúc cần thiết cho các bài kiểm tra. Một phương pháp phá bỏ sẽ làm sạch nó sau khi chạy thử.

Chỉnh sửa để thêm: Khi nghĩ về điều này nhiều hơn một chút, tôi không nghĩ bạn muốn giả lập hệ thống tệp để kiểm tra loại phương thức này. Nếu bạn giả định hệ thống tệp trả về đúng nếu một tệp nhất định tồn tại và sử dụng phương thức đó để kiểm tra xem tệp đó có tồn tại hay không, thì bạn không kiểm tra nhiều thứ. Việc chế tạo hệ thống tệp sẽ hữu ích là nếu bạn muốn kiểm tra một phương thức có sự phụ thuộc vào hệ thống tệp nhưng hoạt động của hệ thống tệp không tách rời với phương thức được kiểm tra.


1

Để trả lời câu hỏi cụ thể của bạn: Không, không có thư viện nào cho phép bạn giả định các cuộc gọi I / O của tệp (mà tôi biết). Điều này có nghĩa là đơn vị "đúng" kiểm tra các loại của bạn sẽ yêu cầu bạn xem xét hạn chế này khi bạn xác định loại của mình.

Ghi chú nhanh về cách tôi xác định bài kiểm tra đơn vị "phù hợp". Tôi tin rằng các bài kiểm tra đơn vị sẽ xác nhận rằng bạn nhận được đầu ra dự kiến ​​(có thể là ngoại lệ, gọi phương thức, v.v.) được cung cấp các đầu vào đã biết. Điều này cho phép bạn thiết lập các điều kiện kiểm tra đơn vị của mình dưới dạng một tập hợp các trạng thái đầu vào và / hoặc trạng thái đầu vào. Cách tốt nhất mà tôi đã tìm thấy để làm điều này là sử dụng các dịch vụ dựa trên giao diện và nội xạ phụ thuộc để mỗi trách nhiệm bên ngoài một loại được cung cấp thông qua một giao diện được truyền qua một hàm tạo hoặc thuộc tính.

Vì vậy, với điều này trong tâm trí, trở lại câu hỏi của bạn. Tôi đã chế nhạo các cuộc gọi hệ thống tệp bằng cách tạo IFileSystemServicegiao diện cùng với FileSystemServiceviệc triển khai chỉ đơn giản là một mặt ngoài trên các phương thức hệ thống tệp mscorlib. Mã của tôi sau đó sử dụng IFileSystemServicethay vì các loại mscorlib. Điều này cho phép tôi cắm tiêu chuẩn của mình FileSystemServicekhi ứng dụng đang chạy hoặc giả định IFileSystemServicetrong các bài kiểm tra đơn vị của tôi. Mã ứng dụng là như nhau bất kể nó chạy như thế nào, nhưng cơ sở hạ tầng bên dưới cho phép mã đó dễ dàng được kiểm tra.

Tôi sẽ thừa nhận rằng thật khó khăn khi sử dụng trình bao bọc xung quanh các đối tượng hệ thống tệp mscorlib, nhưng, trong các tình huống cụ thể này, nó đáng để làm thêm vì việc kiểm tra trở nên dễ dàng và đáng tin cậy hơn nhiều.


1

Tạo một giao diện và chế nhạo nó để thử nghiệm là cách tốt nhất để đi. Tuy nhiên, như một giải pháp thay thế, bạn có thể xem qua khung Microsoft Moles .


0

Giải pháp chung là sử dụng một số API hệ thống tệp trừu tượng (như Apache Commons VFS cho Java): tất cả logic ứng dụng sử dụng API và kiểm tra đơn vị có thể mô phỏng hệ thống tệp thực với triển khai gốc (mô phỏng trong bộ nhớ hoặc đại loại như thế).

Đối với C #, API tương tự tồn tại: NI.Vfs rất giống với Apache VFS V1. Nó chứa các cài đặt mặc định cho cả hệ thống tệp cục bộ và hệ thống tệp trong bộ nhớ (cái cuối cùng có thể được sử dụng trong các thử nghiệm đơn vị từ hộp).


-1

Chúng tôi hiện đang sử dụng một công cụ dữ liệu độc quyền và API của nó không được hiển thị dưới dạng giao diện nên chúng tôi khó có thể kiểm tra mã truy cập dữ liệu của mình. Rồi tôi cũng đi theo cách tiếp cận của Matt và Joseph.


-2

Tôi sẽ đi với câu trả lời của Jamie Ide. Đừng cố gắng chế giễu những điều mà bạn không viết. Sẽ có tất cả các cách phụ thuộc mà bạn không biết - các lớp niêm phong, các phương thức không ảo, v.v.

Một cách tiếp cận khác là bọc các phương thức chấp nhận với một cái gì đó có thể nhạo báng. ví dụ: tạo một lớp có tên là FileWrapper cho phép truy cập vào các phương thức Tệp nhưng là thứ bạn có thể giả định.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.