تمرين - إضافة اختبارات الوحدة إلى التطبيق الخاص بك

مكتمل

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

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

A screenshot of the leaderboard showing incorrect results: Andromeda galaxy scores show in the Milky Way galaxy listing.

يريد الفريق التقاط الخطأ قبل أن يصل إلى المختبرين. اختبارات الوحدة هي طريقة رائعة لاختبار أخطاء الانحدار تلقائيا.

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

تتوفر عدة أُطر اختبار الوحدة لتطبيقات C#. سنستخدم NUnit لأنه شائع لدى المجتمع.

فيما يلي اختبار الوحدة الذي تعمل معه:

[TestCase("Milky Way")]
[TestCase("Andromeda")]
[TestCase("Pinwheel")]
[TestCase("NGC 1300")]
[TestCase("Messier 82")]
public void FetchOnlyRequestedGameRegion(string gameRegion)
{
    const int PAGE = 0; // take the first page of results
    const int MAX_RESULTS = 10; // sample up to 10 results

    // Form the query predicate.
    // This expression selects all scores for the provided game region.
    Expression<Func<Score, bool>> queryPredicate = score => (score.GameRegion == gameRegion);

    // Fetch the scores.
    Task<IEnumerable<Score>> scoresTask = _scoreRepository.GetItemsAsync(
        queryPredicate, // the predicate defined above
        score => 1, // we don't care about the order
        PAGE,
        MAX_RESULTS
    );
    IEnumerable<Score> scores = scoresTask.Result;

    // Verify that each score's game region matches the provided game region.
    Assert.That(scores, Is.All.Matches<Score>(score => score.GameRegion == gameRegion));
}

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

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

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

FetchOnlyRequestedGameRegion("Milky Way");
FetchOnlyRequestedGameRegion("Andromeda");
FetchOnlyRequestedGameRegion("Pinwheel");
FetchOnlyRequestedGameRegion("NGC 1300");
FetchOnlyRequestedGameRegion("Messier 82");

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

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

ضع في اعتبارك التأكيد الذي تم إجراؤه في هذا المثال:

Assert.That(scores, Is.All.Matches<Score>(score => score.GameRegion == gameRegion));

قد تقرأ هذا السطر على النحو التالي:

تأكد من أن منطقة اللعبة لكل نتيجة تم إرجاعها تطابق منطقة اللعبة المتوفرة.

إليك العملية التي يجب اتباعها:

  1. إحضار فرع من مستودع GitHub الذي يحتوي على اختبارات الوحدة.
  2. قم بتشغيل الاختبارات محليا للتحقق من اجتيازها.
  3. أضف المهام إلى تكوين البنية الأساسية لبرنامج ربط العمليات التجارية الخاصة بك لتشغيل الاختبارات وجمع النتائج.
  4. ادفع الفرع إلى مستودع GitHub الخاص بك.
  5. شاهد مشروع Azure Pipelines الخاص بك يقوم تلقائيا بإنشاء التطبيق وتشغيل الاختبارات.

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

هنا، ستقوم بإحضار unit-tests الفرع من GitHub وسحب هذا الفرع أو التبديل إليه.

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

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

  2. قم بتشغيل الأوامر التالية git لإحضار فرع يسمى unit-tests من مستودع Microsoft، ثم قم بالتبديل إلى هذا الفرع.

    git fetch upstream unit-tests
    git checkout -B unit-tests upstream/unit-tests
    

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

  3. كخطوة اختيارية، افتح ملف azure-pipelines.yml في Visual Studio Code وتعرف على التكوين الأولي. يشبه التكوين الأساسي الذي قمت بإنشائه في الوحدة النمطية Create a build pipeline with Azure Pipelines . يقوم بإنشاء تكوين إصدار التطبيق فقط.

تشغيل الاختبارات محليا

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

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

  2. قم بتشغيل dotnet build لإنشاء كل مشروع في الحل.

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

    dotnet test --configuration Release --no-build
    

    --no-build تحدد العلامة عدم إنشاء المشروع قبل تشغيله. لست بحاجة إلى إنشاء المشروع لأنك قمت بإنشائه في الخطوة السابقة.

    يجب أن ترى أن جميع الاختبارات الخمسة قد اجتزت.

    Starting test execution, please wait...
    A total of 1 test files matched the specified pattern.
    
    Passed!  - Failed:     0, Passed:     5, Skipped:     0, Total:     5, Duration: 57 ms
    

    في هذا المثال، استغرق تشغيل الاختبارات أقل من ثانية واحدة.

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

  4. قم بتشغيل الاختبارات مرة ثانية. هذه المرة، قم بتوفير --logger خيار كتابة النتائج إلى ملف سجل.

    dotnet test Tailspin.SpaceGame.Web.Tests --configuration Release --no-build --logger trx
    

    ترى من الإخراج أنه تم إنشاء ملف TRX في دليل TestResults .

    ملف TRX هو مستند XML يحتوي على نتائج تشغيل اختبار. إنه تنسيق شائع لاختبارات NUnit لأن Visual Studio والأدوات الأخرى يمكن أن تساعدك في استعراض النتائج.

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

    إشعار

    لا يقصد بملفات TRX أن يتم تضمينها في التحكم بالمصادر. يسمح لك ملف .gitignore بتحديد الملفات المؤقتة والملفات الأخرى التي تريد أن يتجاهلها Git. تم إعداد ملف .gitignore الخاص بالمشروع بالفعل لتجاهل أي شيء في دليل TestResults.

  5. كخطوة اختيارية، في Visual Studio Code، افتح الملف DocumentDBRepository_GetItemsAsyncShould.cs من المجلد Tailspin.SpaceGame.Web.Tests وافحص رمز الاختبار. حتى إذا لم تكن مهتمًا بإنشاء تطبيقات .NET على وجه التحديد، فقد تجد تعليمة الاختبار البرمجية مفيدة لأنها تشبه التعليمات البرمجية التي قد تراها في أطر عمل اختبار الوحدة الأخرى.

إضافة مهام إلى تكوين البنية الأساسية لبرنامج ربط العمليات التجارية

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

  1. قم بتعديل azure-pipelines.ymlفي تعليمة Visual Studio البرمجية، كما يلي:

    trigger:
    - '*'
    
    pool:
      vmImage: 'ubuntu-20.04'
      demands:
      - npm
    
    variables:
      buildConfiguration: 'Release'
      wwwrootDir: 'Tailspin.SpaceGame.Web/wwwroot'
      dotnetSdkVersion: '6.x'
    
    steps:
    - task: UseDotNet@2
      displayName: 'Use .NET SDK $(dotnetSdkVersion)'
      inputs:
        version: '$(dotnetSdkVersion)'
    
    - task: Npm@1
      displayName: 'Run npm install'
      inputs:
        verbose: false
    
    - script: './node_modules/.bin/node-sass $(wwwrootDir) --output $(wwwrootDir)'
      displayName: 'Compile Sass assets'
    
    - task: gulp@1
      displayName: 'Run gulp tasks'
    
    - script: 'echo "$(Build.DefinitionName), $(Build.BuildId), $(Build.BuildNumber)" > buildinfo.txt'
      displayName: 'Write build info'
      workingDirectory: $(wwwrootDir)
    
    - task: DotNetCoreCLI@2
      displayName: 'Restore project dependencies'
      inputs:
        command: 'restore'
        projects: '**/*.csproj'
    
    - task: DotNetCoreCLI@2
      displayName: 'Build the project - $(buildConfiguration)'
      inputs:
        command: 'build'
        arguments: '--no-restore --configuration $(buildConfiguration)'
        projects: '**/*.csproj'
    
    - task: DotNetCoreCLI@2
      displayName: 'Run unit tests - $(buildConfiguration)'
      inputs:
        command: 'test'
        arguments: '--no-build --configuration $(buildConfiguration)'
        publishTestResults: true
        projects: '**/*.Tests.csproj'
    
    - task: DotNetCoreCLI@2
      displayName: 'Publish the project - $(buildConfiguration)'
      inputs:
        command: 'publish'
        projects: '**/*.csproj'
        publishWebProjects: false
        arguments: '--no-build --configuration $(buildConfiguration) --output $(Build.ArtifactStagingDirectory)/$(buildConfiguration)'
        zipAfterPublish: true
    
    - task: PublishBuildArtifacts@1
      displayName: 'Publish Artifact: drop'
      condition: succeeded()
    

    يقدم هذا الإصدار مهمة الإنشاء هذه DotNetCoreCLI@2 .

    - task: DotNetCoreCLI@2
      displayName: 'Run unit tests - $(buildConfiguration)'
      inputs:
        command: 'test'
        arguments: '--no-build --configuration $(buildConfiguration)'
        publishTestResults: true
        projects: '**/*.Tests.csproj'
    

    تقوم مهمة الإنشاء هذه بتشغيل dotnet test الأمر .

    لاحظ أن هذه المهمة لا تحدد الوسيطة --logger trx التي استخدمتها عند تشغيل الاختبارات يدوياً. publishTestResults تضيف الوسيطة ذلك نيابة عنك. تخبر هذه الوسيطة المسار بإنشاء ملف TRX إلى دليل مؤقت، يمكن الوصول إليه من خلال $(Agent.TempDirectory) المتغير المضمن. كما ينشر نتائج المهمة إلى البنية الأساسية لبرنامج ربط العمليات التجارية.

    تحدد الوسيطة projects جميع مشاريع C# التي تطابق "**/*.Tests.csproj." يتطابق الجزء "**" مع جميع الأدلة، ويتطابق الجزء "*.Tests.csproj" مع جميع المشاريع التي ينتهي اسم ملفها بـ ".Tests.csproj." يحتوي الفرع unit-tests على مشروع تجريبي واحد فقط، Tailspin.SpaceGame.Web.Tests.csproj. من خلال تحديد نمط، يمكنك تشغيل المزيد من مشاريع الاختبار دون الحاجة إلى تعديل تكوين البناء الخاص بك.

دفع الفرع إلى GitHub

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

  1. في الوحدة الطرفية المتكاملة، أضف azure-pipelines.yml إلى الفهرس، وقم بتثبيت التغييرات، وادفع الفرع إلى GitHub.

    git add azure-pipelines.yml
    git commit -m "Run and publish unit tests"
    git push origin unit-tests
    

شاهد Azure Pipelines تقوم بتشغيل الاختبارات

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

  1. في Azure Pipelines، تتبع البنية من خلال كل خطوة من الخطوات.

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

    A screenshot of Azure Pipelines showing console output from running unit tests.

  2. انتقل مرة أخرى إلى ملخص البنية الأساسية لبرنامج ربط العمليات التجارية.

  3. انتقل إلى علامة التبويب الاختبارات .

    ترى ملخصا لتشغيل الاختبار. لقد اجتازت جميع الاختبارات الخمسة.

    A screenshot of Azure Pipelines showing the Tests tab with 5 total tests run and 100 percent passing.

  4. في Azure DevOps، حدد Test Plans، ثم حدد Runs.

    A screenshot of Azure DevOps navigation menu with Test Plans section and Runs tab highlighted.

    ترى أحدث عمليات تشغيل الاختبار، بما في ذلك الاختبار الذي قمت بتشغيله للتو.

  5. انقر نقرا مزدوجا فوق أحدث تشغيل اختبار.

    ترى ملخصا للنتائج.

    A screenshot of Azure DevOps test run results summary showing 5 passed tests.

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

    يمكنك أيضًا تنزيل ملف TRX لفحصه من خلال Visual Studio أو أداة تصور أخرى.

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

دمج فرعك في الفرع الرئيسي

في سيناريو العالم الحقيقي، إذا كنت راضيا عن النتائج، فقد تدمج unit-tests الفرع في main، ولكن للإيجاز، سنتخطى هذه العملية في الوقت الحالي.