web-dev-qa-db-ja.com

ユーティリティクラスをラップし、単体テストの目的で挿入する

Java.nio.file.Filesのように、他のユーティリティクラスに依存するクラスをテストするのは非常に難しいことがわかりました。 powermockのような他の重いモックフレームワークを使用しない限り、クラシックユニットテストスタック(junit、mockito、..)を使用してそれらをモックすることも不可能です。また、mavenプラグイン(surefire、jacoco、オフラインインストルメンテーションなど)と統合しようとすると、悪夢になります。

ユニットテストを簡単にするために私が決めたのは、このユーティリティクラスを通常のインスタンス化可能なクラス内にラップし、次のようにメインクラスに挿入することです。

public class IOManager {
    private final FileSystemUtilsWrapper fileSystemUtilsWrapper;
    private final HttpUtilsWrapper httpUtilsWrapper;

    public IOManager() {
        this.fileSystemUtilsWrapper = new FileSystemUtilsWrapper();
        this.httpUtilsWrapper = new HttpUtilsWrapper();
    }

    public IOManager(
            FileSystemUtilsWrapper fileSystemUtilsWrapper, 
            HttpUtilsWrapper httpUtilsWrapper
    ) {
        if (fileSystemUtilsWrapper == null 
            || httpUtilsWrapper == null) {
            throw new NullPointerException("...");
        }

        this.fileSystemUtilsWrapper = fileSystemUtilsWrapper;
        this.httpUtilsWrapper = httpUtilsWrapper;
    }

    public boolean doIOOperations(String filePath, String url) throws IOException {
        if (fileSystemUtilsWrapper.isMyFileReachable(filePath) 
            && httpUtilsWrapper.isMyURLReachable(url)) {
            // do something and returns IO state
        }
        return false;
    }
}

/**
 * FileSystem utils wrapper
 */
class FileSystemUtilsWrapper {
    public boolean isMyFileReachable(String filePath) {
        Path path = Paths.get(filePath);
        return Files.exists(path) && Files.isWritable(path);
    }
}

/**
 * http utils wrapper
 */
class HttpUtilsWrapper {
    public boolean isMyURLReachable(String url) throws IOException {
        HttpURLConnection urlConnection = (HttpURLConnection) new URL(url)
            .openConnection()
        ;
        // i keep it simple
        return urlConnection.getResponseCode() == 200;
    }
}

簡単な洞察を得るために、単純にしています。

SUT内のユーティリティクラスを直接使用し、I/Oリソースを模擬することも検討したことに注意してください(ダミーファイルを作成および削除し、wiremockを使用してhttpエンドポイントを模擬します)。最も簡単な方法のように見えるかもしれませんが、IMOは単体テストではなく統合テストであるため、そうするべきではありません。

私の分析について、あなたの意見を聞いていただければ幸いです。よろしくお願いいたします。

4
isqo

ここで私が気に入っているのは、オーバーライド可能なデフォルト値です。それは良い。ハードコーディングを回避します。テストに関係なくお勧めします。そしてそうしました before

IOManagerがラッパーをデフォルト値としてだけでなくタイプとしても使用しているのが好きだと思います。ラッパーを置き換えることを意味するものは、それらから継承する必要があります。ラッパーは具体的ではなく、抽象的です。これが yo-yo問題 の始まりであることを意味します。

その問題からあなたを導くことができる良い原則は 依存関係逆転の原理 です。それはそれを教えています:

  • 高レベルのモジュールは、低レベルのモジュールに依存するべきではありません。どちらも抽象化に依存する必要があります。
  • 抽象化は詳細に依存すべきではありません。詳細は抽象化に依存する必要があります。

ここでインターフェイスを使用する必要があると言っているのではありません。しかし、少なくともFileSystemUtilsおよびHttpUtilsという名前の抽象クラスがあり、IOManagerが型として使用できるとしたら、それはいいことです。こうすることで、コードを取得し、HttpsUtilsWrapperを作成してnew URL(url)からnew SecureURL(url)に切り替えるときに、メンテナンスコーダーを使用せずに未使用のコードを確認することはありません。オーバーライドされたコード。

1
candied_orange