私が実装しているインターフェースの一部であるメソッドがあります。このメソッドは、乱数ジェネレータを使用して出力を生成し、それを呼び出し側メソッドに返す別のプライベートメソッドを呼び出します。呼び出しメソッドをテストしたい。どうやってやるの?これはテスト中のメソッドです:
@Override
public String generate(int wordCount) {
StringBuilder sentence = new StringBuilder();
List<String> selectedStrings = selectRandomStringsFromInternalVocabulary(wordCount, new Random());
selectedStrings.sort(Comparator.<String>naturalOrder());
swapOddIndexedStringsWithEvenIndexedStrings(selectedStrings);
for (String Word: selectedStrings)
sentence.append(Word)
.append(" ");
return sentence.toString().trim();
}
これは、乱数ジェネレータを使用する方法です。
private List<String> selectRandomStringsFromInternalVocabulary(int wordCount, Random random) {
List<String> selectedStrings = new ArrayList<>();
int wordCountInVocabulary = internalVocabulary.size();
while (wordCount-- != 0) {
int stringIndex = random.nextInt(wordCountInVocabulary);
selectedStrings.add(internalVocabulary.get(stringIndex));
}
return selectedStrings;
}
私ができると思ったことがいくつかあります。1. 2番目のメソッドをpackage-privateにしてテストします。しかし、私がそれを避けることができるなら、私はプライベートメソッドをテストしたくないです。 2. Randomをパラメーターとして呼び出し関数に追加し、テスト中にモックを渡します。ただし、インターフェイスの一部とそれを実装する他のクラスはRNGを使用しません。さらに、クライアントに実装の詳細を知らせたくありません。
私はこれらの質問を終えました:1. 不確定な出力を伴うユニットテストメソッド 2. ランダムな動作をする関数のユニットテスト
しかし、提案は上記で述べたものと同様です。
「私が何をしようとしているのか 証明 ?」(注:「証明」は数学的な意味で使用されますが、単に何かの妥当性をテストするためです)。単体テストは、アプリケーションが設計されたパラメーター内で機能していることをテストするためにあります。正当性のテストが異なると、異なるアプローチが必要になります。
それで十分かもしれません。これらのタイプのテストもかなり堅牢です。乱数ジェネレーターの実装が変更されても壊れません。内部機能を利用するために、引き続き外部インターフェイスを使用しています。
この場合、文字列の特定のシーケンスをテストすることは、もろく、ほんのわずかしか役に立たないと証明するでしょう。あなたの例では、それほど多くのブランチパスを心配する必要はありません。契約について考え、それが本当に何であるかを考えてランダム性を確保し、受け入れる必要があります。
注意すべき重要なことは、ランダム性を証明していないということです。メソッドが期待どおりに動作することを確認しています。
ランダム性をテストする場合、乱数の広がりが期待どおりであることを証明するために、いくつかの統計分析を行う必要があります。これらの種類のテストでは、単語の数学的な意味での「証明」が必要です。単体テストはその仕事に適したツールではありません。
依存性注入を使用します。 IRandomNumberGeneratorインターフェイスを作成し、それを必要なクラス(コンストラクターの引数として)または関数(パラメーターとして)に挿入します。次のように簡単にすることができます。
interface IRandomNumberGenerator
{
int GetRandomNumber();
}
次に、そのインターフェースを実装する2つのクラスを作成します。実数生成クラスとモック。モック実装は事前定義された数を返し、実際の実装は実際の乱数を返します。モック実装でテストします。
class MockRandomNumberGenerator : IRandomNumberGenerator
{
private int _fakeRandomNumber;
public MockRandomNumberGenerator(int fakeRandomNumber)
{
_fakeRandomNumber = fakeRandomNumber
}
public int GetRandomNumber()
{
return _fakeRandomNumber;
}
}
実際の乱数が生成されている状態テストがない場合、その場合、テスト結果は再現できなくなります。 Javaに慣れていないため、C#構文を使用しています。
テスト時:
指定した特定のシードで乱数ジェネレータを使用します。次に、常に同じシーケンスを取得します。これにより、テストが可能になります。
Randomをテスト中のクラスのメンバーにします。テスト用にモック/固定シード値を注入し、本番用コードには実際の値を注入します。 依存性注入 を使用します。
_class someclassname {
private Random _rng;
constructor(Random rng)
public String generate(int wordCount)
private selectRandomStringsFromInternalVocabulary(int wordCount) //words would be a better name here btw
}
_
語彙を依存関係にすることもできます。
また、とにかくこれで修正されるバグがあるかもしれません。私はJavaについては知りませんが、非常に頻繁に呼び出されるメソッド(たとえば、高速ループ)でnew Random()
を実行すると、デフォルトのコンストラクターが時間ベースのシードを使用すると、うまくいかない場合があります。