エンコーダのユニットテストはどのように行いますか?


9

私はこのようなものを持っています:

public byte[] EncodeMyObject(MyObject obj)

私はこのようにユニットテストをしてきました:

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

編集:私が提案した2つの方法は次のとおりです。

1)上記の例のように、ハードコードされた期待値を使用します。

2)デコーダーを使用してエンコードされたバイト配列をデコードし、入力/出力オブジェクトを比較します。

方法1で見られる問題は、非常に壊れやすく、多くのハードコードされた値を必要とすることです。

方法2の問題は、エンコーダーのテストがデコーダーの正常な動作に依存することです。エンコーダー/デコーダーが同じ場所で同じように破損している場合、テストで誤検知が発生する可能性があります。

これらが、このタイプのメソッドをテストする唯一の方法である場合があります。その場合は問題ありません。この種のテストにもっと良い戦略があるかどうかを確認するために質問しています。作業中の特定のエンコーダーの内部を明らかにできません。この種の問題をどのように解決するか一般的に尋ねていますが、内部は重要ではないと思います。特定の入力オブジェクトが常に同じ出力バイト配列を生成すると想定します。


4
myObjectからmyObjectにどうやって行くの{ 0x01, 0x02, 0xFF }ですか?そのアルゴリズムを分解してテストできますか?私が尋ねる理由は、現在、ある魔法の物が別の魔法の物を生み出すことを証明するテストがあるようです。唯一の自信は、1つの入力が1つの出力を生成することです。アルゴリズムを分解できれば、アルゴリズムの信頼性が高まり、魔法の入力や出力への依存度が低くなります。
Anthony Pegram 2013

3
@Codismエンコーダとデコーダが同じ場所で壊れた場合はどうなりますか?
ConditionRacer

2
テストは、定義により、何かを行い、期待どおりの結果が得られたかどうかを確認することです。これがテストの目的です。もちろん、すべてのコードを実行し、エッジケースやその他の奇妙さを確実にカバーするために、そのような十分なテストを行う必要があります。
Blrfl 2013

1
@ Justin984、まあ、今より深く行きます。私はこれらのプライベート内部をエンコーダーのAPIのメンバーとして公開しません。エンコーダーから完全に削除します。または、エンコーダーは別の場所、依存関係に委任します。それがテスト不可能なモンスターメソッドまたはヘルパークラスの束の間の戦いである場合、私は毎回ヘルパークラスを選択しています。しかし、繰り返しになりますが、私はコードを見ることができないので、現時点では無知の推論を行っています。しかし、テストの信頼性を高めたい場合は、より小さなメソッドでより少ないことを実行することが、そこに到達するための方法です。
Anthony Pegram 2013

1
@ Justin984仕様が変更された場合、テストで予想される出力を変更すると、失敗します。次に、渡すエンコーダーロジックを変更します。TDDがどのように機能することになっているのか正確に思われ、必要なときにのみ失敗します。これがどのように脆弱になるかはわかりません。
Daniel Kaplan

回答:


1

ちょっと厄介な状況です。エンコードする静的フォーマットがある場合は、最初の方法が適しています。それがあなた自身のフォーマットであり、他の誰もが2番目の方法よりもデコードする必要がなかったなら、それは進むべき道でしょう。しかし、あなたは本当にこれらのカテゴリーのどちらにも当てはまりません。

私がやろうとしていることは、抽象化のレベルで物事を分解しようとすることです。

だから私はビットレベルの何かから始めて、次のようなものをテストします

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

つまり、ビットライターは、intのような最も原始的なタイプのフィールドを書き出す方法を知っているという考えです。

より複雑な型は、次のようなものを使用して実装およびテストされます。

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() )

これにより、実際のビットがどのようにパックされるかについての知識が回避されることに注意してください。これは前のテストでテストされたものであり、このテストでは、それが機能することを前提としています。

次に、次の抽象化レベルで、

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() )

そのため、繰り返しになりますが、varstrings、日付、または数値が実際にエンコードされる方法についての知識は含めません。このテストでは、encodeObjectによって生成されるエンコーディングのみに関心があります。

最終結果は、日付の形式が変更された場合、実際に日付を含むテストを修正する必要がありますが、他のすべてのコードとテストは、日付が実際にエンコードされる方法に関係なく、コードを更新してその仕事、それらのテストはすべてうまくいきます。


私はこれが好き。これは、他のコメンターがそれをより小さな断片に分割することについて言っていたことだと思います。仕様が変更されても問題を完全に回避することはできませんが、改善されます。
ConditionRacer

6

依存します。エンコーディングが完全に修正され、すべての実装がまったく同じ出力を作成することになっている場合、サンプルの入力が期待される出力に正確にマッピングされていることを確認する以外に何をチェックしても意味がありません。これは最も明白なテストであり、おそらく最も簡単に記述できます。

MPEG標準のように、代替出力のある余裕がある場合(たとえば、入力に適用できる特定の演算子がありますが、エンコードの労力と出力品質またはストレージスペースを自由にトレードオフできます)の場合は、出力へのデコード戦略を定義し、それが入力と同じであることを確認します。または、エンコードが損失がある場合は、元の入力にかなり近いことを確認します。これはプログラムするのが困難ですが、エンコーダーに今後加えられる可能性のある改善から保護します。


2
デコーダを使用して値を比較するとします。エンコーダとデコーダの両方が同じ場所で故障した場合はどうなりますか?エンコーダーは正しくエンコードせず、デコーダーは正しくデコードしませんが、プロセスが2回誤って行われたため、入力/出力オブジェクトは正しいです。
ConditionRacer

@ Justin984は、いわゆる「テストベクトル」を使用して、エンコーダーとデコーダーをテストするために正確に使用できる入力/出力ペアを知っています
ラチェットフリーク

@ratchet freak期待値でのテストに戻ります。それは結構です、それは私が現在やっていることですが、それは少しもろいので、もっと良い方法があるかどうかを探していました。
ConditionRacer

1
標準を注意深く読み、すべてのルールのテストケースを作成する以外に、エンコーダーとデコーダーの両方に同じバグが含まれることを回避する方法はほとんどありません。たとえば、 "ABC"を "xyz"に変換する必要があるが、エンコーダーがそれを認識しておらず、デコーダーが "xyz"に遭遇した場合、それを理解できないと仮定します。プログラマーがその規則を認識していなかったため、手作りのテストケースには「ABC」シーケンスが含まれていません。また、エンコーダーとデコーダーの両方が問題を無視するため、ランダム文字列のエンコード/デコードによるテストは不合格になります。
user281377

1
知識が不足しているために自分で作成したデコーダーとエンコーダーの両方に影響するバグを見つけるのに役立つように、他のベンダーからエンコーダー出力を取得するように努力してください。また、サードパーティのデコーダでエンコーダの出力をテストしてみてください。その周りに代替手段はありません。
rwong 2013

3

テストしているencode(decode(coded_value)) == coded_valuedecode(encode(value)) == value。必要に応じて、テストにランダム入力を与えることができます。

エンコーダーとデコーダーの両方が相補的な方法で壊れている可能性はまだありますが、エンコード標準の概念的な誤解がない限り、それはかなりありそうにありません。エンコーダーとデコーダーのハードコードテストを(既に行っているように)実行すると、それを防ぐことができます。

これが機能することがわかっている別の実装にアクセスできる場合は、少なくともそれを使用して、単体テストでの使用が不可能であっても、実装が適切であることを確信できます。


補完的なエンコーダ/デコーダのエラーは一般的にありそうもないことに同意します。私の特定のケースでは、エンコーダ/デコーダクラスのコードは、データベースのルールに基づいて別のツールによって生成されます。したがって、補完的なエラーが時々発生します。
ConditionRacer

「無料エラー」はどのように発生しますか?これは、エンコードされた形式の外部仕様、つまり外部デコーダーがあることを意味します。
kevin cline 2013

外部という言葉の使い方がわかりません。しかし、データのエンコード方法とデコーダーに関する仕様があります。補足的なエラーとは、エンコーダーとデコーダーの両方が相補的な方法で動作するが、仕様から逸脱している場合です。元の質問の下のコメントに例があります。
ConditionRacer

エンコーダーがROT13を実装することになっていたが、誤ってROT14を実装し、デコーダーも実装した場合、decode(encode( 'a'))== 'a'ですが、エンコーダーはまだ壊れています。それよりはるかに複雑なことについては、おそらくそのようなことが起こる可能性ははるかに低くなりますが、理論的には可能です。
Michael Shaw、

@MichaelShawは雑学のほんの一部であり、ROT13のエンコーダーとデコーダーは同じです。ROT13はそれ自体が逆です。誤ってROT14を実装した場合、decode(encode(char))等しくありませんchar(等しくなりますchar+2)。
Tom Marthenal 2013

2

要件をテストします

要件が「デコード時に同等のオブジェクトを生成するバイトストリームにエンコードする」だけの場合は、デコードするだけでエンコーダーをテストします。エンコーダとデコーダの両方を作成している場合は、それらを一緒にテストしてください。「マッチングエラー」が発生することはありません。それらが一緒に機能する場合、テストは成功します。

データストリームに他の要件がある場合は、エンコードされたデータを調べてテストする必要があります。

エンコードされた形式が事前定義されている場合は、期待どおりの結果に対してエンコードされたデータを検証する必要があります。または、検証を行うために信頼できるリファレンスデコーダーを入手する必要があります。参照デコーダーを使用すると、フォーマット仕様を誤って解釈する可能性がなくなります。


1

使用しているテストフレームワークとパラダイムによっては、これまで述べたように、アレンジアサートアサートパターンを引き続き使用できます。

[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);
}

EncodeMyObject()このパターンの要件を理解し、このパターンを使用して有効な基準と無効な基準をテストすることができます。それぞれの要件を調整expectedし、デコーダーと同様にの期待される結果をハードコーディングします。

予想はハードコードされているため、大幅な変更がある場合、これらは脆弱になります。

パラメーター駆動型(Pexを確認)で自動化したり、DDDまたはBDDを実行している場合はgerkin / cucumberを確認したりできます。


1

あなたはあなたにとって何が重要かを決めるようになります。

オブジェクトがラウンドトリップを乗り切ることが重要であり、正確なワイヤー形式はそれほど重要ではありませんか?または、正確なワイヤ形式は、エンコーダとデコーダの機能の重要な部分ですか?

前者の場合は、オブジェクトが往復に耐えることを確認するだけではありません。エンコーダーとデコーダーの両方が完全に相補的な方法で壊れている場合、本当に気にする必要はありません。

後者の場合、ワイヤー形式が指定された入力に対して期待どおりであることをテストする必要があります。これは、形式を直接テストするか、参照実装を使用してテストすることを意味します。しかし、基本をテストしたら、追加の往復テストから価値を得ることができます。これは、ボリュームで書く方が簡単なはずです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.