AutoFixture - 配置夹具以限制字符串生成长度

Ric*_*ues 37 c# autofixture

对某些类型使用自动混合构建方法时,如何限制生成的字符串的长度以填充对象字符串属性/字段?

Mar*_*ann 50

使用Build方法本身,没有那么多选项,但你可以做这样的事情:

var constrainedText = 
    fixture.Create<string>().Substring(0, 10);
var mc = fixture
    .Build<MyClass>()
    .With(x => x.SomeText, constrainedText)
    .Create();
Run Code Online (Sandbox Code Playgroud)

但是,就个人而言,我看不出这是更好或更容易理解的:

var mc = fixture
    .Build<MyClass>()
    .Without(x => x.SomeText)
    .Create();
mc.SomeText =
    fixture.Create<string>().Substring(0, 10);
Run Code Online (Sandbox Code Playgroud)

就个人而言,我很少使用这种Build方法,因为我更喜欢基于约定的方法.这样做,至少有三种方法来约束字符串长度.

第一个选项只是约束所有字符串的基础:

fixture.Customizations.Add(
    new StringGenerator(() =>
        Guid.NewGuid().ToString().Substring(0, 10)));
var mc = fixture.Create<MyClass>();
Run Code Online (Sandbox Code Playgroud)

上面的自定义将所有生成的字符串截断为10个字符.但是,由于默认属性赋值算法会将属性的名称添加到字符串中,因此最终结果mc.SomeText将具有类似"SomeText3c12f144-5"的值,因此这可能不是您想要的大多数时间.

另一个选择是使用该[StringLength]属性,正如Nikos所指出的:

public class MyClass
{
    [StringLength(10)]
    public string SomeText { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

这意味着您只需创建一个实例,而无需明确说明属性的长度:

var mc = fixture.Create<MyClass>();
Run Code Online (Sandbox Code Playgroud)

我能想到的第三个选择是我最喜欢的.这添加了一个特定的目标约定,该约定规定每当要求fixture为名称为"SomeText"且类型为string的属性创建值时,结果字符串应为10个字符长:

public class SomeTextBuilder : ISpecimenBuilder
{
    public object Create(object request, ISpecimenContext context)
    {
        var pi = request as PropertyInfo;
        if (pi != null && 
            pi.Name == "SomeText" &&
            pi.PropertyType == typeof(string))

            return context.Resolve(typeof(string))
                .ToString().Substring(0, 10);

        return new NoSpecimen();
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

fixture.Customizations.Add(new SomeTextBuilder());
var mc = fixture.Create<MyClass>();
Run Code Online (Sandbox Code Playgroud)

这种方法的优点在于它只留下SUT并且仍然不会影响任何其他字符串值.


您可以将其概括SpecimenBuilder为任何类和长度,如下所示:

public class StringPropertyTruncateSpecimenBuilder<TEntity> : ISpecimenBuilder
{
    private readonly int _length;
    private readonly PropertyInfo _prop;

    public StringPropertyTruncateSpecimenBuilder(Expression<Func<TEntity, string>> getter, int length)
    {
        _length = length;
        _prop = (PropertyInfo)((MemberExpression)getter.Body).Member;
    }

    public object Create(object request, ISpecimenContext context)
    {
        var pi = request as PropertyInfo;

        return pi != null && AreEquivalent(pi, _prop)
            ? context.Create<string>().Substring(0, _length)
            : (object) new NoSpecimen(request);
    }

    private bool AreEquivalent(PropertyInfo a, PropertyInfo b)
    {
        return a.DeclaringType == b.DeclaringType
               && a.Name == b.Name;
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

fixture.Customizations.Add(
    new StringPropertyTruncateSpecimenBuilder<Person>(p => p.Initials, 5));
Run Code Online (Sandbox Code Playgroud)

  • 更新:现在不推荐使用fixture.CreateAnonymous方法,应该使用Create <T> (3认同)

Nik*_*nis 12

如果最大长度是约束并且您拥有该类型的源代码,则可以使用StringLengthAttribute类指定允许的最大字符长度.

从版本2.6.0开始,AutoFixture支持DataAnnotations,它将自动生成指定最大长度的字符串.

举个例子,

public class StringLengthValidatedType
{
    public const int MaximumLength = 3;

    [StringLength(MaximumLength)]
    public string Property { get; set; }
}

[Fact]
public void CreateAnonymousWithStringLengthValidatedTypeReturnsCorrectResult()
{
    // Fixture setup
    var fixture = new Fixture();
    // Exercise system
    var result = fixture.CreateAnonymous<StringLengthValidatedType>();
    // Verify outcome
    Assert.True(result.Property.Length <= StringLengthValidatedType.MaximumLength);
    // Teardown
}
Run Code Online (Sandbox Code Playgroud)

使用Build时(以自定义单个对象的创建算法),上述测试也将通过:

var result = fixture.Build<StringLengthValidatedType>().CreateAnonymous();
Run Code Online (Sandbox Code Playgroud)

  • 但它们是"测试属性"吗?在我的情况下,我将描述持久性后端允许属性的最大长度. (5认同)
  • 我不想用测试属性污染我的DTO,对我来说没有意义. (2认同)

小智 7

这是我的解决方案。当字符串包含什么并不重要时,我使用这种方法:

public static string GetStringOfLength(this IFixture fixture, int length)
    {
        return string.Join("", fixture.CreateMany<char>(length));
    }
Run Code Online (Sandbox Code Playgroud)

它很短而且对我有用。