Как и многие другие вещи, связанные с AutoFixture, все становится намного проще, если вы можете use more explicit domain modelling. Вместо моделирования StreetName
как string
, ввести объект домена для него:
public sealed class StreetName
{
private readonly string value;
public StreetName(string streetName)
{
value = streetName ?? throw new ArgumentNullException(nameof(streetName));
}
public override bool Equals(object obj)
{
var other = obj as StreetName;
if (other == null)
return base.Equals(obj);
return Equals(value, other.value);
}
public override int GetHashCode()
{
return value.GetHashCode();
}
public override string ToString()
{
return value;
}
public static implicit operator string(StreetName streetAddress)
{
return streetAddress.value;
}
public static implicit operator StreetName(string streetAddress)
{
return new StreetName(streetAddress);
}
}
Это один из тех шагов моделирования, которые являются болезненными в C# и Java, но был бы один вкладыш в F # или Haskell .. .
предположим, однако, что у нас есть список предопределенных названий улиц:
public static class StreetNames
{
public static IEnumerable<string> Values = new[] {
"221 B Baker St.",
"1313 Webfoot Walk",
"420 Paper St.",
"42 Wallaby Way"
/* More addresses go here... */ };
}
Теперь вы можете сказать тривиальным AutoFixture выбрать только из этого списка, используя ElementsBuilder
:
var fixture = new Fixture();
fixture.Customizations.Add(
new ElementsBuilder<StreetName>(StreetNames.Values.Select(s => (StreetName)s)));
На данный момент, хотя, это означает, что при создании StreetName
значения с AutoFixture, он будет выбирать из StreetNames.Values
, но он по-прежнему не собирается делать, когда вы попросите его, чтобы создать Address
значения. Вы можете обратиться (ха-ха), что вопрос с небольшим ISpecimenBuilder
:
public class StreetNameBuilder : ISpecimenBuilder
{
public object Create(object request, ISpecimenContext context)
{
var pi = request as PropertyInfo;
if (pi == null || pi.Name != "StreetName" || pi.PropertyType != typeof(string))
return new NoSpecimen();
var sn = context.Resolve(typeof(StreetName));
return (string)(StreetName)sn;
}
}
Теперь вы можете настроить Fixture
так:
var fixture = new Fixture();
fixture.Customizations.Add(
new ElementsBuilder<StreetName>(StreetNames.Values.Select(s => (StreetName)s)));
fixture.Customizations.Add(new StreetNameBuilder());
Это будет теперь создайте Address
значения с StreetName
значения выбрали из предопределенный список.
Если вы не можете изменить свою модель домена, вы можете добавить такой класс, как StreetName
. Просто добавьте его в базу тестового кода вместо базы производственного кода.
Да, это возможно: http://blog.ploeh.dk/2010/11/22/IntegratingAutoFixturewithObjectHydrator –