Arhn - архитектура программирования

Можно ли совместно использовать функции SpecFlow между этапами?

У нас есть код, который мы хотели бы протестировать с трех разных сторон:

  • Внутренний (прямой вызов)
  • веб-сервис
  • веб приложение

Поскольку мы не хотим писать файлы функций три раза, кажется, что мы должны делиться функциями между шагами, но я не могу понять, как это можно сделать, за исключением, возможно, совместного использования файлов между проектами VS, что всегда казалось немного, ну, шелушащийся.

Является ли обмен файлами функций между проектами лучшим способом добиться этого или есть более разумный метод?

21.02.2012

  • Просто идея... Вы можете добавить какой-нибудь класс-оболочку поверх вашего кода MAIN, чтобы определить источник, который его использует. А потом вести статистику. 21.02.2012

Ответы:


1

Я знаю, что это было задано довольно давно, но то, что я думаю, вы хотите, возможно, и, как я понимаю, есть несколько способов добиться этого. По сути, вы действительно хотите определить функцию один раз, но отключить шаги, которые вызываются для этой функции, в зависимости от того, вызываете ли вы внутреннюю службу, общедоступную веб-службу или веб-приложение. Различные подходы к решению этой проблемы обсуждаются в списке рассылки для specflow, но суть его заключается в следующем (пример показывает подход API и пользовательского интерфейса, но то же самое относится к вашему внутреннему, веб-сервису и веб-приложению, я считаю):

Опция 1 #

(спасибо Оливеру Фридриху из списка рассылки)

вам нужно создать одну сборку для каждого из параметров, которые вы хотите изменить для шагов, поэтому одну для внутреннего, одну для веб-службы и одну для веб-приложения. Вы сообщаете specflow обо всех различных сборках в конфигурации, например:

<specFlow>
  <stepAssemblies>
    <stepAssembly assembly="Tests.API" />
    <stepAssembly assembly="Tests.UI" />
  </stepAssemblies>
</specFlow>

то в ваших общих тестовых шагах у вас есть шаг [BeforeTestRun], который выбирает, из какой сборки загружать шаги:

[Binding]
public class TestRunSetup {

    // this method needs to be static
    [BeforeTestRun]
    public static void BeforeTestRun() 
    {
        if (RunApiTests()) // <-- implement this method to choose whether to run the tests via your chosen method 
        {
            Assembly.Load("Tests.API");
        }
        else 
        {
            Assembly.Load("Tests.UI");
        }
    }
}

Вариант 2 ##

(спасибо Gáspár Nagy из списка рассылки)

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

Вариант 3 ##

(спасибо Дэну Морку из списка рассылки)

Если вы используете возможности внедрения зависимостей SpecFlow, вы можете создать интерфейс для выполнения вызовов, которые вы хотите сделать, и использовать его в общем наборе шагов. Тогда у вас может быть 3 реализации этого интерфейса, одна из которых вызывает вашу внутреннюю службу, другая — веб-службу, а третья — управление веб-приложением. После этого остается только внедрить правильную реализацию этого интерфейса в файлы шага спецификации, что можно сделать следующим образом:

// the abstract concept of the system that could be implemented with 
Selenium, HttpClient, etc. 
public interface IDocument 
{ 
    string Title { get;} 
    void Load(); 
} 

// the steps that are executed when the scenarios from your feature 
file are executed 
[Binding] 
public class Steps 
{ 
    private readonly IDocument _document; 

    public Steps(IDocument document) 
    { 
        _document = document; 
    } 

    [Given("something")] 
    public void GivenSomething() 
    { 
        // set up given state 
    } 

    [When("I view the document")] 
    public void WhenIViewTheDocument() 
    { 
        _document.Load(); 
    } 

    [Then(@"the title should be ""(.*)""")] 
    public void Then(string title) 
    { 
        Assert.ArEqual(_document.Title, title); 
    } 
} 

// this is where the magic happens - get the dependency injection 
// container and register an IDocument implementation
[Binding] 
public class Dependencies 
{ 
    private readonly IObjectContainer _objectContainer; 

    public Dependencies(IObjectContainer objectContainer) 
    { 
        _objectContainer = objectContainer; 
    } 

    [BeforeScenario] 
    public void RegisterDocumentInterfaces() 
    { 
        // register the correct IDocument implementation - UI or API 
    } 
} 

Это по-прежнему оставляет вас с проблемой, как узнать, какую реализацию зарегистрировать. Это будет зависеть от специфики вашего решения, среды сборки, среды выполнения тестов и т. д. Некоторые варианты для этого...

  • параметр тега для BeforeScenarioAttribute
  • создайте две разные конфигурации сборки для вашего проекта, каждая из которых определяет разные константы и использует директивы прекомпилятора для встраивания правильных регистраций в код
  • добавить условную логику для проверки переменной среды
  • добавить условную логику для проверки параметра конфигурации
  • Я не проверял, но, возможно, ScopeAttribute можно расширить, чтобы вы могли создать подкласс и предоставить собственную логику того, выполняется ли метод BeforeScenarioAttribute.

Надеюсь, это даст вам несколько вариантов сделать то, что вы хотите.

ИМХО, первый вариант, вероятно, лучший, хотя вариант 3 тоже довольно приятный.

01.07.2014
  • @Oliver, спасибо за редактирование :) и за оригинальный контент 01.07.2014

  • 2

    У нас был похожий случай, когда одни и те же функции должны были работать для разных методов ввода. Наше решение было похоже на вариант № 3 из ответа Сэма Холдера, но мы хотели использовать файлы конфигурации вместо кода, чтобы решить, какую реализацию использовать в каждом случае.

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

    Теперь для каждого проекта, который имеет общие функции с другим, у нас есть файл .config, в котором мы указываем правильную реализацию для каждого общего интерфейса (в основном входных данных).

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

    Если вам интересно решение, я загружу его на github.

    02.07.2014

    3

    Что сработало для нас, было что-то похожее на 1-й вариант Сэма Холдера.

    1. Ссылайтесь на обе зависимости в своем проекте VS, чтобы обе dll были скопированы в выходной (компиляционный) каталог.
    2. используйте разные файлы app.config для разных сред.
    3. в каждом app.config настроить правильный stepassemblies (закомментировать/удалить ненужное)

      <specFlow>
        <stepAssemblies>
          <stepAssembly assembly="Tests.API" />
          <!--stepAssembly assembly="Tests.UI" /-->
        </stepAssemblies>
      </specFlow>
      

    Мне не нужен ручной вызов Assembly.Load, потому что объявление stepAssembly передаст ссылку на NUnit как на сборку привязки (см. .cs" rel="nofollow">здесь).

    Если у меня обе сборки объявлены как stepAssemblies, я сталкиваюсь с исключением «неоднозначная ссылка».

    Надеюсь, это поможет, потому что совместное использование функций очень полезно, если не необходимо (например, когда вы обновляете свое программное обеспечение и сохраняете тесты функциональных сценариев).

    01.04.2015

    4

    Я думаю, что это действительно 3 разных теста, поэтому я должен иметь в виду 3 набора файлов функций. Однако, если вы действительно не хотите идти по этому пути, вы можете сделать что-то вроде указания таблицы примеров, например.

    Scenario Outline: Testing app
    
    Given I have performed a call to my service using <application>
    When I do something
    Then this happens
    
    Examples:
    |Application|
    | web service |
    | web application |
    | direct call |
    

    Приведенный выше сценарий будет выполняться 3 раза, передавая 3 значения. Значение будет передано в данный метод (в данном случае), чтобы вы могли использовать его для настройки некоторого контекста, а затем повторно использовать те же файлы определения шага. Тогда каждый шаг будет знать, предназначен ли он для использования веб-службы, веб-приложения или прямого вызова.

    У меня все еще есть оговорки по поводу этого, поскольку это отдельные тесты, но это способ сделать то, чего вы хотите достичь.

    21.02.2012

    5

    Я не уверен, правильно ли я думаю о том, что вы хотите, но это может быть то, что вы ищете:

    Scenario Outline: Multiple approaches to test same code
        Given I am using <ApproachToCallCode>
        When I do something
        Then I expect this result
    
    Scenarios: Approaches
        |ApproachToCallCode|
        |Internal          |
        |WebService        |
        |WebApp            |
    

    Тогда вы могли бы использовать условное выражение в данном методе? Я не уверен, что это лучший подход, но он должен работать.

    21.02.2012

    6

    Шаги не привязаны к функции, если шаги одинаковы или хотя бы некоторые из них, если формулировка одинакова, или вы добавляете дополнительный атрибут к методу определения шага, то их можно повторно использовать в других сценариях или функциях.

    В зависимости от случая я бы написал их как дополнительные сценарии или функции.

    21.02.2012
  • Вопрос касался совместного использования функций между шагами, а не шагов между функциями. 23.02.2012
  • На самом деле это не функции, которые выполняются, а их шаги. Функция — это просто логический контейнер 24.02.2012
  • Новые материалы

    Коллекции публикаций по глубокому обучению
    Последние пару месяцев я создавал коллекции последних академических публикаций по различным подполям глубокого обучения в моем блоге https://amundtveit.com - эта публикация дает обзор 25..

    Представляем: Pepita
    Фреймворк JavaScript с открытым исходным кодом Я знаю, что недостатка в фреймворках JavaScript нет. Но я просто не мог остановиться. Я хотел написать что-то сам, со своими собственными..

    Советы по коду Laravel #2
    1-) Найти // You can specify the columns you need // in when you use the find method on a model User::find(‘id’, [‘email’,’name’]); // You can increment or decrement // a field in..

    Работа с временными рядами спутниковых изображений, часть 3 (аналитика данных)
    Анализ временных рядов спутниковых изображений для данных наблюдений за большой Землей (arXiv) Автор: Рольф Симоэс , Жильберто Камара , Жильберто Кейрос , Фелипе Соуза , Педро Р. Андраде ,..

    3 способа решить квадратное уравнение (3-й мой любимый) -
    1. Методом факторизации — 2. Используя квадратичную формулу — 3. Заполнив квадрат — Давайте поймем это, решив это простое уравнение: Мы пытаемся сделать LHS,..

    Создание VR-миров с A-Frame
    Виртуальная реальность (и дополненная реальность) стали главными модными терминами в образовательных технологиях. С недорогими VR-гарнитурами, такими как Google Cardboard , и использованием..

    Демистификация рекурсии
    КОДЕКС Демистификация рекурсии Упрощенная концепция ошеломляющей О чем весь этот шум? Рекурсия, кажется, единственная тема, от которой у каждого начинающего студента-информатика..