最近のJava雑誌の問題におけるケントベックとのインタビューから:
Binstock:マイクロサービスについて説明しましょう。マイクロサービスのテストファーストは、機能するためにいくつかのサービスが他のサービスの束全体の存在を必要とするという意味で複雑になるようです。同意しますか?
ベック:1つの大きなクラスまたはたくさんの小さなクラスを持つことについては、同じ一連のトレードオフのようです。
Binstock:そうです、私が推測する場合を除いて、特定のサービスをテストできるシステムをセットアップできるようにするためには、ここでは非常に多くのモックを使用する必要があります。
ベック:私は同意しません。命令型のスタイルの場合、たくさんのモックを使用する必要があります。外部依存関係がコールチェーンの上位に集められる関数型のスタイルでは、私は思いませんそれは必要です。私はあなたがユニットテストから多くのカバレッジを得ることができると思います。
彼はどういう意味ですか?機能的なスタイルで、外部の依存関係をあざけることからどのように解放できますか?
純粋な関数 は次のようなものです:
ユーザーログインを処理するコードを記述しているとします。ここでは、提供されたユーザー名とパスワードが正しいことを確認し、失敗した試行が多すぎる場合にユーザーがログインできないようにします。命令型では、コードは次のようになります。
bool UserLogin(string username, string password)
{
var user = _database.FindUser(username);
if (user == null)
{
return false;
}
if (user.FailedAttempts > 3)
{
return false;
}
// Password hashing omitted for brevity
if (user.Password != password)
{
_database.RecordFailedLoginAttempt(username);
}
return true;
}
これは純粋な関数ではないことは明らかです。
username
とpassword
の組み合わせに対して常に同じ結果を与えるとは限りません。結果はデータベースに保存されているユーザーレコードにも依存するためです。また、この関数を単体テストするには、2つのデータベース呼び出しFindUser
とRecordFailedLoginAttempt
をモックアウトする必要があることにも注意してください。
このコードをより機能的なスタイルにリファクタリングすると、次のような結果になる可能性があります。
bool UserLogin(string username, string password)
{
var user = _database.FindUser(username);
var result = UserLoginPure(user, password);
if (result == Result.FailedAttempt)
{
_database.RecordFailedLoginAttempt(username);
}
return result == Result.Success;
}
Result UserLoginPure(User user, string pasword)
{
if (user == null)
{
return Result.UserNotFound;
}
if (user.FailedAttempts > 3)
{
return Result.LoginAttemptsExceeded;
}
if (user.Password != password)
{
return Result.FailedAttempt;
}
return Result.Success;
}
UserLogin
関数はまだ純粋ではありませんが、UserLoginPure
関数は純粋な関数であるため、外部の依存関係をモックする必要なく、コアユーザー認証ロジックを単体テストできることに注意してください。これは、データベースとの対話が呼び出しスタックの上位で処理されるためです。