web-dev-qa-db-ja.com

単体でユニットをテストする意味がない場合は、単体テストをスキップして統合テストを直接書くのは正しいことですか?

マーティン・ファウラーの記事によると、

https://martinfowler.com/bliki/TestPyramid.html

統合テストよりも多くの単体テストを作成することをお勧めします。これは理想的に、すべてのEdgeケースを含むユニットテストですべての作業ユニットを記述する必要があることを意味しますか?たとえば、データベースが返すものに大きく依存し、認証ユーザーをシングルトンインスタンスに格納するだけの操作である認証モジュールなど、ユニットテストに意味がないとしたらどうでしょうか。

単体テストをスキップして統合テストを直接書くのは正しいことですか?

認証モジュールに対してユニットテストを引き続き記述しなければならない理由は1つだけですが、ドメインレイヤーと永続化レイヤーの間に抽象化レイヤーを導入して、選択したORMに認証が密接に結合されないようにする必要があります。 ORMの切り替えが簡単になります。

7
Xegara

私の見解では、統合テストは最も重要なテストです。なぜなら、アプリケーションがすべて組み合わされたときに実際にが機能するかどうかを通知するテストだからです。

しかし、彼らにはいくつかの欠点があります

  • テストするには、すべての依存関係を持つ実行中のシステムが必要です
  • 彼らは走るのが遅い
  • 一部のテストは必ず破壊的であるため、ライブ環境に対して実行できません。
  • 彼らは「トップレベル」のテストになる傾向があります。彼らはあなたに何かが間違っていると言いますが、それが何であるかではありません。

ユニットテストにはこれらの欠点がないため、アプリ全体をデプロイすることなく、開発マシンで数百のユニットテストをすばやく実行できます。

したがって、統合テストがあるからといってユニットテストをスキップしないでください。また、単体テストがあるために統合テストをスキップしないでください。

一部のコードを保存するために実行できる1つのトリックは、入力または設定に応じて依存関係をモックするかどうかをテストできるようにすることで、テストをユニットと統合の両方として機能させることです。

これにより、テストを単体テストとしてすばやく実行して、コードを検証できます。次に、統合として、セットアップとデプロイメントを検証します。

編集:統合と単体テストをコードの重複なしに組み合わせる方法の例。

public class AuthServiceTests
{
    [TestCase("api.myapp.com/auth", "databaseuser", true, Category="Integration")]
    [TestCase("mock", "mockuser", true, Category="Unit")]
    public void IsUserTest(string target, string username, bool expected)
    {
        //SetUp
        if(target != "mock")
        {
            httpClient = new HttpClient(target);
        }
        else
        {
            httpClient = new MockHttpClient();
        }
        var authService = new AuthService(httpClient);

        var actual = authService.IsUser(user); 
//obvs this might fail if the databases changes in the integration version
//or the db isnt setup right, or the connection string in the api is wrong etc

//the mock one will only fail if the code has a bug of some kind, say it doesnt work with long usernames, or emoticons or something

        Assert.AreEqual(expected, actual);
    }
}
9
Ewan

現実の世界では、100%のテストカバレッジに到達することはほとんどなく、すべてのコーナーケースを把握することはできません。したがって、単体テストと統合テストの両方で、できる限り多くをカバーしようとします。

モジュールがサードパーティのソースに依存しているためにユニットテストを作成しても意味がないのは、デザインでモックが許可されていないためです。

外部依存関係があるモジュール(ファイルシステムやデータベースなど)がある場合、その外部依存関係をエミュレートして、好きなように返すことができるはずです。次に、完全に有効な単体テストが作成されます。これは、ファイルまたはレコードなどの実際の存在に依存せず、複数回実行できます。

通常、これには設計コストがほとんどかからず、テスト容易性とモジュール化が大幅に向上します。

たとえば、Authenticatorの場合、コードはConnection/Databaseオブジェクトを受け取ってクエリを実行する必要があるので、モックデータベースに渡して、有効/無効なデータを自由に返すことができます。

5
BgrWorker

たとえば、データベースが返すものに大きく依存し、認証ユーザーをシングルトンインスタンスに格納するだけの操作である認証モジュールなど、ユニットテストに意味がないとしたらどうでしょうか。

これは誤った議論です。ユニットの唯一の目的が検証でなく保存することである場合、ユニットテストはその目的のみをテストする必要があります。 DBが返すものの検証はこのテストの範囲外であり、別の場所でテストする必要があります。 「資格情報バリデーター」が欠落している可能性があります。

ただし、テストの一部として、ユニットがそれ自体が完了したと見なす前に資格情報検証ツールを使用する場合があります。

正式な答えは、テストするものを何も提供しない関数は目的を果たさず、存在してはならないということです。

2
Martin Maat