自动固定可以从提供的数据集生成随机字符串/文本吗?

本文关键字:随机 字符串 文本 数据集 | 更新日期: 2023-09-27 18:33:40

可以使用

AutoFixture为字符串属性生成随机数据..但它来自固定数据源?

例如:我有 30 个街道名称硬编码到内存集合中(数组/列表/任何(。然后,对于我的Address实例,StreetName属性不仅仅是一个随机字符串值(这是 AutoFixture 的默认结果(,而是该硬编码集合中的街道名称之一。

我的第一个想法是使用AutoFixture可能能够创建的随机数......这个数字在数组长度/大小内......所以实际上我正在随机化一个数组插槽。然后,使用此随机数获取集合/数组槽的值(也称为街道名称((即给定索引器,获取该索引位置的值(。

这是应该做的吗?

自动固定可以从提供的数据集生成随机字符串/文本吗?

与有关AutoFixture的许多其他事情一样,如果可以使用更明确的域建模,事情就会变得容易得多。与其将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)));

但是,在这一点上,这意味着当您使用 AutoFixture 创建StreetName值时,它会从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());

现在,它将使用从预定义列表中选择的StreetName值创建Address值。

如果无法更改域模型,仍可以添加类似 StreetName 的类。只需将其添加到测试代码库而不是生产代码库即可。