简体   繁体   English

AutoFixture是否可以从提供的数据集中生成随机字符串/文本?

[英]Can AutoFixture generate random strings/text from a provided data set?

It is possible to use AutoFixture to generate random data for a string property .. but it's from a fixed data source? 可以使用AutoFixture为字符串属性生成随机数据..但是它来自固定数据源?

For example: I have 30 street names hardcoded into a memory collection (array/list/whatever). 例如:我有30个街道名称被硬编码到一个内存集合中(数组/列表/其他)。 Then, for my Address instance, the StreetName property isn't just a random string value (which is the default result from AutoFixture) but one of the street names from that hardcoded collection. 然后,对于我的Address实例, StreetName属性不仅是一个随机字符串值(这是AutoFixture的默认结果),而且是该硬编码集合中的街道名称之一。

My first thought was to use a random number which AutoFixture might be able to create.. and this number is inside the array length/size ... so in effect i'm randomizing an array slot. 我的第一个想法是使用一个可以由AutoFixture创建的随机数..而这个数字在数组的长度/大小之内...因此,实际上我正在将数组插槽随机化。 Then, using this random number, get the value (aka street name) of the collection/array slot (ie. given the indexer, get the value at that index location). 然后,使用该随机数,获取集合/阵列插槽的值(即街道名称)(即,给定索引器,获取该索引位置的值)。

Is this how it should be done? 这是应该怎么做?

As with so many other things regarding AutoFixture, things become much easier if you can use more explicit domain modelling . 与其他有关AutoFixture的事情一样,如果您可以使用更明确的域建模 ,则事情会变得容易得多。 Instead of modelling StreetName as a string , introduce a domain object for it: 不要将StreetName建模为string ,而是StreetName引入域对象:

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);
    }
}

This is one of those modelling steps that are painful in C# and Java, but would be a one-liner in F# or Haskell... 这是在C#和Java中痛苦的建模步骤之一,但在F#或Haskell中却是一线的。

Let's assume, however, that we have a list of predefined street names: 但是,让我们假设我们有一个预定义的街道名称列表:

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... */ };
}

You can now trivially tell AutoFixture to pick only from that list, using ElementsBuilder : 现在,您可以使用ElementsBuilder轻松地告诉AutoFixture仅从该列表中进行选择:

var fixture = new Fixture();
fixture.Customizations.Add(
    new ElementsBuilder<StreetName>(StreetNames.Values.Select(s => (StreetName)s)));

At this point, though, it means that when you create StreetName values with AutoFixture, it'll pick from StreetNames.Values , but it still isn't going to do that when you ask it to create Address values. 不过,这意味着,当您使用StreetName创建StreetName值时,它将从StreetNames.Values ,但是当您要求它创建Address值时,它仍然不会这样做。 You can address (ha ha) that issue with a little ISpecimenBuilder : 您可以使用一个小的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;
    }
}

Now you can configure your Fixture like this: 现在您可以像这样配置您的Fixture

var fixture = new Fixture();
fixture.Customizations.Add(
    new ElementsBuilder<StreetName>(StreetNames.Values.Select(s => (StreetName)s)));
fixture.Customizations.Add(new StreetNameBuilder());

It'll now create Address values with StreetName values picked from the predefined list. 现在, StreetName从预定义列表中选取的StreetName值来创建Address值。

If you can't change your domain model, you can still add a class like StreetName . 如果您无法更改域模型,则仍可以添加StreetName类的类。 Just add it to your test code base instead of the production code base. 只需将其添加到测试代码库而不是生产代码库即可。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM