Полезная абстракция на ScenarioContext.Current? - PullRequest
1 голос
/ 02 ноября 2011

Этот вопрос больше для общего обсуждения совместного использования значений в SpecFlow.Пожалуйста, предоставьте любую конструктивную обратную связь, которую вы могли бы получить, основываясь на своем опыте в SpecFlow.

Я относительно новичок в этой технологии, и когда я искал решение для совместного использования значений между файлами определений шагов, я обнаружил ScenarioContext.Current.Get и ScenarioContext.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);
        }
    }

Так что теперь я могу получить доступ к своим общим значениям так же легко, как если бы они были простыми свойствами.

Пожалуйста, предлагайте любые конструктивная обратная связь, которую вы можете получить.Спасибо!

Ответы [ 2 ]

0 голосов
/ 07 июня 2013

Я думаю, что функция, которую вы ищете - это внедрение контекста (внедрение зависимости): https://github.com/techtalk/SpecFlow/wiki/Context-Injection

Это позволяет вам совместно использовать классы между определениями шагов.

0 голосов
/ 27 января 2012

Что касается первой части, я не согласен.Я считаю более полезным, чтобы тест сразу проваливался, если для него не были правильно установлены данные.

Что касается части 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 - очень полезный фреймворк.

...