C# 自动夹具-配置夹具以限制字符串生成长度
当对某些类型使用AutoFixture的生成方法时,如何限制生成的字符串长度以填充该对象的字符串属性/字段?如果最大长度是一个约束,并且您拥有该类型的源代码,则可以使用该类指定允许的最大字符长度 从版本2.6.0开始,AutoFixture支持DataAnnotations,它将自动生成指定最大长度的字符串 例如,C# 自动夹具-配置夹具以限制字符串生成长度,c#,autofixture,C#,Autofixture,当对某些类型使用AutoFixture的生成方法时,如何限制生成的字符串长度以填充该对象的字符串属性/字段?如果最大长度是一个约束,并且您拥有该类型的源代码,则可以使用该类指定允许的最大字符长度 从版本2.6.0开始,AutoFixture支持DataAnnotations,它将自动生成指定最大长度的字符串 例如, public class StringLengthValidatedType { public const int MaximumLength = 3; [Stri
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
}
公共类StringLengthValidatedType
{
公共常数int最大长度=3;
[StringLength(最大长度)]
公共字符串属性{get;set;}
}
[事实]
public void CreateAnonymousWithStringLengthValidatedTypeReturnsCorrectResult()创建匿名
{
//夹具设置
var fixture=新fixture();
//运动系统
var result=fixture.CreateAnonymous();
//核实结果
Assert.True(result.Property.Length使用Build
方法本身,没有那么多选项,但您可以这样做:
var constrainedText =
fixture.Create<string>().Substring(0, 10);
var mc = fixture
.Build<MyClass>()
.With(x => x.SomeText, constrainedText)
.Create();
var input = _fixture.Build<HasAccountNumber>()
.With(x => x.AccountNumber,
new SpecimenContext(new RandomStringOfLengthGenerator())
.Resolve(new RandomStringOfLengthRequest(50)))
.Create();
上述自定义将所有生成的字符串截断为10个字符。但是,由于默认的属性分配算法将属性名称前置到字符串中,因此最终结果将是mc。SomeText
将具有类似“SomeText3c12f144-5”的值,因此大多数情况下这可能不是您想要的
另一种选择是使用[StringLength]
属性,正如Nikos指出的:
public class MyClass
{
[StringLength(10)]
public string SomeText { get; set; }
}
这意味着您可以只创建一个实例,而无需明确说明有关属性长度的任何内容:
var mc = fixture.Create<MyClass>();
用法:
fixture.Customizations.Add(new SomeTextBuilder());
var mc = fixture.Create<MyClass>();
fixture.Customizations.Add(
new StringPropertyTruncateSpecimenBuilder<Person>(p => p.Initials, 5));
用法:
fixture.Customizations.Add(new SomeTextBuilder());
var mc = fixture.Create<MyClass>();
fixture.Customizations.Add(
new StringPropertyTruncateSpecimenBuilder<Person>(p => p.Initials, 5));
fixture.Customizations.Add(
新StringPropertyRuncateSpecimenBuilder(p=>p.首字母缩写,5));
这是一个示例生成器,它可以生成任意长度的随机字符串,甚至比默认情况下的Guid+PropertyName字符串还要长。此外,您还可以选择要使用的字符子集,甚至传入自己的随机字符串(这样,如果需要,您可以控制种子)
公共类RandomStringOfLengthRequest
{
public RandomStringOfLengthRequest(int-length):此(长度,“ABCDEFGHIJKLMNOPQRSTUVWXYZABCDFGHIJKLMNOPQRSTUVWXYZ01234567890!,.-”)
{
}
public RandomStringOfLengthRequest(int-length,string charactersToUse):此(长度,charactersToUse,new Random())
{
}
public RandomStringOfLengthRequest(整数长度、字符串字符使用、随机)
{
长度=长度;
随机=随机;
CharactersToUse=CharactersToUse;
}
公共整数长度{get;私有集;}
公共随机{get;私有集;}
公共字符串字符使用{get;private set;}
公共字符串GetRandomChar()
{
返回CharactersToUse[Random.Next(CharactersToUse.Length)].ToString();
}
}
长度生成器的公共类:ISpecimenBuilder
{
公共对象创建(对象请求,ISPecementContext上下文)
{
if(请求==null)
返回新的NoSpecimen();
var stringOfLengthRequest=请求为RandomStringOfLengthRequest;
if(stringOfLengthRequest==null)
返回新的NoSpecimen();
var sb=新的StringBuilder();
for(var i=0;i
然后可以使用它填充对象的属性,如下所示:
var constrainedText =
fixture.Create<string>().Substring(0, 10);
var mc = fixture
.Build<MyClass>()
.With(x => x.SomeText, constrainedText)
.Create();
var input = _fixture.Build<HasAccountNumber>()
.With(x => x.AccountNumber,
new SpecimenContext(new RandomStringOfLengthGenerator())
.Resolve(new RandomStringOfLengthRequest(50)))
.Create();
var输入=_fixture.Build()
.使用(x=>x.AccountNumber,
新的SpecimenContext(新的RandomStringOfLengthGenerator())
.Resolve(新的随机长度请求(50)))
.Create();
我在项目中添加了一个自定义字符串生成器。它附加了一个4位数字,而不是guid
public class StringBuilder : ISpecimenBuilder
{
private readonly Random rnd = new Random();
public object Create(object request, ISpecimenContext context)
{
var type = request as Type;
if (type == null || type != typeof(string))
{
return new NoSpecimen();
}
return rnd.Next(0,10000).ToString();
}
}
其他一些解决方案相当不错,但是如果您在基于数据模型的测试夹具中生成对象,您将遇到其他问题。首先,对于代码优先的数据模型,StringLength属性不是一个很好的选项,因为它添加了看起来重复的注释。不清楚为什么您需要StringLength和StringLengthh和MaxLength。手动保持它们同步是相当多余的
我倾向于定制夹具的工作方式
1) 您可以自定义类的fixture,并指定在创建该属性时,根据需要截断字符串。因此,要将MyClass中需要struncation的字段截断为10个字符,请使用以下命令:
fixture.Customize<MyClass>(c => c
.With(x => x.FieldThatNeedsTruncation, Fixture.Create<string>().Substring(0,10));
注意:此解决方案并不真正使用AutoFixture,但有时使用软件包比自己编程更困难。
为什么要使用自动对焦?当使用自动对焦更难、更难看时,我首选的用法是:
var fixture = new Fixture();
fixture.Create<string>(length: 9);
var fixture=newfixture();
夹具。创建(长度:9);
所以我创建了一个扩展方法:
public static class FixtureExtensions
{
public static T Create<T>(this IFixture fixture, int length) where T : IConvertible, IComparable, IEquatable<T>
{
if (typeof(T) == typeof(string))
{
// there are some length flaws here, but you get the point.
var value = fixture.Create<string>();
if (value.Length < length)
throw new ArgumentOutOfRangeException(nameof(length));
var truncatedValue = value.Substring(0, length);
return (T)Convert.ChangeType(truncatedValue, typeof(T));
}
// implement other types here
throw new NotSupportedException("Only supported for strings (for now)");
}
}
公共静态类FixtureExtensions
{
公共静态T创建(此IFixture fixture,int-length),其中T:IConvertible、IComparable、IEquatable
{
if(typeof(T)=typeof(string))
{
//这里有一些长度缺陷,但你明白了。
var值=fixture.Create();
if(value.Length
这是我的解决方案和注意事项
首先,很明显AutoFixture中存在一些紧密耦合。创建以了解如何构建和自定义样本。对于字符串,这很烦人,因为我们知道默认值是Guid。使用
/// <summary>
/// Examine the attributes of the current property for the existence of the MaxLengthAttribute.
/// If set, use the value of the attribute to truncate the string to not exceed that length.
/// </summary>
public class MaxLengthAttributeRelay : ISpecimenBuilder
{
/// <summary>
/// Creates a new specimen based on a specified maximum length of characters that are allowed.
/// </summary>
/// <param name="request">The request that describes what to create.</param>
/// <param name="context">A container that can be used to create other specimens.</param>
/// <returns>
/// A specimen created from a <see cref="MaxLengthAttribute"/> encapsulating the operand
/// type and the maximum of the requested number, if possible; otherwise,
/// a <see cref="NoSpecimen"/> instance.
/// Source: https://github.com/AutoFixture/AutoFixture/blob/ab829640ed8e02776e4f4730d0e72ab3cc382339/Src/AutoFixture/DataAnnotations/StringLengthAttributeRelay.cs
/// This code is heavily based on the above code from the source library that was originally intended
/// to recognized the StringLengthAttribute and has been modified to examine the MaxLengthAttribute instead.
/// </returns>
public object Create(object request, ISpecimenContext context)
{
if (request == null)
return new NoSpecimen();
if (context == null)
throw new ArgumentNullException(nameof(context));
var customAttributeProvider = request as ICustomAttributeProvider;
if (customAttributeProvider == null)
return new NoSpecimen();
var maxLengthAttribute = customAttributeProvider.GetCustomAttributes(typeof(MaxLengthAttribute), inherit: true).Cast<MaxLengthAttribute>().SingleOrDefault();
if (maxLengthAttribute == null)
return new NoSpecimen();
return context.Resolve(new ConstrainedStringRequest(maxLengthAttribute.Length));
}
}
fixture.Customizations.Add(new MaxLengthAttributeRelay());
var fixture = new Fixture();
fixture.Create<string>(length: 9);
public static class FixtureExtensions
{
public static T Create<T>(this IFixture fixture, int length) where T : IConvertible, IComparable, IEquatable<T>
{
if (typeof(T) == typeof(string))
{
// there are some length flaws here, but you get the point.
var value = fixture.Create<string>();
if (value.Length < length)
throw new ArgumentOutOfRangeException(nameof(length));
var truncatedValue = value.Substring(0, length);
return (T)Convert.ChangeType(truncatedValue, typeof(T));
}
// implement other types here
throw new NotSupportedException("Only supported for strings (for now)");
}
}
public static string GetStringOfLength(this IFixture fixture, int length)
{
return string.Join("", fixture.CreateMany<char>(length));
}