2010-08-01 30 views
19

Tôi biết rằng bạn nên chạy thử nghiệm đơn vị tách khỏi hệ thống tệp, bởi vì nếu bạn chạm vào hệ thống tệp trong thử nghiệm, bạn cũng kiểm tra hệ thống tệp. OK, đó là hợp lý.
Câu hỏi của tôi là, nếu tôi muốn thử nghiệm lưu tệp vào đĩa, tôi phải làm gì? Như với cơ sở dữ liệu, tôi tách một giao diện có trách nhiệm truy cập cơ sở dữ liệu, và sau đó tạo một triển khai thực hiện điều này cho các thử nghiệm của tôi? Hoặc có thể có một số cách khác?Làm cách nào để kiểm tra đơn vị lưu tệp vào đĩa?

+1

Tạo giao diện để truy cập hệ thống tệp. Sau đó tạo mô hình của nó, hoặc bằng cách sử dụng một khung mocking hoặc by-hand. –

+0

cảm ơn rất nhiều ý kiến ​​và câu trả lời hữu ích, tôi nghĩ rằng tôi sẽ gắn bó với khuôn khổ mocking – chester89

+0

Kiểm tra đơn vị không sử dụng bất kỳ hệ thống nào. Nó không phải là thử nghiệm tích hợp. Kiểm tra đơn vị không kiểm tra lớp cuối cùng, lưu tệp vào đĩa. Bạn cần kiểm tra tích hợp để lưu tệp. Và trong trường hợp này bạn đơn giản lưu nó và sau đó kiểm tra xem nó đã được tạo chưa. Đó là một loại thử nghiệm khác nhau, phương pháp tiếp cận khác nhau và câu chuyện khác nhau. Hầu hết các câu trả lời dưới đây là sai trong khi họ nói về bài kiểm tra đơn vị nhưng cố gắng can thiệp vào các bài kiểm tra tích hợp thực tế –

Trả lời

36

Tiếp cận của tôi về phía này là rất nhiều thành kiến ​​về Growing Object-Oriented Software Guided by Tests (Goos) cuốn sách mà tôi chỉ đọc, nhưng đó là tốt nhất mà tôi biết ngày hôm nay. Cụ thể:

  • Tạo giao diện để xóa bỏ hệ thống tệp khỏi mã của bạn. Giả sử nó là nơi cần có lớp này với tư cách cộng tác viên/người phụ thuộc. Điều này giúp kiểm tra đơn vị của bạn nhanh chóng và phản hồi nhanh chóng.
  • Tạo các thử nghiệm tích hợp để kiểm tra việc triển khai thực tế của giao diện. tức là xác minh rằng gọi Save() thực sự tồn tại một tập tin vào đĩa và có nội dung ghi (sử dụng một tập tin tham khảo hoặc phân tích nó cho một vài điều mà nó nên chứa)
  • Tạo một thử nghiệm chấp nhận kiểm tra toàn bộ hệ thống - kết thúc kết thúc. Ở đây bạn chỉ có thể xác minh rằng một tập tin được tạo ra - mục đích của bài kiểm tra này là để xác nhận xem việc triển khai thực sự có được cắm/cắm đúng cách hay không.

Cập nhật cho commenter:

Nếu bạn đang đọc dữ liệu có cấu trúc (ví dụ như đối tượng Book) (Nếu không được chuỗi thay thế cho IEnumerable)

interface BookRepository 
{ 
    IEnumerable<Books> LoadFrom(string filePath); 
    void SaveTo(string filePath, IEnumerable<Books> books); 
} 

Bây giờ bạn có thể sử dụng constructor- tiêm để tiêm một mô hình vào lớp khách hàng. Lớp khách hàng kiểm tra đơn vị do đó rất nhanh; không nhấn hệ thống tập tin. Họ chỉ cần xác minh rằng phương pháp đúng được kêu gọi phụ thuộc (ví dụ Load/Save)

var testSubject = new Client(new Mock<BookRepository>.Object); 

Tiếp theo, bạn cần phải tạo ra việc thực hiện thực sự của BookRepository mà làm việc ra một File (hoặc một ngày mai Sql DB nếu bạn muốn nó). Không ai khác phải biết. Viết kiểm tra tích hợp cho FileBasedBookRepository (thực hiện Vai trò ở trên) và kiểm tra việc gọi Tải với một tệp tham chiếu cung cấp cho đúng đối tượng và gọi Lưu với danh sách đã biết, vẫn tiếp tục chúng vào đĩa. tức là sử dụng các tệp thực sự Các thử nghiệm này sẽ chậm để đánh dấu chúng bằng thẻ hoặc di chuyển nó sang một bộ riêng biệt.

[TestFixture] 
[Category("Integration - Slow")] 
public class FileBasedBookRepository 
{ 
    [Test] 
    public void CanLoadBooksFromFileOnDisk() {...} 
    [Test] 
    public void CanWriteBooksToFileOnDisk() {...} 
} 

Cuối cùng cần có một/nhiều chấp nhận kiểm tra rằng tập Load và Save.

+0

Chỉ cần tò mò, cuốn sách GOOS là gì? Không thể tìm ra. – Mathias

+0

@Mathias - http://www.growing-object-oriented-software.com/ – Gishu

+1

Nếu không quá nhiều để hỏi. Bạn có nghĩ rằng bạn có thể cung cấp một số mã cho bullet 1 và 2 không? Tôi rất cảm kích nó. –

6

Bạn có thể thay vì chuyển tên tệp vào chức năng lưu của mình, chuyển Luồng, Trình soạn thảo văn bản hoặc tương tự. Sau đó, khi thử nghiệm, bạn có thể vượt qua việc thực hiện dựa trên bộ nhớ và xác minh đúng byte được viết mà không thực sự viết bất cứ thứ gì vào đĩa.

Để kiểm tra các sự cố và ngoại lệ, bạn có thể xem xét khuôn khổ giả mạo. Điều này có thể giúp bạn tạo ra một ngoại lệ cụ thể tại một điểm nhất định trong quá trình lưu và kiểm tra xem mã của bạn có xử lý nó một cách thích hợp không.

+1

Tôi tự hỏi nếu @ chester89 cũng muốn thử nghiệm để xử lý 'không hợp lệ path',' tập tin collision', 'bị khóa tập tin',' không hợp lệ tên tập tin' , 'save interruption' vv Tôi cho rằng tất cả những thứ này có thể được bắt chước. – scunliffe

+0

@scunliffe, vâng, tôi cũng muốn thử nghiệm những điều này. làm thế nào tôi có thể bắt chước hành vi này? – chester89

+0

@chester: Bạn có thể xem xét một khuôn khổ nhạo báng. Điều này có thể giúp bạn tạo ra những ngoại lệ này vào những thời điểm thích hợp. Hãy cẩn thận để suy nghĩ trước khi đi xuống con đường kiểm tra mọi điều kiện lỗi duy nhất có thể và có 100% mã vùng phủ sóng từ các thử nghiệm. Điều này thường đắt hơn nó đáng giá. –

2

Có một nguyên tắc chung là phải thận trọng khi viết các bài kiểm tra đơn vị thực hiện tệp I/O, vì chúng có xu hướng quá chậm. Nhưng không có lệnh cấm tuyệt đối đối với tệp I/O trong các bài kiểm tra đơn vị.

Trong các bài kiểm tra đơn vị của bạn có một thư mục tạm thời được thiết lập và chia nhỏ, và tạo các tệp thử nghiệm và thư mục trong thư mục tạm thời đó. Có, các bài kiểm tra của bạn sẽ chậm hơn các bài kiểm tra CPU thuần túy, nhưng chúng vẫn sẽ nhanh. JUnit thậm chí còn có mã hỗ trợ để trợ giúp với trường hợp này: @Rule trên TemporaryFolder.

Điều đó nói rằng, hầu hết các mã tập tin văn bản có dạng như sau:

  1. Mở một output stream đến tập tin. Mã này phải đối phó với các tệp bị thiếu hoặc các vấn đề về quyền truy cập tệp. Bạn sẽ muốn kiểm tra xem nó có mở tệp và đối phó với các điều kiện lỗi đó không.
  2. Ghi vào luồng đầu ra. Điều này phải viết ở định dạng đúng, đó là phần phức tạp nhất đòi hỏi phải kiểm tra nhiều nhất. Nó phải đối phó với một lỗi I/O trong khi ghi vào luồng đầu ra, mặc dù những lỗi này hiếm khi xảy ra.
  3. Đóng luồng đầu ra. Điều này phải đối phó với một lỗi I/O trong khi đóng luồng, mặc dù những lỗi này hiếm khi xảy ra.

Chỉ thực sự giao dịch đầu tiên với hệ thống tệp. Phần còn lại chỉ sử dụng luồng đầu ra.

Vì vậy, bạn có thể trích xuất phần giữa và phần cuối của phương thức (hàm) của chính nó, thao tác một luồng đầu ra đã cho, thay vì tệp được đặt tên. Sau đó, bạn có thể thử luồng đầu ra đó để kiểm tra đơn vị phương thức. Những bài kiểm tra đơn vị đó sẽ rất nhanh. Hầu hết các ngôn ngữ lập trình đã cung cấp một lớp luồng đầu ra phù hợp.

Điều đó chỉ để phần đầu tiên được kiểm tra đơn vị. Bạn sẽ chỉ cần một vài bài kiểm tra, do đó, bộ thử nghiệm của bạn sẽ vẫn được chấp nhận nhanh chóng.

+0

Xem thêm http://stackoverflow.com/questions/225073/testing-whats-written-to-a-java-outputstream – Raedwald

Các vấn đề liên quan