【问题标题】:How can i generate missing values using table.CreateSet<>(myClass)() in SpecFlow?如何在 SpecFlow 中使用 table.CreateSet<>(myClass)() 生成缺失值?
【发布时间】:2016-10-06 22:56:35
【问题描述】:

我第一次使用 SpecFlow 为我的项目编写测试,但遇到了一个小问题。

我有下一堂课:

public class FancyName
{
    [DataMember]
    public Guid Guid { get; set; }

    [DataMember]
    public string Name { get; set; }

    [DataMember]
    public List <Country> Countries { get; set; }
}

我想在我的测试中使用 SpecFlow 助手生成这个类。

这里是场景部分:

[...]
When i add some names
    | Name | Countries |
    | UK   | 1         |
    | US   | 2         |
[...]

我尝试在这样的步骤定义中解析它:

[When(@"I add some names")]
public void AddNames(Table table)
{
    var names = table.CreateSet<FancyName>();
    [...]
}

我遇到了两个问题:

  1. 我没有通过 Guid,因为我想像 Guid.NewGuid() 这样生成它,所以创建的对象包含 null
  2. 我通过国家作为排序,但我需要创建List&lt;Country&gt;()。

我曾经尝试遍历 Table 并手动创建 FancyName 对象,但据我所知,这不是 SpecFlow 方式。我试图查看文档,但没有找到合适的解决方案。

可能有人知道解决这个问题的真正好方法吗? 提前致谢。

【问题讨论】:

  • Country 类型的外观如何?最后你的类型的实例应该是什么样子?

标签: c# bdd specflow


【解决方案1】:

这里是 SpecFlow.Assist 的主要作者...

我同意上述@sam-holder 的观点,即Table.CreateSet&lt;T&gt; 并不神奇,在这种情况下,转换可能是一个更简单的解决方案。但实际上,Assist 确实具有执行所要求的操作所需的功能。 :)

我想解释一下。我看到两个问题表现为:

1) 如何在每条记录上设置Guid?

2) 如何将字符串值转换为数组?

对于 (1),答案很简单。您可以将一个函数传递给 CreateSet,该函数告诉库如何实例化您要创建的对象。由于您只想设置 Guid,您可以这样做:

    table.CreateSet<FancyName>(() => new FancyName { Guid = Guid.NewGuid()});
    // or simpler, table.CreateSet(() => new FancyName { Guid = Guid.NewGuid()});

对于 (2),您需要进行更多编程。您希望 Assist 知道如何将字符串转换为列表。为此,您必须创建一个“值检索器”并将其注册到 Assist。我可以使用以下代码做到这一点:

public class CountryRetriever : IValueRetriever
{
    public bool CanRetrieve(KeyValuePair<string, string> keyValuePair, Type targetType, Type propertyType)
    {
        return propertyType.FullName.StartsWith("System.Collections.Generic.List`1[[SpecFlowExample.Country");
    }

    public object Retrieve(KeyValuePair<string, string> keyValuePair, Type targetType, Type propertyType)
    {
        return keyValuePair.Value.Split(',')
            .Select(x => new Country {Name = x})
            .ToList();
    }
}

[Binding]
public class Steps
{
    [BeforeTestRun]
    public static void Setup()
    {
        TechTalk.SpecFlow.Assist.Service.Instance.RegisterValueRetriever(new CountryRetriever());
    }

    [When(@"i add some names")]
    public void WhenIAddSomeNames(Table table)
    {
        var things = table.CreateSet<FancyName>(() => new FancyName { Guid = Guid.NewGuid()});

    }
}

像这样直接指向一个列表,尤其是使用字符串,这很不雅,但这段代码确实有效。

当遇到List&lt;Country&gt; 类型时,国家/地区检索器将宣布它可以处理字符串的转换。然后它拆分字符串并创建一个国家列表。

SpecFlow 使用大多数基本 .Net 类型的值检索器开始每个测试运行,但不是您的 List&lt;Country&gt;。为了适应您的类型,您需要在每次测试运行之前注册新的值检索器。

【讨论】:

    【解决方案2】:

    Table.CreateSet&lt;&gt; 无法施展魔法。它不知道它应该为您的对象创建一个新的 Guid,或者它应该创建一个包含 2 个国家的列表。我认为你必须自己创建这个对象。

    解决此问题的最佳方法是使用[StepArgumentTransformation]

    类似这样的:

    [StepArgumentTransformation]
    public List<FancyName> TransformToFancyName(Table table)
    {
        //create the list from the table contents
    }
    
    [When(@"I add some names")]
    public void AddNames(List<FancyName> names)
    {
        .. use your FancyNames here
    }
    

    specflow 将调用您的 StepArgumentTransformation 以获取任何具有 List&lt;FancyName&gt; 参数作为最后一个参数和功能中对应表的步骤

    【讨论】:

      【解决方案3】:

      您可以考虑Nested Tables? 之类的东西,但根据这篇文章,这是个坏主意。它建议引入额外的步骤来填充复杂的对象。

      【讨论】:

        猜你喜欢
        • 2021-11-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-11-04
        相关资源
        最近更新 更多