web-dev-qa-db-ja.com

関数スタイルは依存関係のモックをどのように支援しますか?

最近のJava雑誌の問題におけるケントベックとのインタビューから:

Binstock:マイクロサービスについて説明しましょう。マイクロサービスのテストファーストは、機能するためにいくつかのサービスが他のサービスの束全体の存在を必要とするという意味で複雑になるようです。同意しますか?

ベック:1つの大きなクラスまたはたくさんの小さなクラスを持つことについては、同じ一連のトレードオフのようです。

Binstock:そうです、私が推測する場合を除いて、特定のサービスをテストできるシステムをセットアップできるようにするためには、ここでは非常に多くのモックを使用する必要があります。

ベック:私は同意しません。命令型のスタイルの場合、たくさんのモックを使用する必要があります。外部依存関係がコールチェーンの上位に集められる関数型のスタイルでは、私は思いませんそれは必要です。私はあなたがユニットテストから多くのカバレッジを得ることができると思います。

彼はどういう意味ですか?機能的なスタイルで、外部の依存関係をあざけることからどのように解放できますか?

10
Dan

純粋な関数 は次のようなものです:

  1. alwaysは、同じ引数を指定すると同じ結果を返します
  2. 目に見える副作用はありません(例:状態の変化)

ユーザーログインを処理するコードを記述しているとします。ここでは、提供されたユーザー名とパスワードが正しいことを確認し、失敗した試行が多すぎる場合にユーザーがログインできないようにします。命令型では、コードは次のようになります。

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

これは純粋な関数ではないことは明らかです。

  1. この関数は、特定のusernamepasswordの組み合わせに対して常に同じ結果を与えるとは限りません。結果はデー​​タベースに保存されているユーザーレコードにも依存するためです。
  2. 関数はデータベースの状態を変更できます。つまり、副作用があります。

また、この関数を単体テストするには、2つのデータベース呼び出しFindUserRecordFailedLoginAttemptをモックアウトする必要があることにも注意してください。

このコードをより機能的なスタイルにリファクタリングすると、次のような結果になる可能性があります。

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関数は純粋な関数であるため、外部の依存関係をモックする必要なく、コアユーザー認証ロジックを単体テストできることに注意してください。これは、データベースとの対話が呼び出しスタックの上位で処理されるためです。

8
Justin