SpecFlow позволяет использовать встроенные таблицы для Data Driven сценариев. В своей практике я столкнулся с двумя проблемами при таком подходе:
- Иногда хочется, наоборот, получить авто-документацию из теста (например, тестирование API)
- Когда количество данных велико, лучше хранить их где-то отдельно (часто для 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 mistakesn" +
"As a math idiotn" +
"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 и сценариев с большим количеством данных.
Автор: marshinov