私のドメインは、次のような多くの単純な不変のクラスで構成されています。
public class Person
{
public string FullName { get; }
public string NameAtBirth { get; }
public string TaxId { get; }
public PhoneNumber PhoneNumber { get; }
public Address Address { get; }
public Person(
string fullName,
string nameAtBirth,
string taxId,
PhoneNumber phoneNumber,
Address address)
{
if (fullName == null)
throw new ArgumentNullException(nameof(fullName));
if (nameAtBirth == null)
throw new ArgumentNullException(nameof(nameAtBirth));
if (taxId == null)
throw new ArgumentNullException(nameof(taxId));
if (phoneNumber == null)
throw new ArgumentNullException(nameof(phoneNumber));
if (address == null)
throw new ArgumentNullException(nameof(address));
FullName = fullName;
NameAtBirth = nameAtBirth;
TaxId = taxId;
PhoneNumber = phoneNumber;
Address = address;
}
}
nullチェックとプロパティの初期化の記述はすでに非常に面倒になっていますが、現在、これらの各クラスの単体テストを記述して、引数の検証が正しく機能し、すべてのプロパティが初期化されていることを確認します。これは、非常に退屈な忙しい仕事のように感じられます。
実際の解決策は、C#が不変性とnull不可の参照型をネイティブにサポートすることです。しかし、その間に状況を改善するために私は何ができますか?これらすべてのテストを書く価値はありますか?クラスごとにテストを書くことを避けるために、そのようなクラスのコードジェネレータを書くことは良い考えでしょうか?
ここに私が答えに基づいて持っているものがあります。
nullチェックとプロパティの初期化を次のように簡素化できます。
FullName = fullName.ThrowIfNull(nameof(fullName));
NameAtBirth = nameAtBirth.ThrowIfNull(nameof(nameAtBirth));
TaxId = taxId.ThrowIfNull(nameof(taxId));
PhoneNumber = phoneNumber.ThrowIfNull(nameof(phoneNumber));
Address = address.ThrowIfNull(nameof(address));
Robert Harveyによる次の実装を使用します。
public static class ArgumentValidationExtensions
{
public static T ThrowIfNull<T>(this T o, string paramName) where T : class
{
if (o == null)
throw new ArgumentNullException(paramName);
return o;
}
}
nullチェックのテストはGuardClauseAssertion
from を使用して簡単に行えますAutoFixture.Idioms
(提案に感謝、Esben Skov Pedersen):
var fixture = new Fixture().Customize(new AutoMoqCustomization());
var assertion = new GuardClauseAssertion(fixture);
assertion.Verify(typeof(Address).GetConstructors());
これはさらに圧縮できます。
typeof(Address).ShouldNotAcceptNullConstructorArguments();
この拡張メソッドの使用:
public static void ShouldNotAcceptNullConstructorArguments(this Type type)
{
var fixture = new Fixture().Customize(new AutoMoqCustomization());
var assertion = new GuardClauseAssertion(fixture);
assertion.Verify(type.GetConstructors());
}