Meu domínio consiste em muitas classes simples e imutáveis como esta:
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;
}
}
Escrever as verificações nulas e a inicialização da propriedade já está ficando muito entediante, mas atualmente escrevo testes de unidade para cada uma dessas classes para verificar se a validação de argumentos funciona corretamente e se todas as propriedades foram inicializadas. Parece um trabalho extremamente chato, com benefícios incomensuráveis.
A solução real seria o C # suportar nativamente tipos de referência imutáveis e não anuláveis. Mas o que posso fazer para melhorar a situação nesse meio tempo? Vale a pena escrever todos esses testes? Seria uma boa idéia escrever um gerador de código para essas classes, para evitar escrever testes para cada uma delas?
Aqui está o que eu tenho agora com base nas respostas.
Eu poderia simplificar as verificações nulas e a inicialização da propriedade para ficar assim:
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));
Usando a seguinte implementação por 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;
}
}
É fácil testar as verificações nulas usando o comandoGuardClauseAssertion from AutoFixture.Idioms(obrigado pela sugestão, Esben Skov Pedersen ):
var fixture = new Fixture().Customize(new AutoMoqCustomization());
var assertion = new GuardClauseAssertion(fixture);
assertion.Verify(typeof(Address).GetConstructors());
Isso pode ser compactado ainda mais:
typeof(Address).ShouldNotAcceptNullConstructorArguments();
Usando este método de extensão:
public static void ShouldNotAcceptNullConstructorArguments(this Type type)
{
var fixture = new Fixture().Customize(new AutoMoqCustomization());
var assertion = new GuardClauseAssertion(fixture);
assertion.Verify(type.GetConstructors());
}