В Specflow я могу запустить один тест как шаг другого?

TL; DR; Как создать пробный тест, который вызывает другой тест в качестве первого шага?

Given I already have one specflow test
And I want to run another test that goes deeper than the first test  
Then I create a second test that runs the first test as its first step
And I add additional steps to test the deeper functionality

Извините, немного юмора там.

например, у меня есть тест, который уже создает продажу:

Given I want to create a sales order
And I open the sales order page
And I click the add new order button
Then a new sales order is created

И я хочу получить еще один тест, в котором тесты добавляются в строку продаж

И еще один тест, который проверяет завершение продажи

И еще один тест, который отменяет продажу

И.. так далее

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

Given I have run the create sales order test  // right here it just runs the first test
And I add a sales order line
Then the order total is updated

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

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

Given I want to credit a sale
And I run the create sales order test
And I complete the the sale
And I click the credit button
Then the sale is credited

Ответ 1

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

[Binding]
public class MySteps: Steps //Inheriting this base class is vital or the methods used below won't be available
{
    [Given("I have created an order")]
    public void CreateOrder()
    {
         Given("I want to create a sales order");
         Given("I open the sales order page");
         Given("I click the add new order button");
         Then("a new sales order is created");
    }
}

который вы затем можете использовать в своем сценарии:

Scenario: I add another sale
    Given I have created an order
    When I add a sales order line
    Then the order total is updated

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

Ответ 2

Используйте фон:

Background:
    Given I want to create a sales order
    And I open the sales order page
    And I click the add new order button
    Then a new sales order is created

Scenario: I add another sale
    When I add a sales order line
    Then the order total is updated

Scenario: I add cancel a sale
    When I cancel a sale
    Then the order total is updated to 0

etc.

Ответ 3

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

Во-первых, вымышленный SalesOrder класс:

public class SalesOrder
{
    public double Amount { get; set; }
    public string Description { get; set; }
}

Тогда определения шага

using TechTalk.SpecFlow;
using TechTalk.SpecFlow.Assist;

[Binding]
public class SalesOrderSteps
{
    [Given("I have already created a Sales Order")]
    public void GivenIHaveAlreadyCreatedASalesOrder()
    {
        var order = new SalesOrder()
        {
            // .. set default properties
        };

        // Save to scenario context so subsequent steps can access it
        ScenarioContext.Current.Set<SalesOrder>(order);

        using (var db = new DatabaseContext())
        {
            db.SalesOrders.Add(order);
            db.SaveChanges();
        }
    }

    [Given("I have already created a Sales Order with the following attributes:")]
    public void GivenIHaveAlreadyCreatedASalesOrderWithTheFollowingAttributes(Table table)
    {
        var order = table.CreateInstance<SalesOrder>();

        // Save to scenario context so subsequent steps can access it
        ScenarioContext.Current.Set<SalesOrder>(order);

        using (var db = new DatabaseContext())
        {
            db.SalesOrders.Add(order);
            db.SaveChanges();
        }
    }
}

Теперь вы можете создавать заказы на продажу как однострочные и необязательно включать в себя некоторые пользовательские атрибуты:

Scenario: Something
    Given I have already created a Sales Order

Scenario: Something else
    Given I have already created a Sales Order with the following attributes:
        | Field       | Value             |
        | Amount      | 25.99             |
        | Description | Just a test order |

Если вам нужно получить доступ к этому объекту SalesOrder в других определениях шага без запроса его в базе данных, используйте ScenarioContext.Current.Get<SalesOrder>() для извлечения этого объекта из контекста сценария.

Ответ 4

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

  • Вы можете справиться с этим, создав шаг, который будет вызывать шаги в сценарий (в основном вложенные шаги, такие как принятый ответ выше).
  • Добавьте созданный шаг к фону

или

  • Создайте функцию, которая будет вызывать шаги в сценарии.
  • Добавьте тег @create_sale_order в сценарии, для которых в качестве предварительного условия нужен заказ на продажу.
  • Внедрите привязку перед сценарием для тега @create_sale_order и вызовите функцию, созданную на шаге 1.