ميزات SpecFlow مفيدة

مرحبا بالجميع!

على Habré هناك مقالات ممتازة عن SpecFlow. أرغب في الخوض في هذا الموضوع والتحدث عن إجراء الاختبارات بشكل متوازٍ ، وتمرير البيانات بين الخطوات ، ومساعدة المساعدين ، والتحويلات ، والسنانير ، واستخدام Json كمصدر للبيانات.

التنفيذ الموازي ونقل البيانات بين الخطوات


في وثائق نقل البيانات بين الخطوات ، نجد المثال التالي:

// Loading values into ScenarioContext ScenarioContext.Current["id"] = "value"; ScenarioContext.Current["another_id"] = new ComplexObject(); // Retrieving values from ScenarioContext var id = ScenarioContext.Current["id"]; var complexObject = ScenarioContext.Current["another_id"] As ComplexObject; 

يستخدم هذا الرمز مفاتيح السلسلة. حفظها وكتابتها شاقة للغاية.

يمكن حل هذه المشكلة عن طريق إنشاء فئة ثابتة مع الخصائص الضرورية:

 public static class ScenarioData { public static ComplexObject Complex { get => ScenarioContext.Current.Get<ComplexObject>(nameof(Complex)); set => ScenarioContext.Current.Set(value, nameof(Complex)); } } 

يبدو نقل البيانات الآن كما يلي:

 // Loading values into ScenarioContext ScenarioData.Complex = new ComplexObject(); // Retrieving values from ScenarioContext var complexObject = ScenarioData.Complex; 

لسوء الحظ ، لن نتمكن من استخدام ScenarioContext.Current عند إجراء الاختبارات بشكل متوازٍ حتى نقوم بإجراء الحقن الضروري

 //      [Binding] public abstract class ScenarioSteps { protected ScenarioSteps(ScenarioContext scenarioContext, FeatureContext featureContext) { FeatureContext = featureContext; ScenarioContext = scenarioContext; ScenarioData = new ScenarioData(scenarioContext); } public FeatureContext FeatureContext { get; } public ScenarioContext ScenarioContext { get; } public ScenarioData ScenarioData { get; } } //  ScenarioData public class ScenarioData { private readonly ScenarioContext _context; public ScenarioData(ScenarioContext context) { _context = context; } public ComplexObject Complex { get => _context.Get<ComplexObject>(nameof(Complex)); set => _context.Set(value, nameof(Complex)); } } //      [Binding] public class ActionSteps : ScenarioSteps { public ActionSteps(ScenarioContext scenarioContext, FeatureContext featureContext) : base(scenarioContext, featureContext) { } [When(@"user uses complex object")] public void WhenUserUsesComplexObject() { ScenarioData.Complex = new ComplexObject(); } } 

وبالتالي ، قمنا بحل مشكلتين: تخلصنا من مفاتيح الأوتار وفرنا القدرة على إجراء الاختبارات بشكل متوازٍ. بالنسبة لأولئك الذين يريدون التجربة ، قمت بإنشاء مشروع صغير.

مساعدة المساعدين والتحولات


النظر في الخطوة التالية

 When user starts rendering | SourceType | PageOrientation | PageMediaSizeName | | set-01-valid | Landscape | A4 | 

في كثير من الأحيان ، يتم طرح البيانات من جدول مثل هذا

 [When(@"user starts Rendering")] public async Task WhenUserStartsRendering(Table table) { var sourceType = table.Rows.First()["SourceType"]; var pageOrientation = table.Rows.First()["PageOrientation"]; var pageMediaSizeName = table.Rows.First()["PageMediaSizeName"]; ... } 

باستخدام Assist Helpers ، تبدو قراءة بيانات الاختبار أكثر أناقة. نحن بحاجة إلى إنشاء نموذج مع الخصائص المناسبة:

 public class StartRenderingRequest { public string SourceType { get; set; } public string PageMediaSizeName { get; set; } public string PageOrientation { get; set; } } 

واستخدامها في CreateInstance

 [When(@"user starts Rendering")] public async Task WhenUserStartsRendering(Table table) { var request = table.CreateInstance<StartRenderingRequest>(); ... } 

مع التحويلات ، يمكن تبسيط وصف خطوة الاختبار إلى أبعد من ذلك.

تحديد التحول:

 [Binding] public class Transforms { [StepArgumentTransformation] public StartRenderingRequest StartRenderingRequestTransform(Table table) { return table.CreateInstance<StartRenderingRequest>(); } } 

الآن يمكننا استخدام النوع المطلوب كمعلمة في الخطوة:

 [When(@"user starts Rendering")] public async Task WhenUserStartsRendering(StartRenderingRequest request) { // we have implemented transformation, so we use StartRenderingRequest as a parameter ... } 

بالنسبة لأولئك الذين يرغبون في تجربة - نفس المشروع.

السنانير واستخدام Json كمصدر لبيانات الاختبار


تعد جداول SpecFlow أكثر من كافية لبيانات الاختبار البسيطة. ومع ذلك ، هناك سيناريوهات اختبار مع عدد كبير من المعلمات و / أو بنية بيانات معقدة. من أجل استخدام بيانات الاختبار هذه ، مع الحفاظ على قابلية قراءة البرنامج النصي ، نحتاج إلى Hooks . سوف نستخدم الخط [BeforeScenario] لقراءة البيانات اللازمة من ملف Json. للقيام بذلك ، حدد العلامات الخاصة على مستوى البرنامج النصي

 @jsonDataSource @jsonDataSourcePath:DataSource\FooResponse.json Scenario: Validate Foo functionality Given user has access to the Application Service When user invokes Foo functionality | FooRequestValue | | input | Then Foo functionality should complete successfully 

وإضافة منطق المعالجة إلى السنانير:

 [BeforeScenario("jsonDataSource")] public void BeforeScenario() { var tags = ScenarioContext.ScenarioInfo.Tags; var jsonDataSourcePathTag = tags.Single(i => i.StartsWith(TagJsonDataSourcePath)); var jsonDataSourceRelativePath = jsonDataSourcePathTag.Split(':')[1]; var jsonDataSourcePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, jsonDataSourceRelativePath); var jsonRaw = File.ReadAllText(jsonDataSourcePath); ScenarioData.JsonDataSource = jsonRaw; } 

هذا الرمز يطرح محتويات ملف json (المسار النسبي للملف) في متغير سلسلة ويحفظه في بيانات البرنامج النصي (ScenarioData.JsonDataSource). وبالتالي ، يمكننا استخدام هذه البيانات عند الاقتضاء

 [Then(@"Foo functionality should complete successfully")] public void ThenFooFunctionalityShouldCompleteSuccessfully() { var actual = ScenarioData.FooResponse; var expected = JsonConvert.DeserializeObject<FooResponse>(ScenarioData.JsonDataSource); actual.FooResponseValue.Should().Be(expected.FooResponseValue); } 

نظرًا لأنه قد يكون هناك الكثير من البيانات في Json ، يمكن أيضًا تحديث بيانات الاختبار من خلال العلامات. يمكن للمهتمين رؤية مثال في نفس المشروع.

مراجع


1. الخيار
2. غيركين
3. SpecFlow الوثائق
4. SpecFlow ويكي
5. مواصفات قابلة للتنفيذ: SpecFlow A إلى Z
6. بيانات يحركها الاختبارات و SpecFlow

Source: https://habr.com/ru/post/ar449574/


All Articles