私には3つの機能があります。
ValidateUsername()
:いくつかのルールに従って、文字列が有効なユーザー名であるかどうかを決定しますSetUsername()
検証に合格した場合に文字列をユーザーのユーザー名として設定しますAddNewUser()
:新しいユーザーを作成し、とりわけSetUsername()
を使用して文字列をユーザー名として設定します指定されたユーザー名文字列が正しくない場合、3つすべてが失敗するはずですが、ValidateUsername()
はルールが存在する場所です。これを単体テストするにはどうすればよいですか? ValidateUsername()
でのみ検証をテストすると、誰かが誤って検証を省略し、AddNewUser()
にユーザー名を直接設定する可能性があります。一方、すべての関数に対して異なる検証テストケースをすべて繰り返すことは逆効果です。ベストプラクティスは何ですか?
ユニットテストは、作業単位をテストするつもりであるため、そのように呼ばれます。この場合、各メソッドは作業単位であり、それらの間の全体的な統合ではありません。
ValidateUsername()メソッドのビジネス要件が特定の要件に従ってユーザー名を検証することであると仮定しましょう。この場合の単体テストでは、検証が正しく、ルールが正しく実装されていることを確認する必要があります。
今、他のメソッドのように見えますが、ビジネス要件の1つは検証メソッドを呼び出すことです。 ValidateUsernameが実際に呼び出されたことを確認します。正しく実行されたかどうかは関係ありません。その部分は、ValidateUsernameユニットの以前のテストでテストされます。
1つの無効な名前と1つの有効な名前で他の2つの関数をテストすることにより、検証関数が呼び出されたことを確認できます。または、ロジックをUsernameValidatorとUserServiceに分離し、バリデーターをユーザーサービスに挿入することもできます。次に、バリデーターをモックしてユーザーサービスをテストします。
質問を正しく解釈している場合、AddNewUser()
はSetUsername()
を呼び出し、SetUsername()
はValidateUsername()
を呼び出します。 2つのオプションが思い浮かびます:
1)既存の構造を保持し、依存関係の「ハッピーパス」を想定しながら、各関数が担当する「1つの事柄」の単体テストを記述します。したがって、ValidateUsername()
テストは有効なユーザー名と無効なユーザー名をチェックし、SetUsername()
テストは有効なユーザー名を渡して、ユーザー名が正しい場所にあることを確認し、AddNewUser()
testは有効なユーザー構築データを渡し、ユーザーがそのデータを使用して追加されたことを確認します。
2)機能が互いに依存しなくなるように、構造を「よりフラットな」ものに変更します。したがって、AttemptToAddUser()
、次にValidateUsername()
、次にSetUsername()
を呼び出すAddNewUser()
関数がすべて、コールスタックの同じレベルにある場合があります。
最適な答えは正確なコードに大きく依存しますが、私はそれが#1か#1と#2の間の何かであると予想します。 ValidateUsername()
をAddNewUser()
と同じレベルに引き上げることは完全に理にかなっているかもしれませんが、SetUsername()
関数が外部で使用されることを想像するのは難しいですAddNewUser()
関数(Userオブジェクトなどのメソッドではないと想定)。
そしてもちろん、単体テストがどのように機能するかに関係なく、これらのすべての機能を一緒にチェックする少数の統合テストがおそらく必要です。