web-dev-qa-db-ja.com

ユニットテストでドメインオブジェクトの作成をテストする

私はこのようなユニットテストを持っています:

[Test]
public void Should_create_person()
{
     Assert.DoesNotThrow(() => new Person(Guid.NewGuid(), new DateTime(1972, 01, 01));
}

ここではPersonオブジェクトが作成されている、つまり検証が失敗しないことを主張しています。たとえば、Guidがnullの場合、または生年月日が1900年1月1日より前の場合、検証は失敗し、例外がスローされます(テストが失敗したことを意味します)。

コンストラクターは次のようになります。

public Person(Id id, DateTime dateOfBirth) :
        base(id)
    {
        if (dateOfBirth == null)
            throw new ArgumentNullException("Date of Birth");
        elseif (dateOfBith < new DateTime(1900,01,01)
            throw new ArgumentException("Date of Birth");
        DateOfBirth = dateOfBirth;
    }

これはテストに良いアイデアですか?

:ドメインモデルをユニットテストするための古典主義的アプローチに従っています任意の方位を保持します。

11
w0051977

これは有効なテストです(かなり熱狂的ではありますが)コンストラクターロジックをテストするためにときどきテストしますが、Laivがコメントで述べたように、理由を自問する必要があります。

コンストラクタが次のようになっている場合:

public Person(Guid guid, DateTime dob)
{
  this.Guid = guid;
  this.Dob = dob;
}

スローするかどうかをテストすることには多くのポイントがありますか?パラメータが正しく割り当てられているかどうかは理解できますが、テストはやりすぎです。

ただし、テストが次のような場合:

public Person(Guid guid, DateTime dob)
{
  if(guid == default(Guid)) throw new ArgumentException("Guid is invalid");
  if(dob == default(DateTime)) throw new ArgumentException("Dob is invalid");

  this.Guid = guid;
  this.Dob = dob;
}

その後、テストはより適切になります(実際にコードのどこかで例外をスローしているため)。

私が言えることの1つは、一般に、コンストラクターに多くのロジックを含めることは悪い習慣です。基本的な検証(上記で行ったnull/defaultチェックなど)は問題ありません。しかし、データベースに接続して誰かのデータをロードしている場合は、コードが実際に匂いを嗅ぎ始めるところです...

このため、(多くのロジックが実行されているために)コンストラクターにテストする価値がある場合、おそらく他の何かが間違っています。

ほとんどの場合、ビジネスロジックレイヤーでこのクラスをカバーする他のテストが行​​われます。コンストラクターと変数の割り当ては、これらのテストから完全にカバーされます。したがって、コンストラクタ専用のテストを追加しても意味がありません。しかし、何も白黒ではありません。コードレビューを行っていれば、これらのテストに反対することは何もありません。しかし、ソリューションの他の場所のテストに加えて、テスト以外にも多くの価値があるかどうか疑問に思います。

あなたの例では:

public Person(Id id, DateTime dateOfBirth) :
        base(id)
    {
        if (dateOfBirth == null)
            throw new ArgumentNullException("Date of Birth");
        elseif (dateOfBith < new DateTime(1900,01,01)
            throw new ArgumentException("Date of Birth");
        DateOfBirth = dateOfBirth;
    }

検証を行うだけでなく、基本コンストラクターも呼び出します。私にとってこれは、コンストラクタ/検証ロジックが2つのクラスに分割されるため、これらのテストを行う理由が増えます。これにより、可視性が低下し、予期しない変更のリスクが高まります。

[〜#〜] tldr [〜#〜]

これらのテストにはいくつかの価値がありますが、検証/割り当てロジックは、ソリューションの他のテストでカバーされる可能性があります。これらのコンストラクターに多くのロジックがあり、重要なテストが必要な場合は、そこには厄介なコードのにおいが潜んでいることがわかります。

18
Liath

ここではすでに良い答えですが、もう1つ言及する価値があると思います。

[〜#〜] tdd [〜#〜] "本を読む"場合、コンストラクタが実装される前であっても、最初にコンストラクタを呼び出すテストを記述する必要があります。そのテストは、コンストラクターの実装内にゼロ検証ロジックがある場合でも、実際に提示したテストのように見える可能性があります。

TDDの場合、最初に次のような別のテストを書く必要があることにも注意してください。

_  Assert.Throws<ArgumentException>(() => new Person(Guid.NewGuid(), 
        new DateTime(1572, 01, 01));
_

beforeコンストラクタにDateTime(1900,01,01)のチェックを追加します。

TDDのコンテキストでは、示されたテストは完全に理にかなっています。

12
Doc Brown