Có nên kiểm tra các giá trị của một enum bằng các bài kiểm tra đơn vị không?


15

Nếu bạn có một enum chỉ có các giá trị (không có phương thức nào có thể làm trong Java) và enum này là một phần của định nghĩa nghiệp vụ của hệ thống, bạn có nên viết đơn vị kiểm tra cho nó không?

Tôi đã nghĩ rằng chúng nên được viết, ngay cả khi chúng có vẻ đơn giản và dư thừa, tôi cho rằng những gì liên quan đến đặc tả kinh doanh nên được viết rõ ràng trong một bài kiểm tra, cho dù đó là với đơn vị / tích hợp / ui / vv. kiểm tra hoặc bằng cách sử dụng hệ thống loại ngôn ngữ làm phương pháp kiểm tra. Vì các giá trị mà một enum (ví dụ trong Java) phải có, theo quan điểm của doanh nghiệp, không thể được kiểm tra bằng cách sử dụng hệ thống loại tôi nghĩ nên có một bài kiểm tra đơn vị cho điều đó.

Câu hỏi này không giống với câu hỏi này vì nó không giải quyết vấn đề tương tự như của tôi. Trong câu hỏi đó có một chức năng kinh doanh (saveP People) và người này đang tìm hiểu về việc triển khai nội bộ (forEach). Trong đó, có một lớp doanh nghiệp trung bình (chức năng cứu người) gói gọn cấu trúc ngôn ngữ (forEach). Ở đây, cấu trúc ngôn ngữ (enum) là ngôn ngữ được sử dụng để chỉ định hành vi theo quan điểm kinh doanh.

Trong trường hợp này, chi tiết triển khai trùng khớp với "bản chất thực" của dữ liệu, đó là: một tập hợp (theo nghĩa toán học) của các giá trị. Bạn có thể sử dụng một tập hợp bất biến, nhưng các giá trị tương tự vẫn sẽ xuất hiện ở đó. Nếu bạn sử dụng một mảng, điều tương tự phải được thực hiện để kiểm tra logic nghiệp vụ. Tôi nghĩ câu hỏi hóc búa ở đây là thực tế là ngôn ngữ xây dựng trùng khớp rất tốt với bản chất của dữ liệu. Tôi không chắc là tôi đã giải thích chính xác chưa


19
Chính xác thì một bài kiểm tra đơn vị của một enum trông như thế nào?
jonrsharpe


@jonrsharpe Nó sẽ khẳng định rằng các giá trị bên trong enum là những giá trị mà bạn mong đợi. Tôi sẽ làm điều đó bằng cách lặp lại các giá trị của enum, thêm chúng vào một tập hợp, ví dụ như các chuỗi. Đặt hàng mà đặt. So sánh được thiết lập với một danh sách các giá trị được viết bằng tay trong bài kiểm tra. Họ nên phù hợp.
IS1_SO

1
@jonrsharpe, tôi thích nghĩ về các bài kiểm tra đơn vị cũng là "định nghĩa" hoặc "yêu cầu" được viết bằng mã. Một bài kiểm tra đơn vị của một enum sẽ đơn giản như kiểm tra số lượng vật phẩm trên enum và các giá trị của chúng. Đặc biệt trong C #, trong đó các enum không phải là các lớp, nhưng có thể được ánh xạ trực tiếp tới các số nguyên, đảm bảo các giá trị của chúng và không lập trình bởi sự trùng hợp có thể chứng minh hữu ích cho các mục đích tuần tự hóa.
Machado

2
IMHO không hữu ích hơn nhiều so với thử nghiệm nếu 2 + 2 = 4 để kiểm tra xem Vũ trụ có đúng không. Bạn kiểm tra mã bằng enum đó chứ không phải enum.
Đặc vụ_L

Câu trả lời:


39

Nếu bạn có một enum chỉ có các giá trị (không có phương thức nào có thể làm trong Java) và enum này là một phần của định nghĩa nghiệp vụ của hệ thống, bạn có nên viết đơn vị kiểm tra cho nó không?

Không, họ chỉ là nhà nước.

Về cơ bản, thực tế là bạn đang sử dụng một enum là một chi tiết triển khai ; đó là thứ mà bạn có thể muốn có thể tái cấu trúc thành một thiết kế khác.

Kiểm tra enum cho tính đầy đủ tương tự như kiểm tra rằng tất cả các số nguyên có thể đại diện đều có mặt.

Tuy nhiên, kiểm tra các hành vi mà bảng liệt kê hỗ trợ là một ý tưởng tốt. Nói cách khác, nếu bạn bắt đầu từ một bộ kiểm tra vượt qua và nhận xét bất kỳ giá trị enum nào, thì ít nhất một thử nghiệm sẽ thất bại (lỗi biên dịch được coi là thất bại).


5
Nhưng trong trường hợp này, chi tiết triển khai trùng khớp với "bản chất thực" của dữ liệu, đó là: một tập hợp (theo nghĩa toán học) của các giá trị. Bạn có thể sử dụng một tập hợp bất biến, nhưng các giá trị tương tự vẫn sẽ xuất hiện ở đó. Nếu bạn sử dụng một mảng, điều tương tự phải được thực hiện để kiểm tra logic nghiệp vụ. Tôi nghĩ câu hỏi hóc búa ở đây là thực tế là ngôn ngữ xây dựng trùng khớp rất tốt với bản chất của dữ liệu. Tôi không chắc là tôi đã giải thích chính xác chưa.
IS1_SO

4
@ IS1_SO - theo quan điểm của VOU rằng một bài kiểm tra sẽ thất bại: đã làm điều đó? Trong trường hợp đó, bạn không cần phải kiểm tra Enum cụ thể. Có phải vậy không? Có lẽ đó là một dấu hiệu cho thấy bạn có thể mô hình mã của bạn đơn giản hơn và tạo ra một sự trừu tượng so với 'bản chất thật' của dữ liệu - ví dụ như không phụ thuộc vào thẻ trong một boong, bạn có thực sự cần phải có một đại diện của [ Hearts, Spades, Diamonds, Clubs] nếu Bạn chỉ bao giờ thẻ nếu thẻ có màu đỏ / đen?
một

1
@ IS1_SO cho phép bạn có một danh sách mã lỗi và bạn muốn ném null_ptrlỗi. Bây giờ có mã lỗi thông qua enum. Mã kiểm tra null_ptrlỗi cũng tìm mã thông qua enum. Vì vậy, nó có thể có một giá trị 5(cho ex). Bây giờ bạn cần thêm một mã lỗi khác. Enum được thay đổi (giả sử chúng ta thêm một cái mới vào đầu enum) Giá trị của null_ptrbây giờ 6. Đây co phải vân đê? bây giờ bạn trả lại mã lỗi 6và kiểm tra 6. Miễn là mọi thứ đều phù hợp về mặt logic, bạn vẫn ổn, mặc dù sự thay đổi này phá vỡ bài kiểm tra lý thuyết của bạn.
Baldrickk

17

Bạn không kiểm tra một tuyên bố enum . Bạn có thể kiểm tra xem đầu vào / đầu ra của hàm có các giá trị enum dự kiến ​​hay không. Thí dụ:

enum Parity {
    Even,
    Odd
}

Parity GetParity(int x) { ... }

Bạn không viết kiểm tra xác minh sau đó enum Parityxác định tên EvenOdd. Một bài kiểm tra như vậy sẽ là vô nghĩa vì bạn sẽ chỉ lặp lại những gì đã được quy định trong mã. Nói điều tương tự hai lần không làm cho nó đúng hơn.

Bạn làm ghi kiểm tra xác minh GetParitynói sẽ trở về Evensố 0, Odd1 và vân vân. Điều này rất có giá trị vì bạn không lặp lại mã, bạn đang xác minh hành vi của mã, độc lập với việc thực hiện. Nếu mã bên trong GetParityđược viết lại hoàn toàn, các thử nghiệm vẫn sẽ hợp lệ. Thật vậy, lợi ích chính của các bài kiểm tra đơn vị là chúng cho phép bạn tự do viết lại và cấu trúc lại mã một cách an toàn, bằng cách đảm bảo mã vẫn hoạt động như mong đợi.

Nhưng nếu bạn có một bài kiểm tra đảm bảo khai báo enum xác định tên dự kiến, thì bất kỳ thay đổi nào bạn thực hiện đối với enum trong tương lai cũng sẽ yêu cầu bạn thay đổi bài kiểm tra. Điều này có nghĩa là nó không chỉ gấp đôi công việc, mà còn có nghĩa là bất kỳ lợi ích nào cho bài kiểm tra đơn vị bị mất. Nếu bạn phải thay đổi mã và kiểm tra cùng một lúc , thì không có biện pháp bảo vệ chống lại việc giới thiệu các lỗi.


Tôi đã cập nhật câu hỏi của mình trong một nỗ lực để giải quyết câu trả lời này, kiểm tra nó để xem nếu điều đó có ích.
IS1_SO

@ IS1_SO: OK làm tôi bối rối - bạn có đang tự động tạo ra các giá trị enum hay điều gì đang xảy ra?
JacquesB

Không. Điều tôi muốn nói là trong trường hợp này, cấu trúc ngôn ngữ được chọn để thể hiện các giá trị là một enum. Nhưng như chúng ta biết đó là một chi tiết thực hiện. Điều gì xảy ra nếu một người chọn một mảng hoặc Tập <> (trong Java) hoặc một chuỗi có một số mã thông báo tách để biểu thị các giá trị? Nếu đó là trường hợp thì sẽ có ý nghĩa để kiểm tra rằng các giá trị chứa trong đó là những lợi ích cho doanh nghiệp. Đó là quan điểm của tôi. Liệu lời giải thích này có giúp ích?
IS1_SO

3
@ IS1_SO: Bạn đang nói về việc kiểm tra một thể hiện enum được trả về từ một hàm có giá trị mong đợi nhất định? Bởi vì có bạn có thể kiểm tra điều đó. Bạn không cần phải kiểm tra bản khai enum.
JacquesB

11

Nếu có nguy cơ thay đổi enum sẽ phá vỡ mã của bạn thì chắc chắn, bất cứ điều gì có thuộc tính [Cờ] trong C # sẽ là một trường hợp tốt bởi vì thêm giá trị giữa 2 và 4 (3) sẽ là bit 1 và 2 thay vì một mục kín đáo.

Đó là một lớp bảo vệ.

Bạn nên xem xét việc có một quy tắc thực hành enum mà tất cả các nhà phát triển đều quen thuộc. Đừng dựa vào các biểu diễn văn bản của enum là một điều phổ biến, nhưng điều này có thể mâu thuẫn với các nguyên tắc tuần tự hóa của bạn.

Tôi đã thấy mọi người "sửa" cách viết hoa của các mục enum, sắp xếp chúng theo thứ tự bảng chữ cái hoặc theo một số nhóm logic khác, tất cả chúng đã phá vỡ các bit khác của mã xấu.


5
Nếu các giá trị số của enum được sử dụng ở bất cứ đâu, ví dụ như khi được lưu trữ trong cơ sở dữ liệu, thì việc sắp xếp lại (bao gồm xóa hoặc chèn trước giá trị cuối cùng) có thể khiến các bản ghi hiện tại trở nên không hợp lệ.
stannius

3
+1, câu trả lời này bị đánh giá thấp. Nếu enum của bạn là một phần của tuần tự hóa, giao diện đầu vào với từ bên ngoài hoặc thông tin có thể tổng hợp theo bit, chúng chắc chắn sẽ cần phải được kiểm tra tính nhất quán ở mỗi phiên bản của hệ thống. Ít nhất nếu bạn lo lắng về khả năng tương thích ngược, thường là một điều tốt.
Machado

11

Không, kiểm tra kiểm tra xem một enum có chứa tất cả các giá trị hợp lệ hay không, về cơ bản không lặp lại tuyên bố của enum. Bạn sẽ chỉ kiểm tra rằng ngôn ngữ thực hiện đúng cấu trúc enum là một bài kiểm tra vô nghĩa.

Điều đó đang được nói, bạn nên kiểm tra hành vi phụ thuộc vào các giá trị enum. Ví dụ: nếu bạn đang sử dụng các giá trị enum để tuần tự hóa các thực thể thành json hoặc bất cứ thứ gì, hoặc lưu trữ các giá trị trong cơ sở dữ liệu, bạn nên kiểm tra hành vi cho tất cả các giá trị của enum. Theo cách đó, nếu enum được sửa đổi thì ít nhất một trong các bài kiểm tra sẽ thất bại. Trong mọi trường hợp, những gì bạn sẽ kiểm tra là hành vi xung quanh enum của bạn, chứ không phải bản khai enum.


3

Mã của bạn phải hoạt động chính xác độc lập với các giá trị thực của enum. Nếu đó là trường hợp, thì không cần kiểm tra đơn vị.

Nhưng bạn có thể có mã trong đó việc thay đổi giá trị enum sẽ phá vỡ mọi thứ. Ví dụ: nếu một giá trị enum được lưu trữ trong một tệp bên ngoài và sau khi thay đổi giá trị enum, đọc tệp bên ngoài sẽ cho kết quả sai. Trong trường hợp đó, bạn sẽ có một nhận xét LỚN gần cảnh báo enum bất kỳ ai không sửa đổi bất kỳ giá trị nào và bạn rất có thể viết một bài kiểm tra đơn vị kiểm tra các giá trị số.


1

Nói chung, chỉ cần kiểm tra xem một enum có danh sách các giá trị được mã hóa cứng không có nhiều giá trị, như các câu trả lời khác đã nói, bởi vì sau đó bạn chỉ cần cập nhật kiểm tra và enum cùng nhau.

Tôi đã từng có một trường hợp một mô-đun sử dụng các loại enum từ hai mô-đun khác và ánh xạ giữa chúng. (Một trong những enum có logic bổ sung với nó, cái còn lại dành cho truy cập DB, cả hai đều có các phụ thuộc cần được cách ly với nhau.)

Trong trường hợp này, tôi đã thêm một thử nghiệm (trong mô-đun ánh xạ) để xác minh rằng tất cả các mục enum trong enum nguồn cũng tồn tại trong enum đích (và do đó, ánh xạ sẽ luôn hoạt động). (Đối với một số trường hợp, tôi cũng đã kiểm tra theo cách khác.)

Bằng cách này, khi ai đó đã thêm một mục enum vào một trong các enum và quên thêm mục tương ứng vào mục khác, một bài kiểm tra bắt đầu thất bại.


1

Enums chỉ đơn giản là các loại hữu hạn, với các tên tùy chỉnh (hy vọng có ý nghĩa). Một enum có thể chỉ có một giá trị, giống như voidchỉ chứa nullmột số ngôn ngữ (một số ngôn ngữ gọi đây unitvà sử dụng tên voidcho một enum không có phần tử!). Nó có thể có hai giá trị, giống như booltrong đó có falsetrue. Nó có thể có ba, như colourChannelvới red, greenblue. Và như thế.

Nếu hai enum có cùng số giá trị, thì chúng là "đẳng cấu"; tức là nếu chúng ta tắt tất cả các tên một cách có hệ thống thì chúng ta có thể sử dụng tên này thay cho tên khác và chương trình của chúng ta sẽ không hoạt động khác đi. Đặc biệt, các bài kiểm tra của chúng tôi sẽ không hành xử khác nhau!

Ví dụ, resultcó chứa win/ lose/ drawlà đẳng cấu với trên colourChannel, vì chúng ta có thể thay thế ví dụ colourChannelvới result, redvới win, greenvới losebluevới draw, và chừng nào chúng ta làm như vậy ở khắp mọi nơi (nhà sản xuất và người tiêu dùng, phân tích cú pháp và serialisers, mục cơ sở dữ liệu, các file log, vv ) sau đó sẽ không có thay đổi trong chương trình của chúng tôi. Bất kỳ " colourChannelbài kiểm tra" nào chúng tôi đã viết vẫn sẽ vượt qua, mặc dù không còn colourChannelnữa!

Ngoài ra, nếu một enum chứa nhiều hơn một giá trị, chúng ta luôn có thể sắp xếp lại các giá trị đó để có được một enum mới có cùng số lượng giá trị. Vì số lượng giá trị không thay đổi, cách sắp xếp mới là đẳng cấu với giá trị cũ và do đó chúng tôi có thể tắt tất cả các tên và các thử nghiệm của chúng tôi vẫn sẽ vượt qua (lưu ý rằng chúng tôi không thể tắt định nghĩa; chúng tôi phải vẫn chuyển ra tất cả các trang web sử dụng là tốt).

Điều này có nghĩa là, theo như máy móc có liên quan, enums là "tên có thể phân biệt" và không có gì khác . Điều duy nhất chúng ta có thể làm với một enum là phân nhánh xem hai giá trị là giống nhau (ví dụ red/ red) hoặc khác nhau (ví dụ red/ blue). Vì vậy, đó là điều duy nhất mà một 'bài kiểm tra đơn vị' có thể làm, vd

(  red == red  ) || throw TestFailure;
(green == green) || throw TestFailure;
( blue == blue ) || throw TestFailure;
(  red != green) || throw TestFailure;
(  red != blue ) || throw TestFailure;
...

Như @ jesm00 nói, một bài kiểm tra như vậy đang kiểm tra việc thực hiện ngôn ngữ hơn là chương trình của bạn. Các bài kiểm tra này không bao giờ là một ý tưởng hay: ngay cả khi bạn không tin tưởng vào việc thực hiện ngôn ngữ, bạn nên kiểm tra nó từ bên ngoài , vì không thể tin tưởng để chạy các bài kiểm tra một cách chính xác!

Vì vậy, đó là lý thuyết; Còn thực hành thì sao? Vấn đề chính với đặc tính này của enums là các chương trình 'thế giới thực' hiếm khi khép kín: chúng tôi có các phiên bản kế thừa, triển khai từ xa / nhúng, dữ liệu lịch sử, sao lưu, cơ sở dữ liệu trực tiếp, v.v. vì vậy chúng tôi không bao giờ thực sự 'tắt' tất cả các lần xuất hiện của một tên mà không bỏ lỡ một số sử dụng.

Tuy nhiên, những điều như vậy không phải là 'trách nhiệm' của chính enum: thay đổi một enum có thể phá vỡ giao tiếp với một hệ thống từ xa, nhưng ngược lại, chúng ta có thể khắc phục vấn đề như vậy bằng cách thay đổi một enum!

Trong tình huống như vậy, enum là một đỏ cá trích: những gì nếu một hệ thống cần nó để được này bằng cách nào, và một người khác cần nó để được rằng cách? Không thể là cả hai, cho dù chúng tôi có viết bao nhiêu bài kiểm tra! Thủ phạm thực sự ở đây là giao diện đầu vào / đầu ra, sẽ tạo ra / tiêu thụ các định dạng được xác định rõ hơn là "bất kỳ số nguyên nào mà diễn giải chọn". Vì vậy, giải pháp thực sự là kiểm tra các giao diện i / o : với các bài kiểm tra đơn vị để kiểm tra xem nó có phân tích / in định dạng dự kiến ​​hay không và kiểm tra tích hợp để kiểm tra xem định dạng đó có thực sự được chấp nhận bởi phía bên kia không.

Chúng ta vẫn có thể tự hỏi liệu enum có được 'thực hiện đủ kỹ lưỡng' hay không, nhưng trong trường hợp này, enum lại là một cá trích đỏ. Điều chúng tôi thực sự quan tâm là bộ thử nghiệm . Chúng ta có thể có được sự tự tin ở đây theo một số cách:

  • Phạm vi mã có thể cho chúng ta biết nếu nhiều giá trị enum đến từ bộ kiểm tra có đủ để kích hoạt các nhánh khác nhau trong mã hay không. Nếu không, chúng ta có thể thêm các thử nghiệm kích hoạt các nhánh chưa được khám phá hoặc tạo ra nhiều loại enum khác nhau trong các thử nghiệm hiện có.
  • Kiểm tra thuộc tính có thể cho chúng ta biết nếu nhiều nhánh trong mã có đủ để xử lý các khả năng thời gian chạy hay không. Ví dụ: nếu mã chỉ xử lý redvà chúng tôi chỉ kiểm tra red, thì chúng tôi có phạm vi bảo hiểm 100%. Trình kiểm tra thuộc tính sẽ (cố gắng) tạo các mẫu đối lập với các xác nhận của chúng tôi, chẳng hạn như tạo greenbluecác giá trị mà chúng tôi quên kiểm tra.
  • Kiểm tra đột biến có thể cho chúng ta biết liệu các xác nhận của chúng ta có thực sự kiểm tra enum hay không, thay vì chỉ theo dõi các nhánh và bỏ qua sự khác biệt của chúng.

1

Số bài kiểm tra đơn vị là dành cho các đơn vị thử nghiệm.

Trong lập trình hướng đối tượng, một đơn vị thường là toàn bộ giao diện, chẳng hạn như một lớp, nhưng có thể là một phương thức riêng lẻ.

https://en.wikipedia.org/wiki/Unit_testing

Một bài kiểm tra tự động cho một enum được khai báo sẽ kiểm tra tính toàn vẹn của ngôn ngữ và nền tảng mà nó đang chạy chứ không phải logic trong mã do nhà phát triển ủy quyền. Nó sẽ không phục vụ mục đích hữu ích - tài liệu được bao gồm vì mã khai báo enum đóng vai trò là tài liệu cũng như mã sẽ kiểm tra nó.


0

Bạn sẽ kiểm tra hành vi có thể quan sát được của mã của bạn, tác động của các cuộc gọi phương thức / hàm đến trạng thái có thể quan sát được. Miễn là mã làm đúng việc bạn ổn, bạn không cần phải kiểm tra bất cứ điều gì khác.

Bạn không cần phải xác nhận một cách rõ ràng rằng một loại enum có các mục bạn mong đợi, giống như bạn không khẳng định rõ ràng rằng một lớp thực sự tồn tại hoặc nó có các phương thức và thuộc tính mà bạn mong đợi.

Trên thực tế bằng cách kiểm tra hành vi, bạn hoàn toàn khẳng định rằng các lớp, phương thức và giá trị liên quan đến kiểm thử tồn tại, do đó bạn không cần phải xác nhận rõ ràng.

Lưu ý rằng bạn không cần tên có ý nghĩa cho mã của mình để thực hiện đúng, đó chỉ là sự tiện lợi cho những người đọc mã của bạn. Bạn có thể làm cho mã của mình hoạt động với các giá trị enum như foo, bar... và các phương thức như frobnicate().

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.