كتابة اختبارات واجهة المستخدم

مكتمل

في هذا القسم، يمكنك مساعدة أندي وأميتا في كتابة اختبارات Selenium التي تتحقق من سلوكيات واجهة المستخدم التي وصفتها أميتا.

تقوم أميتا عادة بإجراء الاختبارات على Chrome وFirefox وMicrosoft Edge. هنا، أنت تفعل الشيء نفسه. تم تكوين العامل المستضاف من Microsoft الذي ستستخدمه مسبقا للعمل مع كل من هذه المستعرضات.

إحضار الفرع من GitHub

في هذا القسم، يمكنك إحضار selenium الفرع من GitHub. ثم يمكنك سحب هذا الفرع أو التبديل إليه. ستساعدك محتويات الفرع على المتابعة مع الاختبارات التي يكتبها أندي وأميتا.

يحتوي هذا الفرع على مشروع Space Game الذي عملت معه في الوحدات النمطية السابقة. كما يحتوي على تكوين Azure Pipelines للبدء به.

  1. في Visual Studio Code، افتح المحطة الطرفية المتكاملة.

  2. لتنزيل فرع يسمى selenium من مستودع Microsoft، قم بالتبديل إلى هذا الفرع، ثم قم بتشغيل git fetch الأوامر التالية:git checkout

    git fetch upstream selenium
    git checkout -B selenium upstream/selenium
    

    تلميح

    إذا تابعت مع اختبار أميتا اليدوي في الوحدة السابقة، فربما قمت بتشغيل هذه الأوامر بالفعل. إذا قمت بتشغيلها بالفعل في الوحدة السابقة، فلا يزال بإمكانك تشغيلها مرة أخرى الآن.

    تذكر أن المصدر يشير إلى مستودع Microsoft GitHub. يفهم تكوين Git الخاص بمشروعك جهاز التحكم عن بعد المصدر لأنك قمت بإعداد هذه العلاقة. يمكنك إعداده عند نسخ المشروع من مستودع Microsoft واستنساخه محليا.

    قريبا، ستدفع هذا الفرع إلى مستودع GitHub الخاص بك، والمعروف باسم origin.

  3. اختياريا، في Visual Studio Code، افتح ملف azure-pipelines.yml . تعرف على التكوين الأولي.

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

    إشعار

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

    لمزيد من المعلومات، راجع تنفيذ سير عمل التعليمات البرمجية في البنية الأساسية لبرنامج ربط العمليات التجارية للبناء باستخدام مشغلات Git وGitHub و Build pipeline.

كتابة التعليمات البرمجية لاختبار الوحدة

أميتا متحمسة لتعلم كتابة التعليمات البرمجية التي تتحكم في مستعرض الويب.

هي و(أندي) سيعملان معا لكتابة اختبارات (سيلينيوم). قام أندي بالفعل بإعداد مشروع NUnit فارغ. خلال العملية، يشيرون إلى وثائق Selenium، وبعض البرامج التعليمية عبر الإنترنت، والملاحظات التي أخذوها عندما أجرت أميتا الاختبارات يدويا. في نهاية هذه الوحدة النمطية، ستجد المزيد من الموارد لمساعدتك في إنجاز العملية.

دعونا نراجع العملية التي يستخدمها أندي وأميتا لكتابة اختباراتهما. يمكنك المتابعة عن طريق فتح HomePageTest.cs في دليل Tailspin.SpaceGame.Web.UITests في Visual Studio Code.

تعريف فئة HomePageTest

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

يضيف أندي هذه التعليمة البرمجية إلى HomePageTest.cs:

public class HomePageTest
{
}

Andy: نحتاج إلى تحديد هذه الفئة بحيث تكون public متاحة لإطار عمل NUnit.

إضافة متغير عضو IWebDriver

أندي: بعد ذلك، نحتاج إلى IWebDriver متغير عضو. IWebDriver هي واجهة البرمجة التي تستخدمها لتشغيل مستعرض ويب والتفاعل مع محتوى صفحة الويب.

أميتا: لقد سمعت عن واجهات في البرمجة. هل يمكنك إخباري بالمزيد؟

أندي: فكر في الواجهة كمواصفات أو مخطط لكيفية تصرف المكون. توفر الواجهة أساليب أو سلوكيات هذا المكون. ولكن الواجهة لا توفر أيا من التفاصيل الأساسية. ستقوم أنت أو شخص آخر بإنشاء فئة واحدة أو أكثر من الفئات الملموسة التي تنفذ هذه الواجهة. يوفر السيلينيوم الفئات الملموسة التي نحتاجها.

يوضح هذا الرسم التخطيطي الواجهة IWebDriver وبعض الفئات التي تنفذ هذه الواجهة:

Diagram of the IWebDriver interface, its methods, and concrete classes.

يوضح الرسم التخطيطي ثلاثة من الطرق التي IWebDriver توفر: Navigateو FindElementو Close.

الفئات الثلاث الموضحة هنا، ChromeDriverو FirefoxDriver، و، و EdgeDriver، كل تنفيذ IWebDriver وأساليبه. هناك فئات أخرى، مثل SafariDriver، التي تنفذ IWebDriverأيضا . يمكن لكل فئة برنامج تشغيل التحكم في مستعرض الويب الذي تمثله.

يضيف أندي متغير عضو يسمى driver إلى الفئة، مثل هذه التعليمة البرمجية HomePageTest :

public class HomePageTest
{
    private IWebDriver driver;
}

تحديد تركيبات الاختبار

أندي: نريد إجراء مجموعة كاملة من الاختبارات على Chrome وFirefox وEdge. في NUnit، يمكننا استخدام تركيبات الاختبار لتشغيل مجموعة كاملة من الاختبارات عدة مرات، مرة واحدة لكل متصفح نريد اختباره.

في NUnit، يمكنك استخدام السمة TestFixture لتحديد تركيبات الاختبار الخاصة بك. يضيف أندي هذه التركيبات الاختبارية الثلاثة إلى HomePageTest الفئة:

[TestFixture("Chrome")]
[TestFixture("Firefox")]
[TestFixture("Edge")]
public class HomePageTest
{
    private IWebDriver driver;
}

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

[TestFixture("Chrome")]
[TestFixture("Firefox")]
[TestFixture("Edge")]
public class HomePageTest
{
    private string browser;
    private IWebDriver driver;

    public HomePageTest(string browser)
    {
        this.browser = browser;
    }
}

أندي: أضفنا متغير العضو حتى نتمكن من استخدام اسم المستعرض الحالي في التعليمة البرمجية browser للإعداد. لنكتب التعليمة البرمجية للإعداد بعد ذلك.

تعريف أسلوب الإعداد

أندي: بعد ذلك، نحتاج إلى تعيين متغير العضو الخاص بنا IWebDriver إلى مثيل فئة ينفذ هذه الواجهة للمتصفح الذي نختبر عليه. ChromeDriverFirefoxDriverتنفذ الفئات و و EdgeDriver هذه الواجهة ل Chrome وFirefox وEdge على التوالي.

دعونا ننشئ أسلوبا يسمى Setup الذي يعين driver المتغير. نستخدم السمة OneTimeSetUp لإخبار NUnit بتشغيل هذه الطريقة مرة واحدة لكل مثبت اختبار.

[OneTimeSetUp]
public void Setup()
{
}

في Setup الأسلوب ، يمكننا استخدام عبارة switch لتعيين driver متغير العضو إلى التنفيذ الملموس المناسب بناء على اسم المتصفح. دعونا نضيف هذه التعليمة البرمجية الآن.

// Create the driver for the current browser.
switch(browser)
{
    case "Chrome":
    driver = new ChromeDriver(
        Environment.GetEnvironmentVariable("ChromeWebDriver")
    );
    break;
    case "Firefox":
    driver = new FirefoxDriver(
        Environment.GetEnvironmentVariable("GeckoWebDriver")
    );
    break;
    case "Edge":
    driver = new EdgeDriver(
        Environment.GetEnvironmentVariable("EdgeWebDriver"),
        new EdgeOptions
        {
            UseChromium = true
        }
    );
    break;
    default:
    throw new ArgumentException($"'{browser}': Unknown browser");
}

تأخذ الدالة الإنشائية لكل فئة برنامج تشغيل مسارا اختياريا إلى برنامج التشغيل الذي يحتاجه Selenium للتحكم في مستعرض الويب. لاحقا، سنناقش دور متغيرات البيئة الموضحة هنا.

في هذا المثال، يتطلب المنشئ EdgeDriver أيضا خيارات إضافية لتحديد أننا نريد استخدام إصدار Chromium من Edge.

تعريف أساليب المساعد

أندي: أعلم أننا سنحتاج إلى تكرار إجراءين خلال الاختبارات:

  • البحث عن عناصر على الصفحة، مثل الارتباطات التي نضغط عليها والنوافذ المشروطة التي نتوقع ظهورها
  • النقر فوق عناصر على الصفحة، مثل الارتباطات التي تكشف عن النوافذ المشروطة والزر الذي يغلق كل نموذج

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

كتابة أسلوب مساعد FindElement

عند تحديد موقع عنصر على الصفحة، عادة ما يكون استجابة لحدث آخر، مثل تحميل الصفحة أو إدخال المستخدم للمعلومات. يوفر WebDriverWait Selenium الفئة ، والتي تسمح لك بالانتظار حتى يكون الشرط صحيحا. إذا لم يكن الشرط صحيحا خلال الفترة الزمنية المحددة، WebDriverWait يطرح استثناء أو خطأ. يمكننا استخدام WebDriverWait الفئة لانتظار عرض عنصر معين وأن نكون مستعدين لتلقي إدخال المستخدم.

لتحديد موقع عنصر على الصفحة، استخدم By الفئة . By توفر الفئة أساليب تتيح لك العثور على عنصر باسمه، أو باسم فئة CSS الخاصة به، أو بواسطة علامة HTML الخاصة به، أو في حالتنا، بواسطة سمتهid.

يقوم أندي وأميتا بالتعليمة البرمجية FindElement لأسلوب المساعد. يبدو مثل هذه التعليمة البرمجية:

private IWebElement FindElement(By locator, IWebElement parent = null, int timeoutSeconds = 10)
{
    // WebDriverWait enables us to wait for the specified condition to be true
    // within a given time period.
    return new WebDriverWait(driver, TimeSpan.FromSeconds(timeoutSeconds))
        .Until(c => {
            IWebElement element = null;
            // If a parent was provided, find its child element.
            if (parent != null)
            {
                element = parent.FindElement(locator);
            }
            // Otherwise, locate the element from the root of the DOM.
            else
            {
                element = driver.FindElement(locator);
            }
            // Return true after the element is displayed and is able to receive user input.
            return (element != null && element.Displayed && element.Enabled) ? element : null;
        });
}

كتابة أسلوب مساعد ClickElement

أندي: بعد ذلك، دعنا نكتب أسلوب مساعد ينقر فوق الارتباطات. يوفر Selenium بعض الطرق لكتابة هذا الأسلوب. واحدة منها هي الواجهة IJavaScriptExecutor . باستخدامه، يمكننا النقر فوق الارتباطات برمجيا باستخدام JavaScript. يعمل هذا الأسلوب بشكل جيد لأنه يمكنه النقر فوق الارتباطات دون تمريرها أولا إلى طريقة العرض.

ChromeDriver، FirefoxDriver، وكل EdgeDriver منها ينفذ IJavaScriptExecutor. نحتاج إلى تحويل برنامج التشغيل إلى هذه الواجهة ثم استدعاء ExecuteScript لتشغيل أسلوب JavaScript click() على كائن HTML الأساسي.

يقوم أندي وأميتا بالتعليمة البرمجية ClickElement لأسلوب المساعد. يبدو مثل هذه التعليمة البرمجية:

private void ClickElement(IWebElement element)
{
    // We expect the driver to implement IJavaScriptExecutor.
    // IJavaScriptExecutor enables us to execute JavaScript code during the tests.
    IJavaScriptExecutor js = driver as IJavaScriptExecutor;

    // Through JavaScript, run the click() method on the underlying HTML object.
    js.ExecuteScript("arguments[0].click();", element);
}

أميتا: أحب فكرة إضافة هذه الأساليب المساعدة. تبدو عامة بما يكفي لاستخدامها في أي اختبار تقريبا. يمكننا إضافة المزيد من أساليب المساعد لاحقا كما نحتاج إليها.

تعريف أسلوب الاختبار

أندي: الآن، نحن مستعدون لتحديد أسلوب الاختبار. بناء على الاختبارات اليدوية التي أجريناها سابقا، دعنا نسمي هذا الأسلوب ClickLinkById_ShouldDisplayModalById. من الممارسات الجيدة إعطاء أساليب الاختبار أسماء وصفية تحدد بدقة ما يحققه الاختبار. هنا، نريد النقر فوق ارتباط محدد بواسطة السمة الخاصة به id . ثم نريد التحقق من ظهور النافذة المشروطة المناسبة، أيضا باستخدام سمتها id .

يضيف أندي رمز البداية لأسلوب الاختبار:

public void ClickLinkById_ShouldDisplayModalById(string linkId, string modalId)
{
}

أندي: قبل أن نضيف المزيد من التعليمات البرمجية، دعنا نحدد ما يجب أن يفعله هذا الاختبار.

أميتا: يمكنني التعامل مع هذا الجزء. نريد:

  1. حدد موقع الارتباط حسب السمة الخاصة به id ثم انقر فوق الارتباط.
  2. حدد موقع المشروط الناتج.
  3. أغلق المشروط.
  4. تحقق من عرض المشروط بنجاح.

أندي: عظيم. سنحتاج أيضا إلى التعامل مع بعض الأشياء الأخرى. على سبيل المثال، نحتاج إلى تجاهل الاختبار إذا تعذر تحميل برنامج التشغيل، ونحتاج إلى إغلاق المشروط فقط إذا تم عرض المشروط بنجاح.

بعد إعادة تعبئة أكواب القهوة الخاصة بهم، يضيف أندي وأميتا تعليمة برمجية إلى أسلوب الاختبار الخاص بهم. يستخدمون أساليب المساعد التي كتبوها لتحديد موقع عناصر الصفحة والنقر فوق الارتباطات والأزرار. إليك النتيجة:

public void ClickLinkById_ShouldDisplayModalById(string linkId, string modalId)
{
    // Skip the test if the driver could not be loaded.
    // This happens when the underlying browser is not installed.
    if (driver == null)
    {
        Assert.Ignore();
        return;
    }

    // Locate the link by its ID and then click the link.
    ClickElement(FindElement(By.Id(linkId)));

    // Locate the resulting modal.
    IWebElement modal = FindElement(By.Id(modalId));

    // Record whether the modal was successfully displayed.
    bool modalWasDisplayed = (modal != null && modal.Displayed);

    // Close the modal if it was displayed.
    if (modalWasDisplayed)
    {
        // Click the close button that's part of the modal.
        ClickElement(FindElement(By.ClassName("close"), modal));

        // Wait for the modal to close and for the main page to again be clickable.
        FindElement(By.TagName("body"));
    }

    // Assert that the modal was displayed successfully.
    // If it wasn't, this test will be recorded as failed.
    Assert.That(modalWasDisplayed, Is.True);
}

أميتا: يبدو الترميز رائعا حتى الآن. ولكن كيف يمكننا توصيل هذا الاختبار بالسمات id التي جمعناها سابقا؟

أندي: سؤال رائع. سنتعامل مع ذلك بعد ذلك.

تعريف بيانات حالة الاختبار

أندي: في NUnit، يمكنك توفير البيانات لاختباراتك بعدة طرق. هنا، نستخدم السمة TestCase . تأخذ هذه السمة الوسيطات التي تمررها لاحقا إلى أسلوب الاختبار عند تشغيلها. يمكن أن يكون لدينا سمات متعددة TestCase يختبر كل منها ميزة مختلفة لتطبيقنا. تنتج كل TestCase سمة حالة اختبار مضمنة في التقرير الذي يظهر في نهاية تشغيل البنية الأساسية لبرنامج ربط العمليات التجارية.

يضيف أندي هذه TestCase السمات إلى أسلوب الاختبار. تصف هذه السمات زر تنزيل اللعبة ، وأحد شاشات اللعبة، وأعلى لاعب على لوحة المتصدرين. تحدد كل سمة سمتين id : واحدة للارتباط للنقر والأخرى للنافذة المشروطة المقابلة.

// Download game
[TestCase("download-btn", "pretend-modal")]
// Screen image
[TestCase("screen-01", "screen-modal")]
// // Top player on the leaderboard
[TestCase("profile-1", "profile-modal-1")]
public void ClickLinkById_ShouldDisplayModalById(string linkId, string modalId)
{

...

Andy: لكل سمات TestCase، تكون المعلمة الأولى هي سمة id للارتباط التي يجب النقر فوقها. المعلمة الثانية هي السمة id للنافذة المشروطة التي نتوقع ظهورها. يمكنك أن ترى كيف تتوافق هذه المعلمات مع الوسيطات المكونة من سلسلتين في أسلوب الاختبار الخاص بنا.

أميتا: أرى ذلك. مع بعض التدريب، أعتقد أنه يمكنني إضافة اختباراتي الخاصة. متى يمكننا رؤية هذه الاختبارات قيد التشغيل في البنية الأساسية لبرنامج ربط العمليات التجارية لدينا؟

أندي: قبل أن ندفع التغييرات عبر البنية الأساسية لبرنامج ربط العمليات التجارية، دعنا أولا نتحقق من أن التعليمات البرمجية تقوم بالتحويل البرمجي وتشغيلها محليا. سنلتزم بالتغييرات وندفعها إلى GitHub ونراها تتحرك عبر البنية الأساسية لبرنامج ربط العمليات التجارية فقط بعد التحقق من أن كل شيء يعمل. دعونا نجري الاختبارات محليا الآن.