1

这个问题更多的是关于 SpecFlow 中共享值的一般讨论。请根据您在 SpecFlow 中的经验提供任何建设性的反馈。

我对这项技术比较陌生,在寻找在步骤定义文件之间共享值的解决方案时,我发现ScenarioContext.Current.GetScenarioContext.Current.Set. 这些非常方便,但正如我所见,存在一些问题。

  1. 这种方法涉及很多类型。
  2. 值类型是通过字符串索引插入和检索的,因此需要使用字符串常量或枚举来确保步骤定义之间的一致性。
  3. 假设您尝试检索的值已被插入可能不安全。

我提出了一个抽象概念,我认为它使其中一些更容易接受,我想知道人们对此有何看法。

问题:我的值是否已设置?

我对此的解决方案是将 包装ScenarioContext.Current在一个单例访问器类中。这个类的行为类似于ScenarioContext.Current,除了它AssertInconclusiveException在找不到值时抛出一个。

    private static ScenarioContextAccessor instance;

    public static ScenarioContextAccessor Instance
    {
        get
        {
            if (instance == null)
            {
                instance = new ScenarioContextAccessor();
            }

            return instance;
        }
    }

    private ScenarioContextAccessor() { }

    public T Retrieve<T>(string index)
    {
        try
        {
            T val = (T)ScenarioContext.Current[index];
            if (val == null)
            {
                throw new Exception();
            }

            return val;
        }
        catch
        {
            throw new AssertInconclusiveException(index + " of type " + typeof(T).Name + " was not found in the current scenario context. Did you execute your steps out of order?");
        }
    }

    public T Retrieve<T>()
    {
        try
        {
            T val = ScenarioContext.Current.Get<T>();
            if (val == null)
            {
                throw new Exception();
            }

            return val;
        }
        catch
        {
            throw new AssertInconclusiveException("No object of type " + typeof(T).Name+ " could be found in the current scenario context. Did you execute your steps out of order?");
        }
    }

    public void Set(string index, object value)
    {
        ScenarioContext.Current[index.ToLower(CultureInfo.InvariantCulture)] = value;
    }

    public void Set<T>(T value)
    {
        ScenarioContext.Current.Set<T>(value);
    }
}

问题:这需要输入太多!

我对此的解决方案是有任何步骤定义,需要这些值将它们定义为由ScenarioContextAccessor. 任何访问值类型的属性都使用字符串常量作为索引。

    private string FolderName
    {
        get
        {
            return ScenarioContextAccessor.Instance.Retrieve<string>(FolderingScenarioContextKey.FolderName);
        }
        set
        {
            ScenarioContextAccessor.Instance.Set(FolderingScenarioContextKey.FolderName, value);
        }
    }

    private UserDocumentMetadata Metadata
    {
        get
        {
            return ScenarioContextAccessor.Instance.Retrieve<UserDocumentMetadata>();
        }
        set
        {
            ScenarioContextAccessor.Instance.Set<UserDocumentMetadata>(value);
        }
    }

所以现在我可以像访问简单属性一样轻松访问我的共享值。

请提供您可能有的任何建设性反馈。谢谢!

4

2 回答 2

0

我认为您正在寻找的功能是上下文注入(依赖注入): https ://github.com/techtalk/SpecFlow/wiki/Context-Injection

这使您可以在步骤定义之间共享类。

于 2013-06-07T09:00:18.770 回答
0

至于第1部分,我不同意。如果没有正确设置数据,我发现让测试彻底失败更有用。

至于第 2 部分,我已经使用了这样的模式,特别是用于获取被测试对象的实例,因为它可以节省大量输入和潜在错误。

如果您只需要一个类的虚拟实例,另一种有用的模式(取决于具体情况)而不是您的第 1 部分解决方案是惰性初始化方法:

public static Mock<T> GetOrMockAndStore<T>() where T : class
    {
        Mock<T> output;
        if (ScenarioContext.Current.TryGetValue(out output))
        {
            return output;
        }
        else
        {
            output = new Mock<T>();
            ScenarioContext.Current.Set(output);
        }
        return card;
    }

我正在使用 Moq - 非常有用的框架。

于 2012-01-26T21:07:00.577 回答