我的领域包括许多简单的不可变类,如下所示:
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;
}
}
编写空检查和属性初始化已经非常繁琐,但是目前我为这些类中的每一个编写单元测试,以验证参数验证是否正常工作以及所有属性都已初始化。感觉就像无聊的忙碌而带来的好处是不相称的。
真正的解决方案是让C#原生支持不变性和非空引用类型。但是与此同时,我该怎么做才能改善这种情况呢?是否值得编写所有这些测试?为此类编写代码生成器以避免为每个类编写测试是一个好主意吗?
这是我现在根据答案得到的。
我可以简化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;
}
}
使用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());
}