Làm thế nào để bạn đơn vị kiểm tra một bộ mã hóa?


9

Tôi có một cái gì đó như thế này:

public byte[] EncodeMyObject(MyObject obj)

Tôi đã từng thử nghiệm đơn vị như thế này:

byte[] expectedResults = new byte[3]{ 0x01, 0x02, 0xFF };
Assert.IsEqual(expectedResults, EncodeMyObject(myObject));

EDIT: Hai cách tôi đã thấy đề xuất là:

1) Sử dụng các giá trị dự kiến ​​được mã hóa cứng, như ví dụ trên.

2) Sử dụng bộ giải mã để giải mã mảng byte được mã hóa và so sánh các đối tượng đầu vào / đầu ra.

Vấn đề tôi thấy với phương pháp 1 là nó rất dễ vỡ và đòi hỏi nhiều giá trị được mã hóa cứng.

Vấn đề với phương pháp 2 là việc kiểm tra bộ mã hóa phụ thuộc vào bộ giải mã hoạt động chính xác. Nếu bộ mã hóa / giải mã bị phá vỡ bằng nhau (ở cùng một vị trí), thì các thử nghiệm có thể tạo ra kết quả dương tính giả.

Đây rất có thể là những cách duy nhất để kiểm tra loại phương pháp này. Nếu đó là trường hợp tốt. Tôi đang đặt câu hỏi để xem liệu có chiến lược nào tốt hơn cho loại thử nghiệm này không. Tôi không thể tiết lộ nội bộ của bộ mã hóa cụ thể mà tôi đang làm việc. Tôi đang hỏi nói chung làm thế nào bạn sẽ giải quyết loại vấn đề này và tôi không cảm thấy nội bộ là quan trọng. Giả sử rằng một đối tượng đầu vào đã cho sẽ luôn tạo ra cùng một mảng byte đầu ra.


4
Làm thế nào để myObjectđi từ myObjectđến { 0x01, 0x02, 0xFF }? Thuật toán đó có thể được chia nhỏ và thử nghiệm không? Lý do tôi hỏi là hiện tại, có vẻ như bạn có một bài kiểm tra chứng minh rằng một điều ma thuật tạo ra một điều kỳ diệu khác. Sự tự tin duy nhất của bạn là một đầu vào tạo ra một đầu ra. Nếu bạn có thể phá vỡ thuật toán, bạn có thể tự tin hơn về thuật toán và ít phụ thuộc hơn vào đầu vào và đầu ra ma thuật.
Anthony Pegram

3
@Codism Điều gì xảy ra nếu bộ mã hóa và bộ giải mã bị hỏng ở cùng một vị trí?
ConditionRacer

2
Các xét nghiệm, theo định nghĩa, đang làm một cái gì đó và kiểm tra xem bạn có nhận được kết quả mong đợi hay không, đó là những gì thử nghiệm của bạn làm. Tất nhiên, bạn sẽ cần đảm bảo rằng bạn thực hiện đủ các bài kiểm tra như vậy để đảm bảo bạn thực hiện tất cả các mã của mình và bao gồm các trường hợp cạnh và sự kỳ lạ khác.
Blrfl

1
@ Justin984, tốt, bây giờ chúng ta sẽ đi sâu hơn. Tôi chắc chắn sẽ không tiết lộ những nội bộ riêng tư đó với tư cách là thành viên của API Encoder, chắc chắn là không. Tôi sẽ xóa chúng khỏi Encoder hoàn toàn. Hay đúng hơn, Encoder sẽ ủy thác cho một nơi khác, một sự phụ thuộc . Nếu đó là trận chiến giữa một phương thức quái vật không thể kiểm chứng hoặc một nhóm các lớp trợ giúp, thì tôi sẽ chọn các lớp trợ giúp mỗi lần. Nhưng một lần nữa, tôi đang đưa ra những suy luận không rõ ràng về mã của bạn tại thời điểm này, bởi vì tôi không thể nhìn thấy nó. Nhưng nếu bạn muốn có được sự tự tin trong các bài kiểm tra của mình, có các phương pháp nhỏ hơn làm ít việc hơn là một cách để đạt được điều đó.
Anthony Pegram

1
@ Justin984 Nếu thông số kỹ thuật thay đổi, bạn thay đổi đầu ra dự kiến ​​trong thử nghiệm của mình và hiện tại nó không thành công. Sau đó, bạn thay đổi logic bộ mã hóa để vượt qua. Có vẻ chính xác là TDD hoạt động như thế nào và nó sẽ chỉ thất bại khi cần. Tôi không thấy cách này làm cho nó giòn.
Daniel Kaplan

Câu trả lời:


1

Bạn đang ở trong một tình huống đáng ghét ở đó. Nếu bạn có một định dạng tĩnh mà bạn đã mã hóa thành, phương pháp đầu tiên của bạn sẽ là cách để đi. Nếu đó chỉ là định dạng của riêng bạn và không ai khác phải giải mã hơn phương thức thứ hai sẽ là cách để đi. Nhưng bạn không thực sự phù hợp với một trong những loại đó.

Những gì tôi sẽ làm là cố gắng phá vỡ mọi thứ theo mức độ trừu tượng.

Vì vậy, tôi bắt đầu với một cái gì đó ở cấp độ bit, rằng tôi sẽ thử nghiệm một cái gì đó như

bitWriter = new BitWriter();
bitWriter.writeInt(42, bits = 7);
assertEqual( bitWriter.data(), {0x42} )

Vì vậy, ý tưởng là bitwriter biết cách viết ra các loại trường nguyên thủy nhất, như ints.

Các loại phức tạp hơn sẽ được triển khai bằng cách sử dụng và thử nghiệm một cái gì đó như:

bitWriter = new BitWriter();
writeDate(bitWriter, new Datetime(2001, 10, 4));

bitWriter2 = new BitWriter();
bitWriter2.writeInt(2001, 12)
bitWriter2.writeInt(10, 4)
bitWriter2.writeInt(4, 6)

assertEquals(bitWriter.data(), bitWriter2.data() )

Lưu ý rằng điều này tránh mọi kiến ​​thức về cách các bit thực tế được đóng gói. Điều đó đã được thử nghiệm bởi thử nghiệm trước đó và đối với thử nghiệm này, chúng tôi gần như sẽ cho rằng nó hoạt động.

Sau đó, ở mức độ trừu tượng tiếp theo, chúng ta sẽ có

bitWriter = new BitWriter();
encodeObject(bitWriter, myObject);


bitWriter2 = new BitWriter();
bitWriter2.writeInt(42, 32)
writeDate(bitWriter2, new Datetime(2001, 10, 4));
writeVarString(bitWriter2, "alphanumeric");

assertEquals(bitWriter.data(), bitWriter2.data() )

vì vậy, một lần nữa, chúng tôi không cố gắng đưa vào kiến ​​thức về cách các biến số hoặc ngày hoặc số thực sự được mã hóa. Trong thử nghiệm này, chúng tôi chỉ quan tâm đến việc mã hóa được tạo bởi encodeObject.

Kết quả cuối cùng là nếu định dạng ngày thay đổi, bạn sẽ phải sửa các bài kiểm tra thực sự liên quan đến ngày, nhưng tất cả các mã và bài kiểm tra khác không liên quan đến cách ngày thực sự được mã hóa và một khi bạn cập nhật mã để thực hiện công việc đó, tất cả những bài kiểm tra sẽ vượt qua tốt


Tôi thích điều này. Tôi đoán đây là những gì một số nhà bình luận khác đã nói về việc phá vỡ nó thành những mảnh nhỏ hơn. Nó không hoàn toàn tránh được vấn đề khi thông số kỹ thuật thay đổi, nhưng nó làm cho nó tốt hơn.
ConditionRacer

6

Phụ thuộc. Nếu mã hóa là một cái gì đó hoàn toàn cố định, trong đó mọi thực hiện được cho là tạo ra chính xác cùng một đầu ra, thì sẽ không có ý nghĩa gì khi kiểm tra bất cứ thứ gì ngoài việc xác minh ví dụ đầu vào ánh xạ tới chính xác các đầu ra dự kiến. Đó là bài kiểm tra rõ ràng nhất, và có lẽ cũng dễ viết nhất.

Nếu có phòng ngọ nguậy với đầu ra thay thế, như trong tiêu chuẩn MPEG (ví dụ: có một số toán tử nhất định bạn có thể áp dụng cho đầu vào, nhưng bạn có thể tự do đánh đổi nỗ lực mã hóa so với chất lượng đầu ra hoặc không gian lưu trữ), thì tốt hơn nên áp dụng chiến lược giải mã được xác định cho đầu ra và xác minh rằng nó giống với đầu vào - hoặc, nếu mã hóa bị mất, nó gần hợp lý với đầu vào ban đầu. Điều đó khó lập trình hơn, nhưng bảo vệ bạn trước mọi cải tiến trong tương lai có thể được thực hiện cho bộ mã hóa của bạn.


2
Giả sử bạn sử dụng bộ giải mã và so sánh các giá trị. Điều gì xảy ra nếu cả bộ mã hóa và bộ giải mã đều bị hỏng ở cùng một nơi? Bộ mã hóa mã hóa không chính xác và bộ giải mã giải mã không chính xác, nhưng các đối tượng đầu vào / đầu ra là chính xác vì quá trình được thực hiện không chính xác hai lần.
ConditionRacer

@ Justin984 sau đó sử dụng cái gọi là "vectơ thử nghiệm", bí quyết đầu vào / đầu ra cặp mà bạn có thể sử dụng một cách chính xác để kiểm tra một bộ mã hóa và giải mã
ratchet quái

@ratchet freak Điều đó đưa tôi trở lại thử nghiệm với các giá trị dự kiến. Điều đó tốt, đó là những gì tôi đang làm, nhưng nó hơi dễ vỡ nên tôi đang tìm kiếm xem có cách nào tốt hơn không.
ConditionRacer

1
Ngoài việc đọc kỹ tiêu chuẩn và tạo trường hợp kiểm thử cho mọi quy tắc, hầu như không có cách nào để tránh rằng cả bộ mã hóa và bộ giải mã đều có cùng một lỗi. Ví dụ: giả sử rằng "ABC" phải được dịch thành "xyz" nhưng bộ mã hóa không biết điều đó và bộ giải mã của bạn cũng sẽ không hiểu "xyz" nếu nó gặp phải nó. Các testcase thủ công không chứa chuỗi "ABC", bởi vì lập trình viên không nhận thức được quy tắc đó và cũng là một thử nghiệm với chuỗi ngẫu nhiên mã hóa / giải mã sẽ vượt qua không chính xác vì cả bộ mã hóa và bộ giải mã đều bỏ qua vấn đề.
user281377

1
Để giúp bắt các lỗi ảnh hưởng đến cả bộ giải mã và bộ mã hóa do chính bạn viết do thiếu kiến ​​thức, hãy nỗ lực để có được đầu ra bộ mã hóa từ các nhà cung cấp khác; và cũng thử kiểm tra đầu ra của bộ mã hóa trên bộ giải mã của bên thứ ba. Không có một sự thay thế xung quanh nó.
rwong

3

Kiểm tra điều đó encode(decode(coded_value)) == coded_valuedecode(encode(value)) == value. Bạn có thể cung cấp một đầu vào ngẫu nhiên cho các bài kiểm tra nếu bạn muốn.

Vẫn có khả năng cả bộ mã hóa và bộ giải mã đều bị hỏng theo những cách miễn phí, nhưng điều đó dường như khá khó xảy ra trừ khi bạn có sự hiểu lầm về khái niệm của tiêu chuẩn mã hóa. Thực hiện các bài kiểm tra mã hóa cứng của bộ mã hóa và bộ giải mã (như bạn đã làm) sẽ bảo vệ chống lại điều đó.

Nếu bạn có quyền truy cập vào một triển khai khác mà công việc này được biết là hoạt động, ít nhất bạn có thể sử dụng nó để có được sự tin tưởng rằng việc triển khai của bạn là tốt ngay cả khi sử dụng nó trong các bài kiểm tra đơn vị là không thể.


Tôi đồng ý rằng nói chung một lỗi bộ mã hóa / giải mã bổ sung là không thể xảy ra. Trong trường hợp cụ thể của tôi, mã cho các lớp mã hóa / giải mã được tạo bởi một công cụ khác dựa trên các quy tắc từ cơ sở dữ liệu. Vì vậy, các lỗi bổ sung đôi khi xảy ra.
ConditionRacer

Làm thế nào có thể có 'lỗi miễn phí'? Điều đó ngụ ý rằng có một đặc tả bên ngoài cho dạng được mã hóa và do đó là một bộ giải mã bên ngoài.
kevin cline

Tôi không hiểu cách bạn sử dụng từ bên ngoài. Nhưng có một đặc điểm kỹ thuật về cách dữ liệu được mã hóa và cũng là một bộ giải mã. Một lỗi miễn phí là trong đó cả bộ mã hóa và bộ giải mã đều hoạt động theo cách bổ sung nhưng lại lệch khỏi đặc tả. Tôi có một ví dụ trong các ý kiến ​​dưới câu hỏi ban đầu.
ConditionRacer

Nếu bộ mã hóa được cho là thực hiện ROT13 nhưng vô tình làm ROT14 và bộ giải mã cũng vậy, thì giải mã (mã hóa ('a')) == 'a' nhưng bộ mã hóa vẫn bị hỏng. Đối với những thứ phức tạp hơn thế nhiều, có lẽ ít khả năng điều đó sẽ xảy ra, nhưng về mặt lý thuyết thì có thể.
Michael Shaw

@MichaelShaw chỉ là một mẩu chuyện nhỏ, bộ mã hóa và giải mã cho ROT13 là như nhau; ROT13 là nghịch đảo của chính nó. Nếu bạn thực hiện ROT14 do nhầm lẫn, thì decode(encode(char))sẽ không bằng char(nó sẽ bằng char+2).
Tom Marthenal

2

Kiểm tra theo yêu cầu .

Nếu các yêu cầu chỉ là 'mã hóa thành luồng byte mà khi được giải mã sẽ tạo ra một đối tượng tương đương.', Thì chỉ cần kiểm tra bộ mã hóa bằng cách giải mã. Nếu bạn đang viết cả bộ mã hóa và bộ giải mã, thì chỉ cần kiểm tra chúng cùng nhau. Họ không thể có "lỗi khớp". Nếu họ làm việc cùng nhau, sau đó thử nghiệm vượt qua.

Nếu có các yêu cầu khác cho luồng dữ liệu, thì bạn sẽ phải kiểm tra chúng bằng cách kiểm tra dữ liệu được mã hóa.

Nếu định dạng được mã hóa được xác định trước, thì bạn sẽ phải xác minh dữ liệu được mã hóa theo kết quả mong đợi, như bạn đã làm, hoặc (tốt hơn) có được bộ giải mã tham chiếu có thể tin cậy để thực hiện xác minh. Việc sử dụng bộ giải mã tham chiếu giúp loại bỏ khả năng bạn đã hiểu sai đặc tả định dạng.


1

Tùy thuộc vào khung thử nghiệm và mô hình bạn đang sử dụng, bạn vẫn có thể sử dụng mẫu Sắp xếp xác nhận hành động cho việc này như bạn đã nói.

[TestMethod]
public void EncodeMyObject_ForValidInputs_Encodes()
{
    //Arrange object under test
    MyEncoder encoderUnderTest = new MyEncoder();
    MyObject validObject = new MyOjbect();
    //arrange object for condition under test

    //Act
    byte[] actual = encoderUnderTest.EncodeMyObject(myObject);

    //Assert
    byte[] expected = new byte[3]{ 0x01, 0x02, 0xFF };
    Assert.IsEqual(expected, actual);
}

Bạn nên biết các yêu cầu EncodeMyObject()và có thể sử dụng mẫu này để kiểm tra từng tiêu chí cho các tiêu chí hợp lệ và không hợp lệ, bằng cách sắp xếp từng trong số chúng và mã hóa kết quả mong đợi expected, tương tự cho bộ giải mã.

Vì dự kiến ​​được mã hóa cứng, chúng sẽ dễ vỡ nếu bạn có một thay đổi lớn.

Bạn có thể tự động hóa với một cái gì đó được điều khiển bởi tham số (hãy xem Pex ) hoặc nếu bạn đang làm DDD hoặc BDD hãy xem gerkin / dưa chuột .


1

Bạn có thể quyết định điều gì là quan trọng với bạn.

Điều quan trọng với bạn là một Vật thể sống sót sau chuyến đi khứ hồi và định dạng dây chính xác không thực sự quan trọng? Hoặc định dạng dây chính xác là một phần quan trọng của chức năng của bộ mã hóa và giải mã của bạn?

Nếu trước đây, hơn là chỉ đảm bảo rằng các đối tượng sống sót trong chuyến đi khứ hồi. Nếu cả bộ mã hóa và bộ giải mã đều bị hỏng theo những cách bổ sung chính xác, bạn không thực sự quan tâm.

Nếu sau này, thì bạn cần phải kiểm tra định dạng dây như bạn mong đợi cho các đầu vào đã cho. Điều này có nghĩa là kiểm tra định dạng trực tiếp hoặc bằng cách khác sử dụng triển khai tham chiếu. Nhưng khi đã kiểm tra những điều cơ bản, bạn có thể nhận được giá trị từ các bài kiểm tra khứ hồi bổ sung, việc viết khối lượng sẽ dễ dàng hơn.

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.