web-dev-qa-db-ja.com

Builderパターンの単体テスト可能性

クラスの複雑なロジックを必要とする可能性のある依存関係とパラメーターの設定を支援するためのビルダーパターンを見ています。しかし、私が見た例から、ビルダーパターンはあまりテストしにくいようです。私が取った例 ここ

 public static class Builder {
    private long accountNumber; //This is important, so we'll pass it to the constructor.
    private String owner;
    private String branch;
    private double balance;
    private double interestRate;
    public Builder(long accountNumber) {
        this.accountNumber = accountNumber;
    }
    public Builder withOwner(String owner){
        this.owner = owner;
        return this;  //By returning the builder each time, we can create a fluent interface.
    }
    public Builder atBranch(String branch){
        this.branch = branch;
        return this;
    }
    public Builder openingBalance(double balance){
        this.balance = balance;
        return this;
    }
    public Builder atRate(double interestRate){
        this.interestRate = interestRate;
        return this;
    }
    public BankAccount build(){
        //Here we create the actual bank account object, which is always in a fully initialised state when it's returned.
        BankAccount account = new BankAccount();  //Since the builder is in the BankAccount class, we can invoke its private constructor.
        account.accountNumber = this.accountNumber;
        account.owner = this.owner;
        account.branch = this.branch;
        account.balance = this.balance;
        account.interestRate = this.interestRate;
        return account;
    }
}

すべての関数はビルダーを返し、プライベートメンバーをビルダークラスに設定するため、関数が正しいかどうかをテストすることは困難です。この場合、setterロジックは非常に単純なのでテストする必要がないため、問題ないかもしれません。しかし、ビルドされるクラスのパラメーターを設定するために関数がより複雑な作業を行う必要がある場合はどうでしょうか。たとえば、関数がリストを受け取り、いくつかの値をフィルタリングおよびグループ化して辞書を作成する必要があるとします。

ビルダーパターンは通常どのようにテストされますか?

よりテストしやすくするために考えられる方法:

  1. すべてのロジックを別のクラスに移動し、ビルダーはそのクラスを呼び出すだけです。欠点は、ビルダーがあまり役に立たなくなるようです。

  2. 構築中のクラスを推論してテストします。ただし、これは統合テストになり、非常に複雑になる可能性があります。

2
roverred

ビルダーの唯一の責任は明確です。たとえば、BankAccountオブジェクトなどのオブジェクトを作成することです。このタスクが正しいかどうかの最終的なテストは、そのオブジェクトの最終状態です。ビルダーが内部でどのように機能するかは、テストすべきではない実装の詳細です。

したがって、ビルダーをテストする簡単な方法は、そのメソッドを呼び出し、buildメソッドの出力を確認することです。あなたの例ではBankAccountには興味のある各メンバーのパブリック「ゲッター」があるので、その内容はテストで非常に簡単に検証できるはずです。

しかし、ビルドされるクラスのパラメーターを設定するために関数がより複雑な作業を行う必要がある場合はどうでしょうか。

それは、「より複雑な作業」の意味によって異なります。テストを追加するだけで十分な場合もあります。コードカバレッジやブランチカバレッジ分析は、これらのテストを選択するのに役立ちます。ビルダー自体が非常に複雑なアルゴリズムを使用して作業を行う場合、その複雑なアルゴリズムは独自のクラスの価値がある可能性があり、単体で単体テストすることができます。しかし、本当に必要になるまで、このアプローチを強調しません。

そしてあなたのコメントに:

ビルドされたクラスのメンバーを通じて最終的な状態をテストしようとするときに私が見る1つの問題は、それらのメンバーがパブリックであるかゲッターを持っている必要があることを意味します

意味のあるオブジェクトの内部状態は、ほとんどの場合、何らかの方法で読み取りアクセス可能です。

  • パブリックゲッターを介して直接

  • または間接的に他のパブリックメソッドの動作に何らかの影響を与える

そうしないと、内部状態は不要になります。したがって、構築されたオブジェクトのパブリックインターフェイスを見ると、通常はコンテンツを検証する方法が見つかります。

8
Doc Brown