Pull to refresh

Data Driven Tests & SpecFlow

Reading time3 min
Views12K
SpecFlow позволяет использовать встроенные таблицы для Data Driven сценариев. В своей практике я столкнулся с двумя проблемами при таком подходе:
  1. Иногда хочется, наоборот, получить авто-документацию из теста (например, тестирование API)
  2. Когда количество данных велико, лучше хранить их где-то отдельно (часто для Acceptance Test Case'ов используют Excel)

Покопавшись в Gesigner Generated коде я смог решить обе проблемы.

Напишем базовый класс для тестов, которые не будут использовать DSL


[TestFixture]
public class DslGenerationTestsBase : TestsBase
{
    private static ITestRunner _testRunner;

    [TestFixtureSetUp]
    public virtual void FeatureSetup()
    {
        _testRunner = TestRunnerManager.GetTestRunner();
        var features = GetType().GetCustomAttributes(typeof (FeatureAttribute), false);
        if (!features.Any())
        {
            throw new ConfigurationErrorsException("Feature Attribute is required");
        }

        var feature = (FeatureAttribute)features.Single();
        var featureInfo = new FeatureInfo(
            new System.Globalization.CultureInfo("en-US"),
            feature.Title,
            feature.Story,
            ProgrammingLanguage.CSharp, null);

        _testRunner.OnFeatureStart(featureInfo);
    }

    [TestFixtureTearDown]
    public virtual void FeatureTearDown()
    {
        _testRunner.OnFeatureEnd();
        _testRunner = null;
    }

    [TearDown]
    public virtual void TearDown()
    {
        _testRunner.OnScenarioEnd();
    }

    protected void ScenarioSetup(ScenarioInfo scenarioInfo)
    {
        _testRunner.OnScenarioStart(scenarioInfo);
    }

    protected void ScenarioCleanup()
    {
        _testRunner.CollectScenarioErrors();
    }

    protected void Given(string given, string keyword = "Given ")
    {
        _testRunner.Given(given, null, null, keyword);
    }

    protected void When(string when, string keyword = "When ")
    {
        _testRunner.When(when, null, null, keyword);
    }

    protected void Then(string then, string keyword = "Then ")
    {
        _testRunner.Then(then, null, null, keyword);
    }
}

Для того, чтобы описать Feature в декларативном стиле, напишем собственный атрибут. Мы используем NUnit. Для того, чтобы Feature попала в отчет необходимо использовать DescriptionAttribute. Поэтому, унаследуем наш атрибут от него.

[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class FeatureAttribute : DescriptionAttribute
{
    public string Title { get; private set; }

    public string Story { get; private set; }

    public CultureInfo CultureInfo { get; private set; }

    public FeatureAttribute(string title, string story):base(title)
    {
        Title = title;
        Story = story;
        CultureInfo = new CultureInfo("en-US");
    }

    public FeatureAttribute(string title, string story, CultureInfo cultureInfo)
        : base(title)
    {
        Title = title;
        Story = story;
        CultureInfo = cultureInfo;
    }
}

И напишем класс с тестом


Вместо TestCase можно использовать TestCaseSource и читать данные из любого источника.

[Binding]
[Feature("DslGeneration",
    "In order to avoid silly mistakes\n" +
    "As a math idiot\n" +
    "I want to be told the sum of two numbers")]
[Category("Examples")]
public class DataDrivenDslGenerationExample : DslGenerationTestsBase
{
    private decimal _a;

    private decimal _b;

    [TestCase("1", "2", "3")]
    [TestCase("2", "3", "5")]
    public void CodeSomeTest_DslIsGenerated(string a, string b, string c)
    {
        var scenarioInfo = new ScenarioInfo("DslGeneration Outline", null);
        ScenarioSetup(scenarioInfo);

        Given("Calculator is on");
        When(string.Format("User fill {0} and {1}", a, b));
        Then(string.Format("{0} is returned", c));

        ScenarioCleanup();
    }

    [Given(@"Calculator is on")]
    public void CalculatorIsOn()
    {
        
    }

    [When(@"User fill (.*) and (.*)")]
    public void Fill(int a, int b)
    {
        _a = a;
        _b = b;
    }

    [Then(@"(.*) is returned")]
    public void ResultReturned(int c)
    {
        var actual = _a + _b;
        Assert.AreEqual(c, actual);
    }
}

Если ваш базовый класс находится в другой сборке, то нужно будет добавить ее в конфигурацию:
<specFlow>
  <stepAssemblies>
    <stepAssembly assembly="..." />
  </stepAssemblies>
  <!-- For additional details on SpecFlow configuration options see http://go.specflow.org/doc-config -->
</specFlow>

Вот и все, теперь с помощью SpecFlow.exe можно сгенерировать красивый отчет




Таким образом, можно создать авто-документацию для API и сценариев с большим количеством данных.
Tags:
Hubs:
Total votes 7: ↑6 and ↓1+5
Comments4

Articles