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ư void
chỉ chứa null
một số ngôn ngữ (một số ngôn ngữ gọi đây unit
và sử dụng tên void
cho một enum không có phần tử!). Nó có thể có hai giá trị, giống như bool
trong đó có false
và true
. Nó có thể có ba, như colourChannel
với red
, green
và blue
. 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ụ, result
có chứa win
/ lose
/ draw
là đẳng cấu với trên colourChannel
, vì chúng ta có thể thay thế ví dụ colourChannel
với result
, red
với win
, green
với lose
và blue
vớ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ỳ " colourChannel
bà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 colourChannel
nữ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ý
red
và 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 green
và blue
cá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.