تمرين - إصلاح اختبار فاشل

مكتمل

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

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

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

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

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

تتضمن أحدث ميزات الفريق لوحة المتصدرين. نحتاج إلى الحصول على عدد النقاط من قاعدة البيانات، حتى نتمكن من كتابة اختبار وحدة للتحقق من IDocumentDBRepository<T>.GetItemsAsync الأسلوب.

إليك الشكل الذي يبدو عليه الاختبار. لا تحتاج إلى إضافة أي تعليمات برمجية حتى الآن.

[TestCase(0, ExpectedResult=0)]
[TestCase(1, ExpectedResult=1)]
[TestCase(10, ExpectedResult=10)]
public int ReturnRequestedCount(int count)
{
    const int PAGE = 0; // take the first page of results

    // Fetch the scores.
    Task<IEnumerable<Score>> scoresTask = _scoreRepository.GetItemsAsync(
        score => true, // return all scores
        score => 1, // we don't care about the order
        PAGE,
        count // fetch this number of results
    );
    IEnumerable<Score> scores = scoresTask.Result;

    // Verify that we received the specified number of items.
    return scores.Count();
}

تذكر أنه في اختبار NUnit، TestCase يوفر بيانات مضمنة لاستخدامها لاختبار هذا الأسلوب. يستدعي ReturnRequestedCount NUnit أسلوب اختبار الوحدة كما يلي:

ReturnRequestedCount(0);
ReturnRequestedCount(1);
ReturnRequestedCount(10);

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

سنختار بعض القيم التي تمثل الاستعلامات النموذجية. سنقوم أيضا بتضمين 0 لتغطية حالة الحافة هذه.

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

كما فعلت سابقا، أحضر failed-test الفرع من GitHub وتحقق (أو قم بالتبديل إلى) هذا الفرع.

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

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

    git fetch upstream failed-test
    git checkout -B failed-test upstream/failed-test
    

    لقد أطلقنا على الفرع failed-test اسما لأغراض التعلم. في الممارسة العملية، يمكنك تسمية فرع بعد الغرض منه أو الميزة الخاصة به.

  3. قم بتشغيل هذه الأوامر لإنشاء ملف بيان أداة محلي وتثبيت ReportGenerator الأداة وإضافة الحزمة coverlet.msbuild إلى مشروع الاختبارات:

    dotnet new tool-manifest
    dotnet tool install dotnet-reportgenerator-globaltool
    dotnet add Tailspin.SpaceGame.Web.Tests package coverlet.msbuild
    

    تحتاج إلى هذه الخطوة لأن الإصدار الفرعي failed-test لا يحتوي على العمل الذي أضفته إلى الإصدار الفرعي unit-tests.

  4. أضف ملف مشروع الاختبار وملف بيان الأداة إلى الفهرس المرحلي وقم بتثبيت التغييرات.

    git add Tailspin.SpaceGame.Web.Tests/Tailspin.SpaceGame.Web.Tests.csproj
    git add .config/dotnet-tools.json
    git commit -m "Configure code coverage tests"
    
  5. قم بتشغيل الأمر التالي git push لتحميل failed-test الفرع إلى مستودع GitHub الخاص بك:

    git push origin failed-test
    

راجع فشل الاختبار في البنية الأساسية لبرنامج ربط العمليات التجارية

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

  1. في Azure Pipelines، تتبع البنية أثناء تشغيلها عبر البنية الأساسية لبرنامج ربط العمليات التجارية.

  2. قم بتوسيع تشغيل اختبارات الوحدة - مهمة الإصدار أثناء تشغيلها.

    ترى أن ReturnRequestedCount أسلوب الاختبار يفشل.

    A screenshot of Azure Pipelines dashboard showing output log of an assertion failure on the unit test, expecting 10 but was 9.

    يمر الاختبار عندما تكون قيمة الإدخال 0، ولكنها تفشل عندما تكون قيمة الإدخال 1 أو 10.

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

في الممارسة العملية، لن تتبع دائما البنية يدويا أثناء تشغيلها. فيما يلي بعض الطرق التي قد تكتشف بها الفشل:

  • إعلام بالبريد الإلكتروني من Azure DevOps

    يمكنك تكوين Azure DevOps لإرسال إشعار بالبريد الإلكتروني عند اكتمال الإنشاء. يبدأ سطر الموضوع ب "[فشل الإنشاء]" عند فشل البنية.

    A screenshot of a portion of a build failed email notification.

  • Azure Test Plans

    في Azure DevOps، حدد Test Plans، ثم حدد Runs. ترى عمليات تشغيل الاختبار الأخيرة، بما في ذلك الاختبار الذي تم تشغيله للتو. حدد آخر اختبار مكتمل. ترى أن اختبارين من الاختبارات الثمانية فشلا.

    A screenshot of Azure DevOps test run outcome showing two of eight failed tests as a ring chart.

  • لوحة المعلومات

    في Azure DevOps، حدد Overview، ثم حدد Dashboards. ترى الفشل يظهر في عنصر واجهة مستخدم اتجاه نتائج الاختبار. إن عنصر واجهة المستخدم تغطية التعليمات البرمجية فارغ، ما يشير إلى عدم تشغيل تغطية التعليمات البرمجية.

    A screenshot of Azure DevOps dashboard trend chart widget showing two failed test in the last test run.

  • شارة البناء

    على الرغم من أن الفرع failed-test لا يتضمن شارة البناء في ملف README.md، فإليك ما ستراه على GitHub عندما يفشل البناء:

    A screenshot of Azure Pipelines build badge on GitHub indicating a failure.

تحليل فشل الاختبار

عندما تفشل اختبارات الوحدة، يكون لديك عادة خياران، اعتمادا على طبيعة الفشل:

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

إعادة إنتاج الفشل محليا

في هذا القسم، ستقوم بإعادة إنتاج الفشل محليا.

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

  2. في المحطة الطرفية، قم بتشغيل هذا dotnet build الأمر لإنشاء التطبيق:

    dotnet build --configuration Release
    
  3. في المحطة الطرفية، قم بتشغيل هذا dotnet test الأمر لتشغيل اختبارات الوحدة:

    dotnet test --no-build --configuration Release
    

    يجب أن تشاهد نفس الأخطاء التي رأيتها في البنية الأساسية لبرنامج ربط العمليات التجارية. فيما يلي جزء من الإخراج:

    Starting test execution, please wait...
    A total of 1 test files matched the specified pattern.
      Failed ReturnRequestedCount(1) [33 ms]
      Error Message:
         Expected: 1
      But was:  0
    
      Stack Trace:
         at NUnit.Framework.Internal.Commands.TestMethodCommand.Execute(TestExecutionContext context)
       at NUnit.Framework.Internal.Commands.BeforeAndAfterTestCommand.<>c__DisplayClass1_0.<Execute>b__0()
       at NUnit.Framework.Internal.Commands.BeforeAndAfterTestCommand.RunTestMethodInThreadAbortSafeZone(TestExecutionContext context, Action action)
    
      Failed ReturnRequestedCount(10) [1 ms]
      Error Message:
         Expected: 10
      But was:  9
    
      Stack Trace:
         at NUnit.Framework.Internal.Commands.TestMethodCommand.Execute(TestExecutionContext context)
       at NUnit.Framework.Internal.Commands.BeforeAndAfterTestCommand.<>c__DisplayClass1_0.<Execute>b__0()
       at NUnit.Framework.Internal.Commands.BeforeAndAfterTestCommand.RunTestMethodInThreadAbortSafeZone(TestExecutionContext context, Action action)
    
    
    Failed!  - Failed:     2, Passed:     6, Skipped:     0, Total:     8, Duration: 98 ms
    

البحث عن سبب الخطأ

لاحظت أن كل اختبار فاشل ينتج نتيجة يتم إيقافها بمقدار واحد. على سبيل المثال، عند توقع 10، يقوم الاختبار بإرجاع 9.

ألق نظرة على التعليمات البرمجية المصدر للأسلوب الذي يتم اختباره، LocalDocumentDBRepository<T>.GetItemsAsync. يجب أن تشاهد هذا:

public Task<IEnumerable<T>> GetItemsAsync(
    Func<T, bool> queryPredicate,
    Func<T, int> orderDescendingPredicate,
    int page = 1, int pageSize = 10
)
{
    var result = _items
        .Where(queryPredicate) // filter
        .OrderByDescending(orderDescendingPredicate) // sort
        .Skip(page * pageSize) // find page
        .Take(pageSize - 1); // take items

    return Task<IEnumerable<T>>.FromResult(result);
}

في هذا السيناريو، يمكنك التحقق من GitHub لمعرفة ما إذا كان الملف قد تم تغييره مؤخرا.

A screenshot of GitHub showing a file diff where a minus one operation was added.

أنت تشك في إرجاع pageSize - 1 نتيجة أقل وأن هذا يجب أن يكون فقط pageSize. في السيناريو الخاص بنا، هذا خطأ قمت به عند دفع العمل دون اختبار، ولكن في سيناريو العالم الحقيقي، يمكنك التحقق من المطور الذي قام بتغيير الملف على GitHub لتحديد سبب التغيير.

تلميح

يمكن أن تحدث المناقشة والتعاون أيضا على GitHub. يمكنك التعليق على طلب سحب أو فتح مشكلة.

إصلاح الخطأ

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

  1. في Visual Studio Code، افتح Tailspin.SpaceGame.Web/LocalDocumentDBRepository.cs من مستكشف الملفات.

  2. GetItemsAsync تعديل الأسلوب كما هو موضح هنا:

    public Task<IEnumerable<T>> GetItemsAsync(
        Func<T, bool> queryPredicate,
        Func<T, int> orderDescendingPredicate,
        int page = 1, int pageSize = 10
    )
    {
        var result = _items
            .Where(queryPredicate) // filter
            .OrderByDescending(orderDescendingPredicate) // sort
            .Skip(page * pageSize) // find page
            .Take(pageSize); // take items
    
        return Task<IEnumerable<T>>.FromResult(result);
    }
    

    يتغير pageSize - 1 هذا الإصدار إلى pageSize.

  3. احفظ الملف.

  4. في المحطة الطرفية المتكاملة، أنشئ التطبيق.

    dotnet build --configuration Release
    

    يجب أن ترى نجاح البنية.

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

  5. في المحطة الطرفية، قم بتشغيل اختبارات الوحدة.

    dotnet test --no-build --configuration Release
    

    ترى أن الاختبارات قد اجتزت.

    Starting test execution, please wait...
    A total of 1 test files matched the specified pattern.
    
    Passed!  - Failed:     0, Passed:     8, Skipped:     0, Total:     8, Duration: 69 ms
    
  6. في المحطة الطرفية المتكاملة، أضف كل ملف معدل إلى الفهرس، وقم بتنفيذ التغييرات، وادفع الفرع إلى GitHub.

    git add .
    git commit -m "Return correct number of items"
    git push origin failed-test
    

    تلميح

    النقطة (.) في هذا git add المثال هي حرف بدل. يطابق كافة الملفات غير المميزة في الدليل الحالي وجميع الدلائل الفرعية.

    قبل استخدام حرف البدل هذا، من الجيد تشغيله git status قبل الالتزام بالتأكد من أنك تقوم بتدريج الملفات التي تنوي تنظيمها.

  7. العودة إلى Azure Pipelines. شاهد التغيير يتحرك عبر البنية الأساسية لبرنامج ربط العمليات التجارية. تمر الاختبارات، وينجح البناء الكلي.

    اختياريا، للتحقق من نتائج الاختبار، يمكنك تحديد علامات التبويب الاختبارات وتغطية التعليمات البرمجية عند اكتمال الإنشاء.

    يمكنك أيضا التحقق من لوحة المعلومات لعرض اتجاه النتائج المحدثة.

    A screenshot of Azure DevOps dashboard trend chart widget showing a return to all tests passing.

عظيم! لقد قمت بإصلاح البنية. بعد ذلك، ستتعلم كيفية تنظيف بيئة Azure DevOps.