Làm thế nào để loại lỗi được phát hiện trong khi tạo giả trong một ngôn ngữ động?


10

Vấn đề xảy ra trong khi làm TDD. Sau một vài lần kiểm tra, các kiểu trả về của một số lớp / mô-đun thay đổi. Trong ngôn ngữ lập trình được nhập tĩnh, nếu một đối tượng giả định trước đó được sử dụng trong các thử nghiệm của một số lớp khác và không được sửa đổi để phản ánh sự thay đổi loại, thì sẽ xảy ra lỗi biên dịch.

Tuy nhiên, đối với các ngôn ngữ động, sự thay đổi trong các kiểu trả về có thể không được phát hiện và các bài kiểm tra của lớp khác sẽ vẫn vượt qua. Chắc chắn có thể có các bài kiểm tra tích hợp sẽ thất bại sau này, nhưng các bài kiểm tra đơn vị sẽ vượt qua một cách sai lầm. Có cách nào để tránh điều này?

Cập nhật với một mẫu tầm thường (trên một số ngôn ngữ tạo thành) ...

Phiên bản 1:

Calc = {
    doMultiply(x, y) {return x * y}
}
//.... more code ....

// On some faraway remote code on a different file
Rect = {
    computeArea(l, w) {return Calc.doMultipy(x*y)}
}

// test for Rect
testComputeArea() { 
    Calc = new Mock()
    Calc.expect(doMultiply, 2, 30) // where 2 is the arity
    assertEqual(30, computeArea)
}

Bây giờ, trên phiên bản 2:

// I change the return types. I also update the tests for Calc
Calc = {
    doMultiply(x, y) {return {result: (x * y), success:true}}
}

... Rect sau đó sẽ đưa ra một ngoại lệ trong thời gian chạy, nhưng thử nghiệm vẫn sẽ thành công.


1
Điều mà các câu trả lời cho đến nay dường như bỏ lỡ là câu hỏi không phải là về các thử nghiệm liên quan đến sự thay đổi class X, mà là các thử nghiệm class Yphụ thuộc Xvà do đó được thử nghiệm đối với một hợp đồng khác so với những gì nó chạy trong sản xuất.
Bart van Ingen Schenau

Tôi chỉ hỏi câu hỏi này trên SO , bản thân tôi, liên quan đến Dependency Injection. Xem Lý do 1: Một lớp phụ thuộc có thể được thay đổi trong thời gian chạy (nghĩ thử nghiệm) . Cả hai chúng tôi có cùng suy nghĩ nhưng thiếu những lời giải thích tuyệt vời.
Scott Coates

Tôi đang đọc lại câu hỏi của bạn nhưng tôi cảm thấy hơi bối rối khi giải thích. bạn có thể cung cấp một ví dụ?
Scott Coates

Câu trả lời:


2

Ở một mức độ nhất định, đây chỉ là một phần chi phí kinh doanh với các ngôn ngữ động. Bạn có được rất nhiều sự linh hoạt, còn được gọi là "đủ dây để treo mình". Hãy cẩn thận với nó.

Đối với tôi, vấn đề đề nghị sử dụng các kỹ thuật tái cấu trúc khác với ngôn ngữ được gõ tĩnh. Trong một ngôn ngữ tĩnh, bạn thay thế các kiểu trả về một phần để bạn có thể "dựa vào trình biên dịch" để tìm những nơi có thể phá vỡ. Nó là an toàn để làm, và có lẽ an toàn hơn so với sửa đổi loại trả lại tại chỗ.

Trong một ngôn ngữ động, bạn không thể làm điều đó, vì vậy sẽ an toàn hơn nhiều khi sửa đổi loại trả lại tại chỗ, thay vì thay thế nó. Có thể, bạn sửa đổi nó bằng cách thêm lớp mới của bạn vào nó như một thành viên, cho các lớp cần nó.


2

Nếu mã của bạn thay đổi và các thử nghiệm của bạn vẫn vượt qua, thì có thể có điều gì đó không ổn với các thử nghiệm của bạn (tức là bạn đang thiếu một xác nhận) hoặc mã thực sự không thay đổi.

Ý tôi là gì? Vâng, các thử nghiệm của bạn mô tả các hợp đồng giữa các phần của mã của bạn. Nếu hợp đồng là "giá trị trả lại phải có thể lặp lại", thì việc thay đổi giá trị trả về từ một mảng thành danh sách không thực sự là thay đổi trong hợp đồng và do đó sẽ không nhất thiết gây ra lỗi thử nghiệm.

Để tránh bỏ sót các xác nhận, bạn có thể sử dụng các công cụ như phân tích độ bao phủ mã (nó sẽ không cho bạn biết phần nào trong mã của bạn được kiểm tra, nhưng nó sẽ cho bạn biết phần nào chắc chắn không được kiểm tra), độ phức tạp chu kỳ và độ phức tạp NPath (cung cấp cho bạn giới hạn thấp hơn về số lượng xác nhận cần thiết để đạt được phạm vi bảo hiểm mã C1 và C2 đầy đủ) và người kiểm tra đột biến (đưa các đột biến trong mã của bạn như chuyển truesang false, số âm thành dương, đối tượng vào nullv.v. và kiểm tra xem làm cho các bài kiểm tra thất bại).


+1 Có gì đó không đúng với các bài kiểm tra hoặc mã không thực sự thay đổi. Điều này khiến tôi mất một thời gian để làm quen sau nhiều năm sử dụng C ++ / Java. Hợp đồng giữa các bộ phận trong các ngôn ngữ mã động không nên được trả về CÁI GÌ, nhưng thứ được trả lại chứa gì và nó có thể làm gì.
MrFox

@suslik: Trên thực tế, đây không phải là về ngôn ngữ tĩnh so với ngôn ngữ động, mà là về Trừu tượng dữ liệu bằng cách sử dụng các kiểu dữ liệu trừu tượng so với trừu tượng hóa dữ liệu hướng đối tượng. Khả năng cho một đối tượng mô phỏng một đối tượng khác miễn là nó hoạt động không thể phân biệt được (ngay cả khi chúng thuộc các loại hoàn toàn khác nhau và các thể hiện của các lớp hoàn toàn khác nhau) là nền tảng cho toàn bộ ý tưởng của OO. Hoặc đặt một cách khác: nếu hai đối tượng hành xử giống nhau, chúng cùng loại, bất kể các lớp của chúng nói gì. Đặt một cách khác: các lớp không phải là loại. Thật không may, Java và C # đã hiểu sai điều này.
Jörg W Mittag

Giả sử rằng đơn vị giả được bao phủ 100% và không thiếu khẳng định nào: làm thế nào về một thay đổi tinh tế hơn như "nên trả về null cho foo" thành "nên trả lại chuỗi trống cho foo". Nếu ai đó thay đổi đơn vị đó và thử nghiệm của nó, một số đơn vị tiêu thụ có thể bị hỏng và do giả, điều này là trong suốt. (Và không: tại thời điểm viết hoặc sử dụng mô-đun giả, theo "hợp đồng", không cần thiết phải xử lý các chuỗi rỗng là giá trị trả về vì nó chỉ trả về các chuỗi không trống hoặc chỉ null;)). (Chỉ có thử nghiệm tích hợp thích hợp của cả hai mô-đun trong tương tác mới có thể nắm bắt được điều này.)
thử-bắt-cuối-
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.