الفرق بين المراجعتين لصفحة: «Rails/testing»

من موسوعة حسوب
لا ملخص تعديل
طلا ملخص تعديل
 
(4 مراجعات متوسطة بواسطة نفس المستخدم غير معروضة)
سطر 1: سطر 1:
= اختبارات تطبيقات ريلز =
<noinclude>{{DISPLAYTITLE:اختبار تطبيقات ريلز}}</noinclude>
يشمل هذا الدليل الآليّات المدمجة في ريلز لإجراء الاختبارت على التطبيقات. بعد قراءة هذا الدليل، ستتعلم:
يشمل هذا الدليل الآليّات المدمجة في ريلز لإجراء الاختبارت على التطبيقات. بعد قراءة هذا الدليل، ستتعلم:
* الاصطلاح المتعلّق باختبارات ريلز.
* الاصطلاح المتعلّق باختبارات ريلز.
سطر 16: سطر 16:
controllers/          helpers/              mailers/              system/                test_helper.rb
controllers/          helpers/              mailers/              system/                test_helper.rb
fixtures/              integration/          models/                application_system_test_case.rb
fixtures/              integration/          models/                application_system_test_case.rb
</syntaxhighlight>من المفترض أن تحوي المجلّدات ‏helpers، و ‏‏mailers‏، و models الاختبارات الخاصّة بالدّوال المساعدة للواجهة، وإجراء المراسلة، والنّماذج على التّوالي. بينما يحتوي المجلّد controllers  على الاختبارات الخاصّة بوحدات التحكّم، ومسارات التوجيه، والواجهات. أما المجلّد integration، فإنّه يحوي الاختبارات الخاصّة بالتفاعلات بين وحدات التحكّم.
</syntaxhighlight>من المفترض أن تحوي المجلّدات ‏helpers، و ‏‏mailers‏، و models الاختبارات الخاصّة بالدّوال المساعدة للواجهة، و [[Rails/action mailer|Action Mailer]]، والنّماذج على التّوالي. بينما يحتوي المجلّد controllers  على الاختبارات الخاصّة بوحدات التحكّم، ومسارات التوجيه، والواجهات. أما المجلّد integration، فإنّه يحوي الاختبارات الخاصّة بالتفاعلات بين وحدات التحكّم.


يحتوي المجلد system على اختبارات النّظام التي تستعمل عند الاختبارات الشّاملة على المتصفّح. تمكّنك اختبارات النّظام من تجربة تطبيقك على الوجه الذي يُفترض بالمستخدمين أن يجرّبوه، كما تمكّنك أيضًا من اختبار [[JavaScript]]. هذا بالإضافة إلى أنّها تَرِث من Capybara.
يحتوي المجلد system على اختبارات النّظام التي تستعمل عند الاختبارات الشّاملة على المتصفّح. تمكّنك اختبارات النّظام من تجربة تطبيقك على الوجه الذي يُفترض بالمستخدمين أن يجرّبوه، كما تمكّنك أيضًا من اختبار [[JavaScript]]. هذا بالإضافة إلى أنّها تَرِث من Capybara.


تمثّل معدات الاختبار (Fixtures) طريقةً لتنظيم بيانات الاختبار وتقع في المجلّد fixtures، ويُنشَأ المجلّد jobs حال تولُّيد أوّل اختبار تابع له. يحوي الملفّ test_helper.rb الضّبط الافتراضيّ للاختبارات ككُلّ، بينما يحوي الملفّ application_system_test_case.rb الضّبط الافتراضيّ لاختبارات النّظام.
تمثّل تحضيرات الاختبار (Fixtures) طريقةً لتنظيم بيانات الاختبار وتقع في المجلّد fixtures، ويُنشَأ المجلّد jobs حال تولُّيد أوّل اختبار تابع له. يحوي الملفّ test_helper.rb الضّبط الافتراضيّ للاختبارات ككُلّ، بينما يحوي الملفّ application_system_test_case.rb الضّبط الافتراضيّ لاختبارات النّظام.


=== بيئة الاختبارات ===
=== بيئة الاختبارات ===
سطر 66: سطر 66:
assert true
assert true
</syntaxhighlight>التحقّق هو سطر شيفرة يعمل على تقدير صحّة كائن (أو تعبير) ما. من أمثلة ذلك:
</syntaxhighlight>التحقّق هو سطر شيفرة يعمل على تقدير صحّة كائن (أو تعبير) ما. من أمثلة ذلك:
* هل هذه القيمة = تلك القيمة؟
* هل هذه القيمة تساوي تلك القيمة؟
* هل هذا الكائن هو كائن عدمي <code>nil</code>؟
* هل هذا الكائن هو كائن عدمي <code>nil</code>؟
* هل سيولّد سطر الشيفرة هذا استثتاءً؟
* هل سيولّد سطر الشيفرة هذا استثتاءً؟
سطر 283: سطر 283:
|-
|-
|<code>[http://api.rubyonrails.org/v5.2.1/classes/ActiveSupport/Testing/Assertions.html#method-i-assert_difference assert_difference(expressions, difference = 1, message = nil) {...}‎]</code>
|<code>[http://api.rubyonrails.org/v5.2.1/classes/ActiveSupport/Testing/Assertions.html#method-i-assert_difference assert_difference(expressions, difference = 1, message = nil) {...}‎]</code>
|يتحقّق من الاختلاف العددي بين القيمة المعادة للتعبير expressions وبين النتيجة المقيَّمة في الكتلة المعطاة.
|يتحقّق من الاختلاف العددي بين القيمة المعادة للتعبير <code>expressions</code> وبين النتيجة المقيَّمة في الكتلة المعطاة.
|-
|-
|<code>[http://api.rubyonrails.org/v5.2.1/classes/ActiveSupport/Testing/Assertions.html#method-i-assert_no_difference assert_no_difference(expressions, message = nil, &block)‎]</code>
|<code>[http://api.rubyonrails.org/v5.2.1/classes/ActiveSupport/Testing/Assertions.html#method-i-assert_no_difference assert_no_difference(expressions, message = nil, &block)‎]</code>
|اختبار الناتج العددي لتقييم التعبير expressions والتأكد من عدم تغيره قبل وبعد تمريره إلى الكتلة.
|اختبار الناتج العددي لتقييم التعبير <code>expressions</code> والتأكد من عدم تغيره قبل وبعد تمريره إلى الكتلة.
|-
|-
|<code>[http://api.rubyonrails.org/v5.2.1/classes/ActiveSupport/Testing/Assertions.html#method-i-assert_changes assert_changes(expressions, message = nil, from:, to:, &block)‎]</code>
|<code>[http://api.rubyonrails.org/v5.2.1/classes/ActiveSupport/Testing/Assertions.html#method-i-assert_changes assert_changes(expressions, message = nil, from:, to:, &block)‎]</code>
|التحقق من تغيّر ناتج تقييم التعبير expressions بعد تمريره إلى الكتلة.
|التحقق من تغيّر ناتج تقييم التعبير <code>expressions</code> بعد تمريره إلى الكتلة.
|-
|-
|<code>[http://api.rubyonrails.org/v5.2.1/classes/ActiveSupport/Testing/Assertions.html#method-i-assert_no_changes assert_no_changes(expressions, message = nil, &block)‎]</code>
|<code>[http://api.rubyonrails.org/v5.2.1/classes/ActiveSupport/Testing/Assertions.html#method-i-assert_no_changes assert_no_changes(expressions, message = nil, &block)‎]</code>
|التحقق من عدم تغير تقييم التعبير expressions بعد تمريره إلى الكتلة.
|التحقق من عدم تغير تقييم التعبير <code>expressions</code> بعد تمريره إلى الكتلة.
|-
|-
|<code>[http://api.rubyonrails.org/v5.2.1/classes/ActiveSupport/Testing/Assertions.html#method-i-assert_nothing_raised assert_nothing_raised { block }‎]</code>
|<code>[http://api.rubyonrails.org/v5.2.1/classes/ActiveSupport/Testing/Assertions.html#method-i-assert_nothing_raised assert_nothing_raised { block }‎]</code>
سطر 298: سطر 298:
|-
|-
|<code>[http://api.rubyonrails.org/v5.2.1/classes/ActionDispatch/Assertions/RoutingAssertions.html#method-i-assert_recognizes assert_recognizes(expected_options, path, extras={}, message=nil)‎]</code>
|<code>[http://api.rubyonrails.org/v5.2.1/classes/ActionDispatch/Assertions/RoutingAssertions.html#method-i-assert_recognizes assert_recognizes(expected_options, path, extras={}, message=nil)‎]</code>
|اختبار عملية إعادة التوجيه للمسار المعطى والتأكد من أنها نُفِّذت بشكل صحيح وأن الخيارات المحللة (المعطاة في expected_options الذي هو جدول Hash) تطابق المسار. بشكل أساسي، يتأكد هذا الاختبار من تعرف ريلز مسار التوجيه المعطى عبر expected_options.
|اختبار عملية إعادة التوجيه للمسار المعطى والتأكد من أنها نُفِّذت بشكل صحيح وأن الخيارات المحللة (المعطاة في <code>expected_options</code> الذي هو جدول <code>[[Ruby/Hash|Hash]]</code>) تطابق المسار. بشكل أساسي، يتأكد هذا الاختبار من تعرف ريلز مسار التوجيه المعطى عبر <code>expected_options</code>.
|-
|-
|<code>[http://api.rubyonrails.org/v5.2.1/classes/ActionDispatch/Assertions/RoutingAssertions.html#method-i-assert_generates assert_generates(expected_path, options, defaults={}, extras = {}, message=nil)‎]</code>
|<code>[http://api.rubyonrails.org/v5.2.1/classes/ActionDispatch/Assertions/RoutingAssertions.html#method-i-assert_generates assert_generates(expected_path, options, defaults={}, extras = {}, message=nil)‎]</code>
|اختبار الخيارات المعطاة والتأكد من إمكانية استعمالها لتوليد المسار المعطى. هذا الاختبار هو عكس الاختبار assert_recognizes.  تستعمل المعاملات الإضافية لإخبار الطلب بأسماء وقيم معاملات الطلب الإضافية التي ستكون في سلسلة الاستعلام. يسمح المعامل message بتحديد رسالة خطأ مخصصة لعرضها عند فشل الاختبار.
|اختبار الخيارات المعطاة والتأكد من إمكانية استعمالها لتوليد المسار المعطى. هذا الاختبار هو عكس الاختبار <code>assert_recognizes</code>.  تستعمل المعاملات الإضافية لإخبار الطلب بأسماء وقيم معاملات الطلب الإضافية التي ستكون في سلسلة الاستعلام. يسمح المعامل <code>message</code> بتحديد رسالة خطأ مخصصة لعرضها عند فشل الاختبار.
|-
|-
|<code>[http://api.rubyonrails.org/v5.2.1/classes/ActionDispatch/Assertions/ResponseAssertions.html#method-i-assert_response assert_response(type, message = nil)‎]</code>
|<code>[http://api.rubyonrails.org/v5.2.1/classes/ActionDispatch/Assertions/ResponseAssertions.html#method-i-assert_response assert_response(type, message = nil)‎]</code>
|اختبار الرد (response) والتأكد من احتوائه عىل رمز حالة محدد. يمكنك أن تحدد ‎:success ليشير إلى المجال 200-299، أو ‎:redirect ليشير إلى المجال 300-399، أو ‎:missing ليشير إلى 404، أو ‎:error ليشير إلى المجال 500-599. يمكنك أيضًا تمرير رقم حالة مجرد أو رمزه المقابل. لمزيد من المعلومات، اطلع على قائمة رموز الحالة الكاملة وكيفية إعادة تعيينها.
|اختبار الرد (response) والتأكد من احتوائه عىل رمز حالة محدد. يمكنك أن تحدد <code>‎:success</code> ليشير إلى المجال 200-299، أو <code>‎:redirect</code> ليشير إلى المجال 300-399، أو <code>‎:missing</code> ليشير إلى 404، أو <code>‎:error</code> ليشير إلى المجال 500-599. يمكنك أيضًا تمرير رقم حالة مجرد أو رمزه المقابل. لمزيد من المعلومات، اطلع على قائمة رموز الحالة الكاملة وكيفية إعادة تعيينها.
|-
|-
|<code>[http://api.rubyonrails.org/v5.2.1/classes/ActionDispatch/Assertions/ResponseAssertions.html#method-i-assert_redirected_to assert_redirected_to(options = {}, message=nil)‎]</code>
|<code>[http://api.rubyonrails.org/v5.2.1/classes/ActionDispatch/Assertions/ResponseAssertions.html#method-i-assert_redirected_to assert_redirected_to(options = {}, message=nil)‎]</code>
|اختبار خيارات إعادة التوجيه الممررة والتأكد من مطابقتها لتلك المُمرَّرة إلى عملية إعادة التوجيه المستدعاة في أحدث إجراء. يمكن أن تكون هذه المطابقة جزئية مثل assert_redirected_to(controller: "weblog")‎ الذي سيطابق أيَضًا إعادة التوجيه redirect_to(controller: "weblog", action: "show")‎ وهلم جرًا. يمكنك أيضًا تمرير مسارات موجهة مسماة مثل assert_redirected_to root_path وكائنات سجل فعال مثل assert_redirected_to @article.
|اختبار خيارات إعادة التوجيه الممررة والتأكد من مطابقتها لتلك المُمرَّرة إلى عملية إعادة التوجيه المستدعاة في أحدث إجراء. يمكن أن تكون هذه المطابقة جزئية مثل <code>assert_redirected_to(controller: "weblog")</code>‎ الذي سيطابق أيَضًا إعادة التوجيه <code>redirect_to(controller: "weblog", action: "show")‎</code> وهلم جرًا. يمكنك أيضًا تمرير مسارات موجهة مسماة مثل <code>assert_redirected_to root_path</code> وكائنات [[Rails/active record|Active Record]] مثل <code>assert_redirected_to @article</code>.
|}
|}
ستتطرّق إلى استعمالات بعض هذه التحقّقات في الفصل القادم.
ستتطرّق إلى استعمالات بعض هذه التحقّقات في الفصل القادم.


=== ملاحظة وجيزة حول حالات الاختبار ===
=== ملاحظة وجيزة حول حالات الاختبار ===
جميع التحقّقات الأساسيّة المُعرّفة في Minitest::Assertions مُتاحة أيضًا في الأصناف التي نستعملها في حالات الاختبار الخاصّة بنا. يُوفّر لك ريلز الأصناف الآتية لترِث منها:
جميع التحقّقات الأساسيّة المُعرّفة في <code>Minitest::Assertions</code> مُتاحة أيضًا في الأصناف التي نستعملها في حالات الاختبار الخاصّة بنا. يُوفّر لك ريلز الأصناف الآتية لترِث منها:
* ActiveSupport::TestCase
* <code>[http://api.rubyonrails.org/v5.2.2/classes/ActiveSupport/TestCase.html ActiveSupport::TestCase]</code>
* ActionMailer::TestCase
* <code>[http://api.rubyonrails.org/v5.2.2/classes/ActionMailer/TestCase.html ActionMailer::TestCase]</code>
* ActionView::TestCase
* <code>[http://api.rubyonrails.org/v5.2.2/classes/ActionView/TestCase.html ActionView::TestCase]</code>
* ActiveJob::TestCase
* <code>[http://api.rubyonrails.org/v5.2.2/classes/ActiveJob/TestCase.html ActiveJob::TestCase]</code>
* ActionDispatch::IntegrationTest
* <code>[http://api.rubyonrails.org/v5.2.2/classes/ActionDispatch/IntegrationTest.html ActionDispatch::IntegrationTest]</code>
* ActionDispatch::SystemTestCase
* <code>[http://api.rubyonrails.org/v5.2.2/classes/ActionDispatch/SystemTestCase.html ActionDispatch::SystemTestCase]</code>
* ريلز::Generators::TestCase
* <code>[http://api.rubyonrails.org/v5.2.2/classes/Rails/Generators/TestCase.html Rails::Generators::TestCase]</code>
تتضمّن كلّ هذه الأصناف Minitest::Assertions ممّا يُمكّننا من استعمال جميع التحقّقات الأساسيّة في الاختبارات الخاصّة بنا.
تتضمّن كلّ هذه الأصناف <code>Minitest::Assertions</code> ممّا يُمكّننا من استعمال جميع التحقّقات الأساسيّة في الاختبارات الخاصّة بنا.


ملاحظة: لمزيد من المعلومات حول Minitest يُرجى الرّجوع إلى التوثيق الخاصّ بها.
'''ملاحظة''': لمزيد من المعلومات حول <code>Minitest</code>، يُرجى الرّجوع إلى [http://docs.seattlerb.org/minitest التوثيق الخاصّ بها].
 
=== منفّذ الاختبارات في ريلز ===
من الممكن تنفيذ جميع الاختبارات دفعة واحدة باستعمال الأمر bin/ريلز test. كما يمكننا أن ننفّذ اختبارًا واحدًا من خلال تمرير اسم الملفّ الذي يحوي حالات الاختبار للأمر bin/ريلز test.
 
$ bin/ريلز test test/models/article_test.rb


=== منفذ الاختبارات في ريلز ===
من الممكن تنفيذ جميع الاختبارات دفعة واحدة باستعمال الأمر <code>bin/rails test</code>. كما يمكننا أن ننفّذ اختبارًا واحدًا من خلال تمرير اسم الملفّ الذي يحوي حالات الاختبار للأمر <code>bin/rails test</code>.<syntaxhighlight lang="shell">
$ bin/rails test test/models/article_test.rb
Run options: --seed 1559
Run options: --seed 1559
 
<nowiki>#</nowiki> Running:
# Running:
 
..
..
 
Finished in 0.027034s, 73.9810 runs/s, 110.9715 assertions/s.
Finished in 0.027034s, 73.9810 runs/s, 110.9715 assertions/s.
 
2 runs, 3 assertions, 0 failures, 0 errors, 0 skips
2 runs, 3 assertions, 0 failures, 0 errors, 0 skips
</syntaxhighlight>سيُنفّذ هذا كلّ توابع الاختبار في حالة الاختبار هذه.


سيُنفّذ هذا كلّ توابع الاختبار في حالة الاختبار هذه.
يُمكن أيضًا تنفيذ تابع اختبار معيّن في حالة الاختبار من خلال إعطاء الراية <code>‎-n</code>‎‎‎ أو <code>‎--name‎</code> بالإضافة إلى اسم تابع الاختبار.<syntaxhighlight lang="shell">
 
$ bin/rails test test/models/article_test.rb -n test_the_truth
يُمكن أيضًا تنفيذ تابع اختبار معيّن في حالة الاختبار من خلال إعطاء الراية ‎‎‏‏‏‎‎‎‎‎-n‎‎‎ أو ‎--name‎ بالإضافة إلى اسم تابع الاختبار.
 
$ bin/ريلز test test/models/article_test.rb -n test_the_truth
 
Run options: -n test_the_truth --seed 43583
Run options: -n test_the_truth --seed 43583
 
<nowiki>#</nowiki> Running:
# Running:
 
.
.
 
Finished tests in 0.009064s, 110.3266 tests/s, 110.3266 assertions/s.
Finished tests in 0.009064s, 110.3266 tests/s, 110.3266 assertions/s.
 
1 tests, 1 assertions, 0 failures, 0 errors, 0 skips
1 tests, 1 assertions, 0 failures, 0 errors, 0 skips
</syntaxhighlight>يُمكنك أيضًا تنفيذ سطرٍ معيّن من الاختبار وذلك بإعطاء رقمه:<syntaxhighlight lang="shell">
$ bin/rails test test/models/article_test.rb:6 # تنفيذ سطر محدد من الاختبار
</syntaxhighlight>بالإضافة إلى ذلك، يُمكنك تنفيذ مجلّد كامل من الاختبارات من خلال إعطاء المسار إلى هذا المجلّد:<syntaxhighlight lang="shell">
$ bin/rails test test/controllers # تنفيذ جميع الاختبارات الواقعة في مجلد محدد
</syntaxhighlight>يُقدّم مُنفّذ الاختبارات العديد من المزايا الأخرى كالفشل بسرعة وتأجيل المخرجات إلى حين الانتهاء من تنفيذ الاختبار إلى غير ذلك.


يُمكنك أيضًا تنفيذ سطرٍ معيّن من الاختبار وذلك بإعطاء رقمه.
يمكنك الاطلاع على التوثيق الخاصّ بمنفّذ الاختبار كما يأتي:<syntaxhighlight lang="shell">
 
$ bin/rails test -h
$ bin/ريلز test test/models/article_test.rb:6 # run specific test and line
 
بالإضافة إلى ذلك، يُمكنك تنفيذ مجلّد كامل من الاختبارات من خلال إعطاء المسار إلى هذا المجلّد.
 
$ bin/ريلز test test/controllers # run all tests from specific directory
 
يُقدّم مُنفّذ الاختبارات العديد من المزايا الأخرى كالفشل بسرعة وتأجيل المخرجات إلى حين الانتهاء من تنفيذ الاختبار إلى غير ذلك.
 
يمكنك الاطلاع على التوثيق الخاصّ بمنفّذ الاختبار كما يأتي:
 
$ bin/ريلز test -h
 
minitest options:
minitest options:
 
    -h, --help                       Display this help.
   -h, --help                    Display this help.
    -s, --seed SEED                 Sets random seed. Also via env. Eg: SEED=n rake
 
    -v, --verbose                   Verbose. Show progress processing files.
   -s, --seed SEED               Sets random seed. Also via env. Eg: SEED=n rake
    -n, --name PATTERN               Filter run on /regexp/ or string.
 
        --exclude PATTERN           Exclude /regexp/ or string from run.
   -v, --verbose                 Verbose. Show progress processing files.
 
Known extensions: rails, pride
   -n, --name PATTERN            Filter run on /regexp/ or string.
 
Usage: bin/rails test [options] [files or directories]
       --exclude PATTERN         Exclude /regexp/ or string from run.
 
Known extensions: ريلز, pride
 
Usage: bin/ريلز test [options] [files or directories]
 
You can run a single test by appending a line number to a filename:
You can run a single test by appending a line number to a filename:
 
   bin/ريلز test test/models/user_test.rb:27
    bin/rails test test/models/user_test.rb:27
 
You can run multiple files and directories at the same time:
You can run multiple files and directories at the same time:
 
   bin/ريلز test test/controllers test/integration/login_test.rb
    bin/rails test test/controllers test/integration/login_test.rb
 
By default test failures and errors are reported inline during a run.
By default test failures and errors are reported inline during a run.
Rails options:
    -w, --warnings                  Run with Ruby warnings enabled
    -e, --environment                Run tests in the ENV environment
    -b, --backtrace                  Show the complete backtrace
    -d, --defer-output              Output test failures and errors after the test run
    -f, --fail-fast                  Abort test run on first failure or error
    -c, --[no-]color                Enable color in the output
</syntaxhighlight>


ريلز options:
== قاعدة بيانات الاختبارات ==
 
لا يكاد يكون تطبيق ريلز إلّا وله تفاعل كبير مع قاعدة بيانات، لذا فلابدّ لاختباراتك من قاعدة بيانات لتتفاعل معها كذلك. لكتابة اختبارات فعّالة، عليك أن تفهم كيفيّة تهيئة قاعدة البيانات هذه وملئها بعيّنات من البيانات.
   -w, --warnings                Run with Ruby warnings enabled
 
   -e, --environment             Run tests in the ENV environment
 
   -b, --backtrace               Show the complete backtrace
 
   -d, --defer-output            Output test failures and errors after the test run
 
   -f, --fail-fast               Abort test run on first failure or error


   -c, --[no-]color              Enable color in the output
لكلّ تطبيق ريلز ثلاث بيئات افتراضيّة: بيئة التطوير، بيئة الاختبار، وبيئة الإنتاج. تُضبط قاعدة البيانات لكلٍّ من هذه البيئات في الملف config/database.yml.
 
= قاعدة بيانات الاختبارات =
لا يكاد يكون تطبيق ريلز إلّا وله تفاعل كبير مع قاعدة بيانات، لذا فلابدّ لاختباراتك من قاعدة بيانات لتتفاعل معها كذلك. لكتابة اختبارات فعّالة، عليك أن نفهم كيفيّة تهيئة قاعدة البيانات هذه و ملئها بعيّنات من البيانات.
 
لكلّ تطبيق ريلز ثلاث بيئات افتراضيّة: بيئة التطوير، بيئة الاختبار، وبيئة الإنتاج. تُضبط قاعدة البيانات لكلٍّ من هذه البيئات في config/database.yml.


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


=== صيانة مخطّط قاعدة البيانات الاختبار ===
=== صيانة مخطط قاعدة بيانات الاختبار ===
لتنفيذ الاختبارات الخاصّة بك، يجب أن تكون قاعدة البيانات ذات بنية حالّية. يبحث مساعد الاختبار عمّا إذا كان لقاعدة البيانات تهجيرات معلّقة، وذلك بمحاولة تحميل db/schema.rb أو db/structure.sql الخاصّين بك في قاعدة البيانات. فإن كانت هناك تهجيرات لا تزال معلّقة، يتمّ إطلاق خطأ. يدلّ هذا عادةً على أن المخطّط الخاصّ بك لم يُهجّر كليّةً، ويمكن تحديثه عن طريق إجراء التهجيرات مقابل قاعدة بيانات التطوير (bin/ريلز db:migrate).    
لتنفيذ الاختبارات الخاصّة بك، يجب أن تكون قاعدة البيانات ذات بنية حالّية. يبحث مساعد الاختبار عمّا إذا كان لقاعدة البيانات تهجيرات معلّقة، وذلك بمحاولة تحميل db/schema.rb أو db/structure.sql الخاصّين بك في قاعدة البيانات. فإن كانت هناك تهجيرات لا تزال معلّقة، يتمّ إطلاق خطأ. يدلّ هذا عادةً على أنَّ المخطّط الخاصّ بك لم يُهجّر كليّةً، ويمكن تحديثه عن طريق إجراء التهجيرات مقابل قاعدة بيانات التطوير (عبر الأمر <code>bin/rails db:migrate</code>).    


ملاحظة: إذا أُحدِثت تغييرات على تهجيرات حاليّة، فلابد من إعادة بناء قاعدة البيانات. يمكن فعل ذلك عن طريق تنفيذ bin/ريلز db:test:prepare.
'''ملاحظة''': إذا أُحدِثت تغييرات على تهجيرات حاليّة، فلابد من إعادة بناء قاعدة البيانات. يمكن فعل ذلك عن طريق تنفيذ الأمر <code>bin/rails db:test:prepare</code>.


=== حقيقة الـ Fixtures ===
=== حقيقة تحضيرات الاختبار (Fixtures) ===
من أجل اختبارات جيّدة، ينبغي الاهتمام بتهيئة بيانات الاختبارات. يكون ذلك في ريلز بتعريف وتخصيص ما يُسمّى بـ Fixtures. بإمكان الاطلاع على التوثيق الشامل الخاص ّ بها في Fixtures API documentation.
من أجل اختبارات جيّدة، ينبغي الاهتمام بتهيئة بيانات الاختبارات. يكون ذلك في ريلز بتعريف وتخصيص ما يُسمّى "بتحضيرات الاختبار" (Fixtures). بإمكانك الاطلاع أكثر عليها في [http://api.rubyonrails.org/v5.2.2/classes/ActiveRecord/FixtureSet.html التوثيق الشامل للواجهة البرمجية].


==== ما هي الـ Fixtures؟ ====
==== ما هي معدات الاختبار؟ ====
الـ Fixtures هو إطلاق مزخرف على عيّنات البيانات. تمكّنك الـ Fixtures من تعمير قاعدة بيانات الاختبار ببيانات معرّفة مسبقًا قبل تنفيذ الاختبار. تُكتب الـ  Fixtures بلغة YAML و تكون مستقلّة عن قاعدة البيانات. يوجد هناك ملفّ واحد لكلّ نموذج.
تحضيرات الاختبار (Fixtures) هو إطلاق مزخرف على "عيّنات البيانات" (sample data). تمكّنك تحضيرات الاختبارات (Fixtures) من تعمير قاعدة بيانات الاختبار ببيانات معرّفة مسبقًا قبل تنفيذ الاختبار. تُكتب التحضيرات بلغة YAML و تكون مستقلّة عن قاعدة البيانات. يوجد هناك ملفّ واحد لكلّ نموذج.


ملاحظة: ليست الـ Fixtures مصمَّمة لإنشاء جميع الكائنات التي تحتاجها اختباراتك، وتكون إدارتها أفضل عندما تُستعمل مع البيانات الافتراضيّة التي يمكن تطبيقها في الحالة الشائعة.
'''ملاحظة''': ليست التحضيرات (Fixtures) مصمَّمة لإنشاء جميع الكائنات التي تحتاجها اختباراتك، وتكون إدارتها أفضل عندما تُستعمل مع البيانات الافتراضيّة التي يمكن تطبيقها في الحالة الشائعة.


بإمكانك إيجاد الـ Fixtures في المجلّد test/fixtures. عندما تنفّذ الأمر ريلز generate model لإنشاء نموذج جديد، يُنشئ ريلز بذور Fixtures في هذا المجلّد.
بإمكانك إيجاد تحضيرات الاختبار (Fixtures) في المجلّد test/fixtures. عندما تنفّذ الأمر <code>rails generate model</code> لإنشاء نموذج جديد، يُنشئ ريلز بذورًا للحضيرات (fixture stubs) في هذا المجلّد.


==== YAML ====
==== YAML ====
تعتبر الـ Fixtures المهيّهة بـ YAML طريقة لتمثيل عيّنات البيانات على نحوٍ يفهمه البشر. يكون لهذا النوع الـ Fixtures امتداد .yml (كما في users.yml).
تعتبر التحضيرات المكتوبة بصياغة YAML طريقةً لتمثيل عيّنات البيانات على نحوٍ يفهمه البشر. يكون لهذا النوع التحضيرات الامتداد ‎.yml (كما في users.yml).
 
هذه عيّنة ملفّ YAML Fixture
 
<nowiki>#</nowiki> lo & behold! I am a YAML comment!


إليك عيّنة من ملفّ تحضيرات مكتوب بصياغة YAML:<syntaxhighlight lang="rails">
# lo & behold! I am a YAML comment!
david:
david:
 
  name: David Heinemeier Hansson
 name: David Heinemeier Hansson
  birthday: 1979-10-15
 
  profession: Systems development
 birthday: 1979-10-15
 
 profession: Systems development
 
steve:
steve:
  name: Steve Ross Kellock
  birthday: 1974-09-27
  profession: guy with keyboard
</syntaxhighlight>يُعطى لكلّ تحضير (Fixture) اسمًا متبوعًا بقائمة مزاحة للأزواج مفتاح/قيمة المفصولة عن بعضها بنقطتين رأسيّتين. تُفصل السجلّات عادة بسطر فارغ. بإمكانك وضع تعليقات في ملفّ التحضيرات (Fixture) باستخدام المحرف <code>#</code> كما في السطر الأوّل.


 name: Steve Ross Kellock
إذا كنت تستخدم [[Rails/association basics|الارتباطات]]، يمكنك تعريف عقدة مرجعيّة بين كلّ تحضيرين مختلفتين. إليك مثالًا بارتباط من النوع [[Rails/association basics#.D8.A7.D8.B1.D8.AA.D8.A8.D8.A7.D8.B7 .D8.A7.D9.84.D8.A7.D9.86.D8.AA.D9.85.D8.A7.D8.A1 .D9.88.D8.A7.D9.84.D8.AA.D8.B9.D8.AF.D8.AF.D9.8A.D8.A9 .28has and belongs to many.29|belongs_to/has_many]]:<syntaxhighlight lang="rails">
 
# In fixtures/categories.yml
 birthday: 1974-09-27
 
 profession: guy with keyboard
 
يُعطى لكلّ Fixture اسم متبوع بقائمة مزاحة لأزواج مفتاح\قيمة مفصولة بنقطتين رأسيّتين. تُفصل السجلّات عادة بسطر فارغ. بإمكانك وضع تعليقات في ملفّ Fixture باستخدام المحرف # في العمود الأوّل.
 
إذا كنت تستخدم الارتباطات، يمكنك تعريف عقدة مرجعيّة بين كلّ Fixtures مختلفتين. إليك مثالًا بارتباط belongs_to/has_many.
 
<nowiki>#</nowiki> In fixtures/categories.yml
 
about:
about:
 
  name: About
 name: About
 
# In fixtures/articles.yml
<nowiki>#</nowiki> In fixtures/articles.yml
 
first:
first:
  title: Welcome to Rails!
  body: Hello world!
  category: about
</syntaxhighlight>لاحظ أنّ قيمة المفتاح <code>category</code> للمقال <code>first</code> الموجود في fixtures/articles.yml هي <code>about</code>. يطلب هذا من ريلز تحميل الصنف <code>about</code> الموجود في fixtures/categories.yml.


 title: Welcome to ريلز!
'''ملاحظة''': لتمكين الارتباطات من الإشارة إلى بعضها البعض بالاسم، يمكنك استعمال اسم التحضير (Fixture) بدل تحديد الخاصية <code>id:</code>‎ في التحضيرات المرتبطة بها. يعيّين ريلز بشكلٍ آليّ مفتاحًا رئيسيًّا حتى يضمن التناسق بين جميع التنفيذات. لمزيد من المعلومات حول تصرّف الارتباطات هذا، يرجى الاطلاع على [http://api.rubyonrails.org/v5.2.2/classes/ActiveRecord/FixtureSet.html توثيق الواجهة البرمجية للتحضيرات].
 
 body: Hello world!
 
 category: about
 
لاحظ أنّ قيمة المفتاح category للمقال first الموجود في fixtures/articles.yml هي about. يطلب هذا من ريلز تحميل الفئة about الموجودة في fixtures/categories.yml.
 
ملاحظة: لتمكين الارتباطات من الإشارة إلى بعضها البعض بالاسم، يمكنك استعمال اسم الـ Fixture بدل تحديد الصّفة id:‎ في الـ Fixtures المرتبطة بها. يعيّين ريلز بشكلٍ آليّ مفتاًحًا رئسيًّا حتى يضمن التناسق بين جميع التنفيذات. لمزيد من المعلومات حول تصرّف الارتباطات هذا يرجى الاطلاع على  Fixtures API documentation.
 
=== ERB موجود ===
يسمح لك ERB بتضمين شيفرات روبي داخل القوالب، ويعمل Fixture YAML مسبقًا مع ERB عندما يحمّل ريلز Fixtures. ويسمح لك هذا باستخدام روبي لمساعدتك على إنشاء بعض البيانات التجريبيّة، فعلى سبيل المثال، تُنشئ الشيفرة البرمجيّة التاليّة آلاف المستخدمين:


==== وجود ERB ====
يسمح لك ERB بتضمين شيفرات روبي داخل القوالب، ويعمل التحضير المكتوب بصياغة YAML مسبقًا مع ERB عندما يحمّل ريلز التحضيرات. ويسمح لك هذا باستخدام روبي لمساعدتك على إنشاء بعض البيانات التجريبيّة؛ فعلى سبيل المثال، تُنشئ الشيفرة البرمجيّة التاليّة ألف مستخدم:<syntaxhighlight lang="html">
<% 1000.times do |n| %>
<% 1000.times do |n| %>
user_<%= n %>:
user_<%= n %>:
 
  username: <%= "user#{n}" %>
 username: <%= "user#{n}" %>
  email: <%= "user#{n}@example.com" %>
 
 email: <%= "user#{n}@example.com" %>
 
<% end %>
<% end %>
</syntaxhighlight>


=== Fixtures عند العمل ===
==== التحضيرات في موقع العمل ====
يحمّل ريلز تلقائيًا جميع Fixtures من مجلّد test/fixtures بشكل افتراضي، وتتكوّن عملية التحميل من ثلاثة خطوات:
يحمّل ريلز تلقائيًا جميع التحضيرات (Fixtures) من المجلّد test/fixtures بشكل افتراضي، وتتكوّن عملية التحميل من ثلاثة خطوات:
# إزالة أي بيانات موجودة من الجدول المقابل إلى Fixture.
# إزالة أي بيانات موجودة من الجدول المقابل للتحضير (Fixture).
# تحميل بيانات Fixture إلى الجدّول.
# تحميل بيانات التحضير إلى الجدّول.
# تفريغ بيانات Fixture إلى تابع في حالة كنت ترغب في الوصول إليه بشكل مباشر.
# تفريغ بيانات التحضير إلى تابع في حالة كنت ترغب في الوصول إليه بشكل مباشر.
ملاحظة: لإزالة البيانات الموجودة في قاعدة البيانات، يحاول ريلز تعطيل مشغلات التكامل المرجعي referential integrity triggers (مثل المفاتيح الخارجيّة وقيود التحقّق)، وإذا حصلت على أخطاء الأذونات في اختبارات التشغيل، فتأكد من امتلاك مستخدم قاعدة البيانات على امتياز تعطيل هذه المشغلات في بيئة الاختبار، (في PostgreSQL، يمكن للمستخدمين المميزين فقط تعطيل جميع المشغلات، اقرأ المزيد عن أذونات PostgreSQL هنا).
'''ملاحظة''': لإزالة البيانات الموجودة في قاعدة البيانات، يحاول ريلز تعطيل مشغلات السلامة المرجعية (referential integrity triggers، مثل المفاتيح الخارجيّة وقيود التحقّق)، وإذا حصلت على أخطاء الأذونات في اختبارات التشغيل، فتأكد من امتلاك مستخدم قاعدة البيانات على امتياز تعطيل هذه المشغلات في بيئة الاختبار. (في PostgreSQL، يمكن للمستخدمين المميزين فقط تعطيل جميع المشغلات، اقرأ المزيد عن أذونات PostgreSQL [http://blog.endpoint.com/2012/10/postgres-system-triggers-error.html هنا].)
 
=== Fixtures هي كائنات Active Record ===
إن Fixtures هي أمثلة لـ Active Record، وكما ذكرنا في النقطة الثالثة أعلاه، يمكنك الوصول إلى الكائن مباشرةً لأنه متوفّر تلقائيًا كتابع حيث أن نطاقه محلي هو حالة الاختبار، فمثلًا:
 
<nowiki>#</nowiki> this will return the User object for the fixture named david


==== التحضيرات هي كائنات Active Record ====
إن التحضيرات (Fixtures) هي أمثلة عن [[Rails/active record|Active Record]]؛ وكما ذكرنا في النقطة الثالثة أعلاه، يمكنك الوصول إلى الكائن مباشرةً لأنه متوفّر تلقائيًا كتابع حيث أن نطاقه محلي هو حالة الاختبار. فمثلًا:<syntaxhighlight lang="rails">
# david للتحضير الذي يدعى User هذا سيعيد الكائن
users(:david)
users(:david)
 
<nowiki>#</nowiki> this will return the property for david called id
# david للتحضير it هذا سيعيد الخاصية
 
users(:david).id
users(:david).id
 
<nowiki>#</nowiki> one can also access methods available on the User class
# عبر User يمكن الوصول إلى التوابع الموجودة في الصنف
 
david = users(:david)
david = users(:david)
david.call(david.partner)
david.call(david.partner)
 
</syntaxhighlight>للحصول على تحضيرات متعددة في وقت واحد، يمكنك تمرير قائمة من أسماء التحضيرات مثل:<syntaxhighlight lang="rails">
للحصول على Fixtures متعددة في وقت واحد، يمكنك تمرير قائمة من أسماء Fixtures، فمثلًا:
# david و steve هذا سيعيد مصفوفة تحوي التحضيرين
 
<nowiki>#</nowiki> this will return an array containing the fixtures david and steve
 
users(:david, :steve)
users(:david, :steve)
</syntaxhighlight>


= اختبار النموذج =
== اختبار النموذج ==
يُستخدم اختبار النماذج لاختبار نماذج مختلفة من تطبيقك الخاص.
يهدف اختبار النماذج لاختبار نماذج مختلفة من تطبيقك الخاص.


تُخزّن اختبارات نماذج ريلز في مجلّد test/models ويوفّر لك ريلز مولّد لإنشاء هيكل اختبار النموذج لك:
تُخزّن اختبارات نماذج ريلز في المجلّد test/models ويوفّر لك ريلز مولّدًا لإنشاء هيكل اختبار النموذج:<syntaxhighlight lang="shell">
 
$ bin/rails generate test_unit:model article title:string body:text
$ bin/ريلز generate test_unit:model article title:string body:text
create test/models/article_test.rb
 
create test/fixtures/articles.yml
create  test/models/article_test.rb
</syntaxhighlight>لا تملك اختبارات النموذج صنفًا فرعيًا (superclass) خاصًّا بها مثل <code>ActionMailer::TestCase</code>؛ وبدلًا من ذلك، ترث من <code>[http://api.rubyonrails.org/v5.2.2/classes/ActiveSupport/TestCase.html ActiveSupport::TestCase]</code>.
 
create  test/fixtures/articles.yml


لا تملك اختبارات النموذج على superclass مثل ActionMailer::TestCase وبدلًا من ذلك، يرثون من ActiveSupport::TestCase.
== اختبار النظام ==
 
تسمح لك اختبارات النظام (system tests) باختبار تفاعلات المستخدم مع تطبيقك، أو تشغيل الاختبارات في متصفح حقيقي أو في متصفّح بدون رأس ([[wikipedia:Headless_browser|headless browser]]، أي متصفح ويب ولكن بدون واجهة المستخدم الرسومية)؛ ومن الداخل، تستخدم اختبارات النظام Capybara.
= اختبار النظام =
يسمح لك نظام اختبارات النظام (System tests) باختبار تفاعلات المستخدم مع تطبيقك، أو تشغيل الاختبارات في متصفح حقيقي أو في متصفّح بدون رأس (headless browser)، ومن الداخل، يستخدم نظام الاختبارات Capybara.
 
لإنشاء اختبارات نظام ريلز، يمكنك استخدام مجلّد test/system في تطبيقك، وسيوفّر ريلز مولّد لإنشاء هيكل اختبار النظام لك.
 
$ bin/ريلز generate system_test users
 
  invoke test_unit
 
  create test/system/users_test.rb
 
وهذا ما سيبدو عليه نظام اختبار أُنشئ حديثًا:


لإنشاء اختبارات نظام ريلز، يمكنك استخدام المجلّد test/system في تطبيقك، وسيوفّر ريلز مولّدًا لإنشاء هيكل اختبار النظام:<syntaxhighlight lang="shell">
$ bin/rails generate system_test users
      invoke test_unit
      create test/system/users_test.rb
</syntaxhighlight>وهذا ما سيبدو عليه اختبار نظام أُنشئ حديثًا:<syntaxhighlight lang="rails">
require "application_system_test_case"
require "application_system_test_case"
 
class UsersTest < ApplicationSystemTestCase
class UsersTest < ApplicationSystemTestCase
 
  # test "visiting the index" do
 # test "visiting the index" do
  #   visit users_url
 
  #
 #  visit users_url
  #   assert_selector "h1", text: "Users"
 
  # end
 #
 
 #  assert_selector "h1", text: "Users"
 
 # end
 
end
end
</syntaxhighlight>تعمل اختبارات النظام بشكل افتراضي باستخدام المشغّل Selenium والمتصفح Chrome وشاشة بحجم 1400x1400، ويوضح القسم التالي كيفيّة تغيير الإعدادات الإفتراضيّة.


يعمل نظام الاختبارات بشكل افتراضي باستخدام المشغّل Selenium ومتصفح كروم وشاشة بحجم 1400x1400، ويوضح القسم التالي كيفيّة تغيير الإعدادات الإفتراضيّة.
=== تغيير الإعدادات الافتراضية ===
 
== تغيير الإعدادات الإفتراضيّة ==
يجعل ريلز عمليّة تغيير الإعدادات الافتراضيّة لاختبارات النظام بسيطة للغاية، فاستُخرجت جميع الإعدادات بعيدا حتى تتمكن من التركيز على كتابة الاختبارات الخاصة بك.
يجعل ريلز عمليّة تغيير الإعدادات الافتراضيّة لاختبارات النظام بسيطة للغاية، فاستُخرجت جميع الإعدادات بعيدا حتى تتمكن من التركيز على كتابة الاختبارات الخاصة بك.


يُنشئ ملف جديد باسم application_system_test_case.rb في مجلد test عند إنشاء تطبيق جديد أو scaffold، وهنا ستكون جميع إعدادات نظام الإختبارات موجودة.
يُنشئ ملف جديد باسم application_system_test_case.rb في المجلد test عند إنشاء تطبيق جديد أو توليد تطبيق عبر <code>scaffold</code>، وهنا ستكون جميع إعدادات نظام الاختبارات موجودة.
 
إذا رغبّت بتغيير الإعدادات الافتراضيّة، فيمكنك تغيير مشغّل نظام الاختبارات، فلنفترض أنك ترغب في تغيير برنامج التشغيل من Selenium إلى Poltergeist، فيجب عليك أولا إضافة poltergeist gem إلى Gemfile ومن ثم، عّدل على ملف application_system_test_case.rb كالآتي:


إذا رغبّت بتغيير الإعدادات الافتراضيّة، يمكنك تغيير مشغّل نظام الاختبارات؛ فلنفترض أنك ترغب في تغيير المشغل من Selenium إلى Poltergeist، يجب عليك أولًا إضافة الجوهرة <code>poltergeist</code> إلى Gemfile ومن ثم، عّدل على الملف application_system_test_case.rb بالشكل التالي:<syntaxhighlight lang="rails">
require "test_helper"
require "test_helper"
require "capybara/poltergeist"
require "capybara/poltergeist"
 
class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
 
  driven_by :poltergeist
 driven_by :poltergeist
end
 
</syntaxhighlight>اسم المشغل هو معامل إجباري من أجل <code>driven_by</code>، ويمكنك إضافة المعاملات الاختيارية التالية:
End
* ‎<code><nowiki>:</nowiki>using</code>: للمتصفح (يُستخدم فقط مع Selenium)، و
 
* ‎<code>:screen_size</code>: لتغيير حجم الشاشة للقطات الشاشة (Screenshots)، و
اسم برنامج التشغيل هو معامل إجباري لـ driven_by، ويمكنك إضافة المعاملات الاختيارية التالية:
* <code>‎:options</code>: لتحديد وضبط الخيارات المدعومة من قبل المشغّل.
 
<syntaxhighlight lang="rails">
<nowiki>:</nowiki>using للمتصفح (يُستخدم فقط مع Selenium)، :screen_size) لتغيير حجم الشاشة للقطات الشاشة (Screenshots)، و :options لوضع الخيارات المدعومة من قبل المشغّل.
 
require "test_helper"
require "test_helper"
 
class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
  driven_by :selenium, using: :firefox
end


 driven_by :selenium, using: :firefox
</syntaxhighlight>إذا أردت استخدام متصفح بلا رأس (headless browser)، فيمكنك استخدام متصفح Chrome بلا رأس أو متصفح Firefox بلا رأس وذلك عن طريق إضافة <code>headless_chrome</code> أو <code>headless_firefox</code> في المعامل <code>:using</code>:<syntaxhighlight lang="rails">
 
End
 
إذا أردت استخدام متصفح بلا رأس (headless browser)، فيمكنك استخدام Headless Chrome أو Headless Firefox وذلك عن طريق إضافة headless_chrome أو headless_firefox في معامل :using
 
require "test_helper"
require "test_helper"
 
class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
  driven_by :selenium, using: :headless_chrome
end


 driven_by :selenium, using: :headless_chrome
</syntaxhighlight>إذا كانت إعدادات Capybara تتطلّب ضبطًا أكثر مما توفره ريلز، فيجب إضافة هذه الإعدادات الإضافيّة إلى الملف application_system_test_case.rb.


End
يرجى الإطلاع على [https://github.com/teamcapybara/capybara#setup توثيق Capybara] للاطلاع على الإعدادات الإضافيّة.


إذا كانت إعدادات Capybara تتطلّب إعدادًا أكثر مما توفره ريلز، فيجب إضافة هذه الإعدادات الإضافيّة إلى ملف application_system_test_case.rb.
=== مساعد إلتقاط الشاشة ===
إنَّ <code>ScreenshotHelper</code> هو مساعد مصمم لالتقاط لقطات من الاختبارات الخاصة بك، وقد يكون ذلك مفيدًا في عرض المتصفح عند فشل الاختبار، أو لمشاهدة لقطات الشاشة لاحقًا لتصحيح الأخطاء.


يرجى الإطلاع على وثائق Capybara للحصول على إعدادات إضافيّة.
هنالك تابعين هما: <code>take_screenshot</code> و <code>take_failed_screenshot</code>، وهذا الأخير موجود تلقائيًا في <code>after_teardown</code> داخل ريلز.


== مساعد لقطة الشاشة ==
يمكنك تضمين تابع المساعد <code>take_screenshot</code> في أي مكان في اختباراتك لالتقاط لقطة شاشة للمتصفح.
ScreenshotHelper هو مساعد مصمم لالتقاط لقطات من الاختبارات الخاصة بك، وقد يكون ذلك مفيدًا في عرض المتصفح عند فشل الإختبار، أو لمشاهدة لقطات الشاشة لاحقًا لتصحيح الأخطاء.


هنالك تابعين: take_screenshot وtake_failed_screenshot، وهذا الأخير موجود تلقائيًا في after_teardown داخل ريلز.
=== تنفيذ اختبار النظام ===
 
سنضيف الآن نظام اختبار إلى تطبيق المدونة الخاص بنا، إذ سنعرض لك كيفية كتابة نظام اختبار عن طريق زيارة الصفحة الرئيسية (index) وإنشاء مقالة مدونة جديدة.
يمكنك تضمين تابع المساعد take_screenshot في أي مكان في اختباراتك لإلتقاط لقطة شاشة للمتصفح.
 
== تنفيذ اختبار النظام ==
سنضيف الآن نظام اختبار إلى تطبيق المدونة الخاص بنا، سنعرض لك كيفية كتابة نظام اختبار عن طريق زيارة صفحة الفهرس (index) وإنشاء مقالة مدونة جديدة.
 
إذا استخدمت مولّد scaffold، فسيُنشئ هيكل اختبار للنظام تلقائيًا لك، وإذا لم تستخدم مولد scaffold، فابدأ بإنشاء هيكل اختبار للنظام.
 
$ bin/ريلز generate system_test articles
 
سيُنشئ لنا هذا ملف اختبار جاهز، وستكون هذه مخرجات الأمر السابق::
 
invoke  test_unit
 
create test/system/articles_test.rb
 
دعونا الآن نفتح هذا الملف ونكتب تأكيدنا (assertion) الأول:


إذا استخدمت المولّد <code>scaffold</code>، فسيُنشئ لك هيكل اختبار للنظام تلقائيًا؛ وإذا لم تستخدمه، فابدأ بإنشاء هيكل اختبار للنظام عبر الأمر التالي:<syntaxhighlight lang="shell">
$ bin/rails generate system_test articles
</syntaxhighlight>سيُنشئ لنا هذا ملف اختبار جاهز، وستكون هذه مخرجات الأمر السابق:<syntaxhighlight lang="text">
invoke  test_unit
create    test/system/articles_test.rb
</syntaxhighlight>دعونا الآن نفتح هذا الملف ونكتب اختبارنا (assertion) الأول:<syntaxhighlight lang="shell">
require "application_system_test_case"
require "application_system_test_case"
 
class ArticlesTest < ApplicationSystemTestCase
class ArticlesTest < ApplicationSystemTestCase
  test "viewing the index" do
    visit articles_path
    assert_selector "h1", text: "Articles"
  end
end
</syntaxhighlight>يجب أن يبحث الاختبار على العنوان <code>h1</code> الموجود في صفحة المقالات الرئيسية ويمرره.


 test "viewing the index" do
شغّل الآن اختبارات النظام:<syntaxhighlight lang="shell">
 
bin/rails test:system
visit articles_path
</syntaxhighlight>'''ملاحظة''': بشكل افتراضي، لن يشغّل الأمر <code>bin/rails test</code> اختبارات النظام الخاصة بك، لذا تأكد من تنفيذ <code>bin/rails test:system</code> لتشغيلها.
 
assert_selector "h1", text: "Articles"
 
 end
 
End
 
يجب أن يبحث الاختبار على h1 موجود في صفحة فهرس المقالات ويمر.
 
شغّل نظام الاختبارات.
 
bin/ريلز test:system
 
ملاحظة: بشكل افتراضي، لن يشغّل bin/ريلز test نظام الاختبارات الخاصة بك، فتأكد من تشغيل bin/ريلز test:system لتشغيلهم.
 
=== إنشاء اختبار نظام المقالات ===
دعونا الآن نختبر التدفق لإنشاء مقال جديد في مدونتنا.


==== إنشاء اختبار نظام للمقالات ====
دعونا الآن نختبر سير عملية إنشاء مقال جديد في مدونتنا:<syntaxhighlight lang="rails">
test "creating an article" do
test "creating an article" do
  visit articles_path
  click_on "New Article"
  fill_in "Title", with: "Creating an Article"
  fill_in "Body", with: "Created this article successfully!"
  click_on "Create Article"
  assert_text "Creating an Article"
end
</syntaxhighlight>إن الخطوة الأولى هي استدعاء <code>articles_path</code>، فسينّقل هذا الاختبار إلى صفحة المقالات الرئيسية.


 visit articles_path
بعد ذلك، سيجد <code>click_on "New Article"‎</code> الزر "New Article" في صفحة الرئيسية. وسيؤدي هذا إلى إعادة توجيه المتصفح إلى /articles/new.
 
 click_on "New Article"
 
 fill_in "Title", with: "Creating an Article"
 
 fill_in "Body", with: "Created this article successfully!"
 
 click_on "Create Article"
 
 assert_text "Creating an Article"
 
End
 
إن الخطوة الأولى هي استدعاء articles_path، فسينّقل هذا الاختبار إلى صفحة فهرسة المقالات.
 
بعد ذلك، سيجد click_on "New Article" زر "New Article" في صفحة الفهرس. وسيؤدي هذا إلى إعادة توجيه المتصفح إلى /articles/new.
 
بعد ذلك سيعبأ الاختبار عنوان ونص المقال بالنص المحدد، وبمجرّد ملء الحقول، سيُنقر على  "Create Article" حيث سيُرسل طلب POST لإنشاء مقال جديد في قاعدة البيانات.
 
سيُعاد توجيهنا إلى صفحة فهرس المقالات وهنالك سنتأكد من أن النص من عنوان المقال الجديد موجود في صفحة فهرس المقالات.


=== الغوص أكثر ===
بعد ذلك سيكتب الاختبار عنوانًا ونصًا للمقال في المكان المحدد، وسينقر، بمجرّد ملء الحقول، على  "Create Article" لسيُرسَل طلب POST لإنشاء مقال جديد في قاعدة البيانات.
يكمن جمال اختبار النظام في أنه يشبه اختبار التكامل من حيث أنه يختبر تفاعل المستخدم مع المتحكم، النموذج، والعرض، ولكن اختبار النظام أكثر قوّة منه ويختبر التطبيق فعليًَا كما لو كان المستخدم الحقيقي يستخدمه، ويمكنك فعل أكثر من ذلك، فيمكنك اختبار ما يكمن للمستخدم نفسه فعله عند استخدام تطبيقك مثل التعليق، حذف المقالات، نشر المقالات، إلخ.


= اختبار التكامل =
سيُعاد توجيهنا إلى صفحة فهرس المقالات (الصفحة الرئيسية) وهنالك سنتأكد من أن المحتوى النصي لعنوان المقال الجديد موجود في صفحة فهرس المقالات.
تُستخدم اختبارات التكامل لاختبار كيّفيّة تفاعل أجزاء مختلفة من تطبيقك، وتُستخدم بشكل عام لاختبار سير العمل المهم داخل تطبيقنا.


لإنشاء اختبارات التكامل في ريلز، نستخدم مجلّد test/integration لتطبيقنا، ويوفّر ريلز مولد لإنشاء هيكل اختبار تكامل لنا.
==== الغوص أكثر ====
يكمن جمال اختبار النظام في أنه يشبه اختبار التكامل (integration testing) من حيث أنه يختبر تفاعل المستخدم مع المتحكم، والنموذج، والعرض، ولكن اختبار النظام أكثر قوّة منه ويختبر التطبيق فعليًَا كما لو كان المستخدم الحقيقي يستخدمه؛ ويمكِّنك فعل أكثر من ذلك، إذ يمكنك من اختبار ما يمكن للمستخدم نفسه فعله عند استخدام تطبيقك مثل التعليق، حذف المقالات، نشر المقالات، ...إلخ.


$ bin/ريلز generate integration_test user_flows
== اختبار التكامل ==
 
تُستخدم اختبارات التكامل (Integration tests) لاختبار كيّفيّة تفاعل أجزاء مختلفة من تطبيقك، وتُستخدم بشكل عام لاختبار سير العمل المهم داخل التطبيق.
  exists  test/integration/
 
  create  test/integration/user_flows_test.rb
 
هذا ما سيبدو عليه اختبار تكامل منشئ حديثًا:


لإنشاء اختبارات التكامل في ريلز، نستخدم المجلّد test/integration لتطبيقنا، ويوفّر ريلز مولدًا لإنشاء هيكل اختبار تكامل لنا.<syntaxhighlight lang="shell">
$ bin/rails generate integration_test user_flows
      exists  test/integration/
      create  test/integration/user_flows_test.rb
</syntaxhighlight>هذا ما سيبدو عليه اختبار تكامل أنشئ حديثًا:<syntaxhighlight lang="rails">
require 'test_helper'
require 'test_helper'
 
class UserFlowsTest < ActionDispatch::IntegrationTest
class UserFlowsTest < ActionDispatch::IntegrationTest
  # test "the truth" do
  #  assert true
  # end
end
</syntaxhighlight>يرث الاختبار من <code>ActionDispatch::IntegrationTest</code>، ويتيح هذا لنا بعض المساعدين الإضافيين لاستخدامهم في اختبارات التكامل الخاصة بنا.


 # test "the truth" do
=== المساعدين المتاحين لاختبارات التكامل ===
 
بالإضافة إلى مساعدي الاختبارات القياسيّة، تأتي الوراثة من <code>ActionDispatch::IntegrationTest</code> مع بعض المساعدين الإضافيين المتوفرين عند كتابة اختبارات التكامل، دعونا نقدم لك باختصار الفئات الثلاثة التي يجب أن نختار منهم:
 #   assert true
# للتعامل مع مشغل (runner) اختبار التكامل، راجع <code>[http://api.rubyonrails.org/v5.2.2/classes/ActionDispatch/Integration/Runner.html ActionDispatch::Integration::Runner]</code>.
 
# عند تنفيذ الطلبات، سيتاح لنا <code>[http://api.rubyonrails.org/v5.2.2/classes/ActionDispatch/Integration/RequestHelpers.html ActionDispatch::Integration::RequestHelpers]</code> للاستخدام.
 # end
# إذا أردت تعديل الجلسة، أو حالة اختبار التكامل، فألقي نظّرة على <code>[http://api.rubyonrails.org/v5.2.2/classes/ActionDispatch/Integration/Session.html ActionDispatch::Integration::Session]</code> لمساعدتك.
 
End
 
يُورث الاختبار من ActionDispatch::IntegrationTest، ويتيح هذا لنا بعض المساعدين الإضافيين لاستخدامهم في اختبارات التكامل الخاصة بنا.
 
== المساعدين المتاحين لاختبارات التكامل ==
بالإضافة إلى مساعدي الاختبارات القياسيّة، تأتي الوراثة من ActionDispatch::IntegrationTest مع بعض المساعدين الإضافيين المتوفرين عند كتابة اختبارات التكامل، دعونا نقدم لك باختصار الفئات الثلاثة التي يجب أن نختار منهم:
 
للتعامل مع عدّاء اختبار التكامل، راجع ActionDispatch::Integration::Runner.
 
عند تنفيذ الطلبات، سيتاح لنا ActionDispatch::Integration::RequestHelpers للاستخدام.
 
إذا أردت تعديل الجلسة، أو حالة اختبار التكامل، فألقي نظّرة على ActionDispatch::Integration::Session لمساعدتك.


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


سنبدأ بتوليد هيكل اختبار تكاملنا:
سنبدأ بتوليد هيكل اختبار تكامل خاص بنا:<syntaxhighlight lang="shell">
 
$ bin/rails generate integration_test blog_flow
$ bin/ريلز generate integration_test blog_flow
</syntaxhighlight>سينشئ لنا هذا ملف الاختبار، وسيخّرج لنا الأمر السابق ما يلي:<syntaxhighlight lang="text">
 
invoke test_unit
سينشئ لنا هذا ملف الاختبار، وسيخّرج لنا الأمر السابق ما يلي:
create   test/integration/blog_flow_test.rb
 
</syntaxhighlight>الآن، دعونا نفتح هذا الملف ونكتب اختبارنا (assertion) الأول:<syntaxhighlight lang="rails">
invoke  test_unit
 
create test/integration/blog_flow_test.rb
 
الآن، دعونا نفتح هذا الملف ونكتب تأكيدنا الأول:
 
require 'test_helper'
require 'test_helper'
 
class BlogFlowTest < ActionDispatch::IntegrationTest
class BlogFlowTest < ActionDispatch::IntegrationTest
 
  test "can see the welcome page" do
 test "can see the welcome page" do
    get "/"
 
    assert_select "h1", "Welcome#index"
   get "/"
  end
 
   assert_select "h1", "Welcome#index"
 
 end
 
end
end
</syntaxhighlight>سنلقي نظّرة على <code>assert_select</code> للاستعلام عن شيفرة [[HTML]] الناتجة عن طلب في قسم "Testing Views" الموجود في الأسفل؛ ويُستخدم لاختبار استجابة طلبنا من خلال التأكيد على وجود عناصر HTML الأساسيّة ومحتوياتها.


سنلقي نظّرة على assert_select للاستعلام عن HTML الناتج عن طلب في قسم "Testing Views" الموجود في الأسفل، ويُستخدم لاختبار استجابة طلبنا من خلال التأكيد على وجود عناصر HTML الأساسيّة ومحتوياتها.
عندما نزور مسارنا الجذر، ينبغي أن نرى الملف welcome/index.html.erb مصيَّرًا للعرض، ولذلك يجب أن يتحقق هذا التأكيد.
 
عندما نزور مسارنا الجذر، ينبغي أن نرى welcome/index.html.erb مصدّر للعرض، ولذلك يجب أن يمر هذا التأكيد.
 
=== إنشاء تكاملات المقالات ===
ما رأيكم أن نختبر قدرتنا على إنشاء مقالة جديدة في مدونتنا والإطلاع على المقالة الناتجة.


==== إنشاء تكاملات المقالات ====
ما رأيك أن نختبر قدرتنا على إنشاء مقالة جديدة في مدونتنا والإطلاع على المقالة الناتجة؟ إليك الشيفرة التالية:<syntaxhighlight lang="rails">
test "can create an article" do
test "can create an article" do
  get "/articles/new"
  assert_response :success
  post "/articles",
    params: { article: { title: "can create", body: "article successfully." } }
  assert_response :redirect
  follow_redirect!
  assert_response :success
  assert_select "p", "Title:\n  can create"
end
</syntaxhighlight>دعونا نقسّم هذا الاختبار حتى نفهمه.


 get "/articles/new"
نبدأ باستدعاء الإجراء <code>:new</code> على المتحكم <code>Articles</code>، ويجب أن تكون نتيجة هذه الاستجابة ناجحة.
 
 assert_response :success
 
 post "/articles",
 
params: { article: { title: "can create", body: "article successfully." } }
 
 assert_response :redirect
 
 follow_redirect!
 
 assert_response :success
 
 assert_select "p", "Title:\n  can create"
 
End
 
دعونا نقسّم هذا الاختبار حتى نفهمه.
 
نبدأ باستدعاء الإجراء :new على متحكم Articles، ويجب أن تكون نتيجة هذه الاستجابة ناجحة.
 
بعد ذلك، نرسل طلبًا للنشر (post) إلى الإحراء :create لمتحكم Articles:


بعد ذلك، نرسل طلبًا للنشر (post) إلى الإجراء ‎<code>:create</code> للمتحكم <code>Articles</code>:<syntaxhighlight lang="rails">
post "/articles",
post "/articles",
 
  params: { article: { title: "can create", body: "article successfully." } }
 params: { article: { title: "can create", body: "article successfully." } }
 
assert_response :redirect
assert_response :redirect
follow_redirect!
follow_redirect!
</syntaxhighlight>السطران التاليان للطلب هما للتعامل مع عملية إعادة التوجيه التي قمنا بإعدادها عند إنشاء مقالة جديدة.


السطران التاليان للطلب هما للتعامل مع عملية إعادة التوجيه التي قمنا بإعدادها عند إنشاء مقالة جديدة.
'''ملاحظة''': لا تنس أن تستدعي <code>follow_redirect!‎</code> إذا كنت تخطط لتقديم طلبات لاحقة بعد إجراء إعادة التوجيه.
 
ملاحظة: لا تنس أن تستدعي follow_redirect! إذا كنت تخطط لتقديم طلبات لاحقة بعد إجراء إعادة التوجيه.


وأخيرًا يمكننا التأكيد على أن ردنا كان ناجحًا وأن مقالتنا الجديدة قابلة للقراءة على الصفحة.
وأخيرًا، يمكننا التأكيد على أن ردنا كان ناجحًا وأن مقالتنا الجديدة قابلة للقراءة على الصفحة.


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


= الاختبارات الوظيفيّة لوحدات التحكم الخاصة بنا =
== الاختبارات الوظيفية لوحدات التحكم الخاصة بنا ==
في ريلز، اختبار الإجراءات المختلفة للمتحكم هو شكل من أشكال كتابة الاختبارات الوظيفيّة، تذكر أن وحدات التحكم الخاصة بك تتعامل مع طلبات الويب الواردة إلى التطبيق الخاص بك وتستجيب في النهاية مع عرض مصدّر، وعند كتابة الاختبارات الوظيفيّة، فإنك تختبر كيف تتعامل الإجراءات مع الطلبات والنتائج المتوقعة أو الاستجابة وفي بعض الحالات عرض HTML.
في ريلز، اختبار الإجراءات المختلفة للمتحكم هو شكل من أشكال كتابة الاختبارات الوظيفيّة؛ تذكر أن وحدات التحكم الخاصة بك تتعامل مع طلبات الويب الواردة إلى التطبيق الخاص بك وتستجيب في النهاية مع عرض مصيَّر (rendered view). وعند كتابة الاختبارات الوظيفيّة، فإنك تختبر كيف تتعامل الإجراءات مع الطلبات والنتائج المتوقعة أو الاستجابة وفي بعض الحالات عرض شيفرة [[HTML]].


== ما يجب تضمينه في اختباراتك الوظيفيّة ==
=== ما يجب تضمينه في اختباراتك الوظيفية ===
يجب عليك اختبار أشياء مثل:
يجب عليك اختبار أشياء مثل:
* هل كان طلب الويب ناجحًا؟
* هل كان طلب الويب ناجحًا؟
سطر 807: سطر 672:
* هل كان الكائن الصحيح مخزّن في قالب الاستجابة؟
* هل كان الكائن الصحيح مخزّن في قالب الاستجابة؟
* هل كانت الرسالة المناسبة معروضة للمستخدم في العرض؟
* هل كانت الرسالة المناسبة معروضة للمستخدم في العرض؟
أسهل طريقة لرؤية الاختبارات الوظيفيّة في العمل هي إنشاء وحدة تحكم باستخدام مولّد السقالة (scaffold generator):
أسهل طريقة لرؤية الاختبارات الوظيفيّة في العمل هي إنشاء وحدة تحكم باستخدام المولّد <code>scaffold</code>:<syntaxhighlight lang="shell">
 
$ bin/rails generate scaffold_controller article title:string body:text
$ bin/ريلز generate scaffold_controller article title:string body:text
...
 
create  app/controllers/articles_controller.rb
...
invoke  test_unit
create    test/controllers/articles_controller_test.rb
...
...
</syntaxhighlight>سيُنشئ هذا الشيفرة البرمجيّة للمتحكم والاختبارات للمورد <code>Article</code>؛ يمكنك إلقاء نظرة  على الملف articles_controller_test.rb في المجلد test/controllers.


create  app/controllers/articles_controller.rb
إذا كان لديك متحكمًا بالفعل وترغب فقط في إنشاء شيفرة اختبار <code>scaffold</code> لكل إجراء من إجراءات السبعة الافتراضيّة، فيمكنك استخدام الأمر التالي:<syntaxhighlight lang="shell">
 
$ bin/rails generate test_unit:scaffold article
...
...
 
invoke test_unit
invoke  test_unit
create   test/controllers/articles_controller_test.rb
 
create test/controllers/articles_controller_test.rb
 
 
سيُنشء هذا الشيفرة البرمجيّة للمتحكم والاختبارات لمورد Article، يمكنك إلقاء نظرة  على ملف articles_controller_test.rb في مجلد test/controllers.
 
إذا كان لديك متحكم بالفعل وترغب فقط في إنشاء شيفرة برمجيّة لسقالة الاختبار لكل إجراء من إجراءات السبعة الافتراضيّة، فيمكنك استخدام الأمر التالي:
 
$ bin/ريلز generate test_unit:scaffold article
 
...
...
 
</syntaxhighlight>لنقِ نظرة على اختبار واحد من هذا القبيل وهو <code>test_should_get_index</code> من الملف articles_controller_test.rb:<syntaxhighlight lang="rails">
invoke  test_unit
# articles_controller_test.rb
 
create test/controllers/articles_controller_test.rb
 
 
لنقِ نظرة على اختبار واحد من هذا القبيل وهو test_should_get_index من ملف articles_controller_test.rb.
 
<nowiki>#</nowiki> articles_controller_test.rb
 
class ArticlesControllerTest < ActionDispatch::IntegrationTest
class ArticlesControllerTest < ActionDispatch::IntegrationTest
  test "should get index" do
    get articles_url
    assert_response :success
  end
end
</syntaxhighlight>في الاختبار <code>test_should_get_index</code>، يحاكي ريلز طلبًا على الإجراء <code>index</code> مع التأكد من نجاح الطلب وضمان إنشاء جسم (body) الاستجابة الصحيح أيضًا.


 test "should get index" do
يحصل التابع <code>get</code> على طلب الويب ويعبئ النتائج في ‎<code>@response</code>، ويقبل هذا 6 معاملات:
 
* عنوان URI: لإجراء المتحكم الذي تطلبه، ويمكن أن يكون هذا على شكل سلسلة نصيّة أو مساعد مسار توجيه (على سبيل المثال <code>articles_url</code>).
get articles_url
* <code>Params</code>: خيار مع [[Ruby/Hash|جدول hash]] من معاملات الطلب لتمريرها إلى الإجراء (على سبيل المثال معاملات سلسلة النصية للطلب أو متغيرات المقال).
 
* <code>headers</code>: لتعيين الرؤوس التي ستمرّر مع الطلب.
assert_response :success
* <code>env</code>: لتخصيص بيئة الطلب حسب الحاجة.
 
* <code>xhr</code>: مهما كان الطلب هو طلب Ajax أو لا، فيمكنك تعيين هذا الخيار إلى القيمة <code>true</code> لجعل الطلب هو طلب Ajax.
 end
* <code>as</code>: لتشفير الطلب بنوع محتوى مختلف، وهو يدعم ‎<code>:json</code> بشكل افتراضي.
 
جميع معاملات الكلمات الرئيسيّة هذه اختياريّة.
End
 
في اختبار test_should_get_index، يحاكي ريلز طلبًا على إجراء index مع التأكد من نجاح الطلب وضمان إنشاء جسم (body) الاستجابة الصحيح أيضًا.
 
يحصل تابع get على طلب الويب ويعبئ النتائج في @response، ويقبل هذا 6 معاملات:
* عنوان URI لإجراء المتحكم الذي تطلبه، ويمكن أن يكون هذا على شكل سلسلة نصيّة أو مساعد مسار (على سبيل المثال articles_url).
* Params: خيار مع hash لمعاملات الطلب لتمريرها إلى الإجراء (على سبيل المثال معاملات سلسلة النصية للطلب أو متغيرات المقال).
* Headers: لتعيين الرؤوس التي ستمرّر مع الطلب.
* Env: لتخصيص بيئة الطلب حسب الحاجة.
* Xhr: مهما كان الطلب هو طلب Ajax أو لا، فيمكنك وضع هذه true لجعل الطلب Ajax.
* As: لتشفير الطلب بنوع محتوى مختلف، وهو يدعم :json بشكل افتراضي.
جميع معاملات الكلمات الرئيسيّة هذه  اختياريّة.
 
مثال: استدعاء الإجراء :show للمقال الأول، وتمرير رأس HTTP_REFERER:
 
get article_url(Article.first), headers: { "HTTP_REFERER" => "<nowiki>http://example.com/home</nowiki>" }
 
مثال آخر: استدعاء الإجراء :update للمقال الأخير، وتمرير نص جديد للعنوان في params، وسنجعله طلب Ajax:


مثال: استدعاء الإجراء ‎<code>:show</code> للمقال الأول، وتمرير الترويسة <code>HTTP_REFERER</code>:<syntaxhighlight lang="shell">
get article_url(Article.first), headers: { "HTTP_REFERER" => "http://example.com/home" }
</syntaxhighlight>مثال آخر: استدعاء الإجراء ‎<code>:update</code> للمقال الأخير، وتمرير نص جديد للعنوان في <code>params</code>، وسنجعله طلب Ajax:<syntaxhighlight lang="shell">
patch article_url(Article.last), params: { article: { title: "updated" } }, xhr: true
patch article_url(Article.last), params: { article: { title: "updated" } }, xhr: true
</syntaxhighlight>'''ملاحظة''': إذا حاولت تشغيل اختبار <code>test_should_create_article</code> من articles_controller_test.rb، فستفشل هذه بسبب التحقق من مستوى النموذج الذي أضيف حديثًا.


ملاحظة: إذا حاولت تشغيل اختبار test_should_create_article من articles_controller_test.rb فستفشل هذه بسبب التحقق من مستوى النموذج الذي أضيف حديثًا.
لنعدّل اختبار test_should_create_article في articles_controller_test.rb لتتحقق جميع اختباراتنا:<syntaxhighlight lang="rails">
 
لنعدّل اختبار test_should_create_article في articles_controller_test.rb لتمر جميع اختباراتنا:
 
test "should create article" do
test "should create article" do
  assert_difference('Article.count') do
    post articles_url, params: { article: { body: 'Rails is awesome!', title: 'Hello Rails' } }
  end
  assert_redirected_to article_path(Article.last)
end
</syntaxhighlight>الآن، يمكنك محاولة تشغيل جميع الاختبارات وستنجح كلها.


 assert_difference('Article.count') do
'''ملاحظة''': إذا تابعت الخطوات الموجودة في قسم "المصادقة الأساسيّة"، فستحتاج إلى إضافة ما يلي إلى كتلة الإعداد للحصول على جميع الاختبارات التي ستتحقق:<syntaxhighlight lang="rails">
 
post articles_url, params: { article: { body: 'ريلز is awesome!', title: 'Hello ريلز' } }
 
 end
 
 assert_redirected_to article_path(Article.last)
 
End
 
الآن، يمكنك محاولة تشغيل جميع الاختبارات وستمر.
 
إذا تابعت الخطوات الموجودة في قسم "المصادقة الأساسييّة" فستحتاج إلى إضافة ما يلي إلى كتلة الإعداد للحصول على جميع الاختبارات التي تمر:
 
request.headers['Authorization'] = ActionController::HttpAuthentication::Basic.
request.headers['Authorization'] = ActionController::HttpAuthentication::Basic.
  encode_credentials('dhh', 'secret')
</syntaxhighlight>


 encode_credentials('dhh', 'secret')
=== أنواع الطلبات المتاحة للاختبارات الوظيفية ===
إذا كنت معتادًا على بروتوكول HTTP، فستعرف أن <code>get</code> هو نوع من أنواع الطلبات، وهنالك 6 أنواع طلبات مدعومة في اختبارات ريلز الوظيفيّة هي:
* <code>get</code>
* <code>post</code>
* <code>patch</code>
* <code>put</code>
* <code>head</code>
* <code>delete</code>
تحتوي جميع أنواع الطلبات على توابع مكافئة يمكنك استخدامها. وستستعمل get و post و put و delete كثيرًا في تطبيق C.R.U.D العادي.


== أنواع الطلبات المتاحة للاختبارات الوظيفيّة ==
'''ملاحظة''': لا تتحقّق الاختبارات الوظيفيّة مما إذا كان نوع الطلب المحدد مقبولًا من قبل الإجراء أو لا، فنحن مهتمون أكثر بالنتيجة؛ توجد اختبارات طلب لهذه الحالة لجعل اختباراتك أكثر إفادة.
إذا كنت معتادًا على بروتوكول HTTP، فستعرف أن get هو نوع من أنواع الطلبات، وهنالك 6 أنواع طلبات مدعومة في اختبارات ريلز الوظيفيّة:
* get
* post
* patch
* put
* head
* delete
تحتوي جميع أنواع الطلبات على توابع معادلة يمكنك استخدامها، وستستعمل في تطبيق C.R.U.D العادي get و post و put و delete كثيرًا.
 
ملاحظة: لا تتحقّق الاختبارات الوظيفيّة ما إذا كان نوع الطلب المحدد مقبولًا من قبل الإجراء أو لا، فنحن مهتمون أكثر بالنتيجة، توجد اختبارات طلب لهذه الحالة لجعل اختباراتك أكثر إفادة.
 
== اختبارات طلبات (XHR (AJAX) ==
لإختبار طلبات AJAX، يمكنك تحديد خيار xhr: true لتوابع get، post، patch، put وdelete، فعلى سبيل المثال:


== اختبارات طلبات AJAX) XHR)  ==
لاختبار طلبات AJAX، يمكنك تحديد الخيار <code>xhr: true</code> للتوابع <code>get</code>، و <code>post</code>، و <code>patch</code>، و <code>put</code>، و <code>delete</code>. فعلى سبيل المثال:<syntaxhighlight lang="rails">
test "ajax request" do
test "ajax request" do
 
  article = articles(:one)
 article = articles(:one)
  get article_url(article), xhr: true
 
 get article_url(article), xhr: true
  assert_equal 'hello world', @response.body
 
  assert_equal "text/javascript", @response.content_type
 assert_equal 'hello world', @response.body
 
 assert_equal "text/javascript", @response.content_type
 
end
end
</syntaxhighlight>


== ثلاثة أجزاء من Apocalypse ==
=== ثلاثة جداول Hash ===
بعد إجراء الطلب ومعالجته، سيكون لدينا 3 كائنات تجزئة جاهزة للاستخدام:
بعد إجراء الطلب ومعالجته، سيكون لدينا ثلاثة كائنات من <code>[[Ruby/Hash|Hash]]</code> جاهزة للاستخدام هي:
* Cookies - لملفات تعريف الارتباط التي عيّناها.
* <code>cookies</code>: لملفات تعريف الارتباط التي عيّناها.
* Flash - لأي كائنات موجودة في flash.
* <code>flash</code>: لأي كائنات موجودة في flash.
* Session - لأي كائنات موجودة في متغيرات الجلسة.
* <code>session</code>: لأي كائنات موجودة في متغيرات الجلسة.
كما هو الحال مع كائنات التجزئة العاديّة، يمكنك الوصول إلى القيم عن طريق الإشارة للمفاتيح عن طريق السلسلة النصيّة، ويمكنك أيضًا الإشارة إليبهم عن طريق اسم الرمز، فمثلًا:
كما هو الحال مع الكائنات <code>[[Ruby/Hash|Hash]]</code> العاديّة، يمكنك الوصول إلى القيم عن طريق الإشارة للمفاتيح عبر السلسلة النصيّة، ويمكنك أيضًا الإشارة إليها عن طريق اسم الرمز. إليك الشيفرة التالية مثلًا:<syntaxhighlight lang="rails">
flash["gordon"]              flash[:gordon]
session["shmession"]          session[:shmession]
cookies["are_good_for_u"]    cookies[:are_good_for_u]
</syntaxhighlight>


flash["gordon"]            flash[:gordon]
=== متغيرات النسخة المتاحة ===
 
يمكنك أيضًا الوصول إلى ثلاثة متغيّرات نسخة في اختباراتك الوظيفيّة بعد إجراء الطلب هي:
session["shmession"]       session[:shmession]
* <code>‎@controller</code>: المتحكم المعالج للطلب.
 
* <code>‎@request</code>: كائن الطلب.
cookies["are_good_for_u"] cookies[:are_good_for_u]
* <code>‎@response</code>: كائن الاستجابة.<syntaxhighlight lang="rails">
 
== متغيرات المثيلات المتاحة ==
يمكنك أيضًا الوصول إلى ثلاثة متغيّرات مثيل في اختباراتك الوظيفيّة، بعد إجراء الطلب:
* @controller - يعالج المتحكم الطلب.
* @request - كائن الطلب.
* @response - كائن الاستجابة.
class ArticlesControllerTest < ActionDispatch::IntegrationTest
class ArticlesControllerTest < ActionDispatch::IntegrationTest
  test "should get index" do
    get articles_url
    assert_equal "index", @controller.action_name
    assert_equal "application/x-www-form-urlencoded", @request.media_type
    assert_match "Articles", @response.body
  end
end
</syntaxhighlight>
=== إعداد الترويسات ومتغيرات CGI ===
يمكن تمرير [https://tools.ietf.org/search/rfc2616#section-5.3 ترويسات HTTP] و[https://tools.ietf.org/search/rfc3875#section-4.1 متغيرات CGI] كترويسات مثل:<syntaxhighlight lang="rails">
# HTTP ضبط ترويسة
get articles_url, headers: { "Content-Type": "text/plain" }
# محاكاة الطلب مع ترويسة مخصصة
# CGI ضبط متغير
get articles_url, headers: { "HTTP_REFERER": "http://example.com/home" }
# مخصص (env variable) محاكاة الطلب مع متغير بيئة
</syntaxhighlight>


 test "should get index" do
=== اختبار الاشعارات الوامضة  ===
إذا كنت تتذكر، أحد [[Ruby/Hash|الجداول hash]] الثلاثة آنفة الذكر كان <code>flash</code>.


get articles_url
نرغب الآن في إظهار رسالة وامضة (رسالة <code>flash</code>) إلى تطبيق المدونة كلما نجح أحدهم في إنشاء مقالة جديدة.
 
assert_equal "index", @controller.action_name
 
assert_equal "application/x-www-form-urlencoded", @request.media_type
 
assert_match "Articles", @response.body
 
 end
 
End
 
== إعداد الرؤوس ومتغيرات CGI ==
يمكن تمرير رؤوس HTTP ومتغيرات CGI كرؤوس:
 
<nowiki>#</nowiki> setting an HTTP Header
 
get articles_url, headers: { "Content-Type": "text/plain" } # simulate the request with custom header
 
<nowiki>#</nowiki> setting a CGI variable
 
get articles_url, headers: { "HTTP_REFERER": "<nowiki>http://example.com/home</nowiki>" } # simulate the request with custom env variable
 
== اختبار اشعارات flash ==
إذا كنت تتذكر، واحد من متغيرات hash الثلاثة من Apocalypse كان flash.
 
نرغب الآن في إضافة رسالة flash إلى تطبيق المدونة كلما نجح أحدهم في إنشاء مقالة جديدة.
 
لنبدأ بإضافة هذا التأكيد إلى اختبار test_should_create_article:


لنبدأ بإضافة هذا التأكيد إلى الاختبار <code>test_should_create_article</code>:<syntaxhighlight lang="rails">
test "should create article" do
test "should create article" do
 
  assert_difference('Article.count') do
 assert_difference('Article.count') do
    post article_url, params: { article: { title: 'Some title' } }
 
  end
post article_url, params: { article: { title: 'Some title' } }
 
  assert_redirected_to article_path(Article.last)
 end
  assert_equal 'Article was successfully created.', flash[:notice]
 
end
 assert_redirected_to article_path(Article.last)
</syntaxhighlight>إذا أجرينا الاختبار الآن، فمن المفترض أن نرى رسالة الفشل:<syntaxhighlight lang="shell">
 
$ bin/rails test test/controllers/articles_controller_test.rb -n test_should_create_article
 assert_equal 'Article was successfully created.', flash[:notice]
 
End
 
إذا أجرينا الاختبار الآن، فمن المفترض أن نرى رسالة الفشل:
 
$ bin/ريلز test test/controllers/articles_controller_test.rb -n test_should_create_article
 
Run options: -n test_should_create_article --seed 32266
Run options: -n test_should_create_article --seed 32266
 
<nowiki>#</nowiki> Running:
# Running:
 
F
F
 
Finished in 0.114870s, 8.7055 runs/s, 34.8220 assertions/s.
Finished in 0.114870s, 8.7055 runs/s, 34.8220 assertions/s.
 
 1) Failure:
  1) Failure:
 
ArticlesControllerTest#test_should_create_article [/test/controllers/articles_controller_test.rb:16]:
ArticlesControllerTest#test_should_create_article [/test/controllers/articles_controller_test.rb:16]:
--- expected
--- expected
+++ actual
+++ actual
@@ -1 +1 @@
@@ -1 +1 @@
-"Article was successfully created."
-"Article was successfully created."
+nil
+nil
 
1 runs, 4 assertions, 1 failures, 0 errors, 0 skips
1 runs, 4 assertions, 1 failures, 0 errors, 0 skips
 
</syntaxhighlight>لننفّذ الرسالة الوامضة الآن على متحكمنا، إذ سيبدو الإجراء <code>:create</code> الآن كالتالي:<syntaxhighlight lang="rails">
لننفّذ رسالة flash الآن على متحكمنا، سيبدو إجراء :create الآن كالتالي:
 
def create
def create
 
  @article = Article.new(article_params)
 @article = Article.new(article_params)
 
  if @article.save
 if @article.save
    flash[:notice] = 'Article was successfully created.'
 
    redirect_to @article
   flash[:notice] = 'Article was successfully created.'
  else
 
    render 'new'
   redirect_to @article
  end
 
 else
 
   render 'new'
 
 end
 
end
end
 
</syntaxhighlight>والآن إذا أجرينا اختباراتنا، فستنجح:<syntaxhighlight lang="shell">
والآن إذا أجرينا اختباراتنا، فستمر:
$ bin/rails test test/controllers/articles_controller_test.rb -n test_should_create_article
 
$ bin/ريلز test test/controllers/articles_controller_test.rb -n test_should_create_article
 
Run options: -n test_should_create_article --seed 18981
Run options: -n test_should_create_article --seed 18981
 
<nowiki>#</nowiki> Running:
# Running:
 
.
.
 
Finished in 0.081972s, 12.1993 runs/s, 48.7972 assertions/s.
Finished in 0.081972s, 12.1993 runs/s, 48.7972 assertions/s.
 
1 runs, 4 assertions, 0 failures, 0 errors, 0 skips
1 runs, 4 assertions, 0 failures, 0 errors, 0 skips
</syntaxhighlight>


== جمع كل شيء معًا ==
=== جمع كل شيء معًا ===
في هذه المرحلة، يختبر متحكم مقالاتنا إجراءات :index و :new و:create. فماذا عن التعامل مع البيانات الموجودة؟
في هذه المرحلة، يختبر متحكم مقالاتنا الإجراءات <code>:index</code> و <code>:new</code> و ‎<code>:create</code>. فماذا عن التعامل مع البيانات الموجودة؟
 
دعونا نكتب اختبار لإجراء :show


دعونا نكتب اختبار للإجراء <code>:show</code>:<syntaxhighlight lang="rails">
test "should show article" do
test "should show article" do
  article = articles(:one)
  get article_url(article)
  assert_response :success
end
</syntaxhighlight>تذكر من مناقشتنا في وقت سابق عن تحضيرات الاختبار (Fixtures)، سيعطينا التابع <code>()articles</code> إمكانيّة الوصول إلى تحضيرات مقالاتنا.


 article = articles(:one)
فماذا عن حذف مقالة موجودة؟<syntaxhighlight lang="rails">
 
 get article_url(article)
 
 assert_response :success
 
End
 
تذكر من مناقشتنا في وقت سابق عن Fixtures، فسيعطينا تابع () articles() إمكانيّة الوصول إلى Fixtures مقالاتنا.
 
فماذا عن حذف مقالة موجودة؟
 
test "should destroy article" do
test "should destroy article" do
 
  article = articles(:one)
 article = articles(:one)
  assert_difference('Article.count', -1) do
 
    delete article_url(article)
 assert_difference('Article.count', -1) do
  end
 
delete article_url(article)
  assert_redirected_to articles_path
 
end
 end
</syntaxhighlight>يمكننا أيضًا إضافة اختبار لتحديث مقالة موجودة.<syntaxhighlight lang="rails">
 
 assert_redirected_to articles_path
 
End
 
يمكننا أيضًا إضافة  اختبار لتحديث مقالة موجودة.
 
test "should update article" do
test "should update article" do
  article = articles(:one)
  patch article_url(article), params: { article: { title: "updated" } }
  assert_redirected_to article_path(article)
  # Reload association to fetch updated data and assert that title is updated.
  article.reload
  assert_equal "updated", article.title
end
</syntaxhighlight>لاحظ أننا بدأنا نرى بعض التكرارات في هذه الاختبارات الثلاثة، فهم يصلون إلى نفس بيانات تحضير (Fixture) المقالة، ويمكنك تجنب تكرار هذا (مبدأ لا تكرر نفسك [[[wikipedia:Don't_repeat_yourself|D.R.Y.‎]]]) عن طريق استخدام التابعين <code>setup</code> و <code>teardown</code> التي يوفّرها <code>ActiveSupport::Callbacks</code>.


 article = articles(:one)
يجب أن يظهر اختبارنا كالتالي (تجاهل الاختبارات الأخرى في الوقت الحالي، فنحن نتناساهم للإيجاز):<syntaxhighlight lang="rails">
 
 patch article_url(article), params: { article: { title: "updated" } }
 
 assert_redirected_to article_path(article)
 
 # Reload association to fetch updated data and assert that title is updated.
 
 article.reload
 
 assert_equal "updated", article.title
 
End
 
لاحظ أننا بدأنا نرى بعض التكرارات في هذه الاختبارات الثلاثة، فهم يصلون إلى نفس بيانات Fixture المقالة، ويمكنك D.R.Y (عدم تكرارها بنفسك) هذه عن طريق استخدام التوابع setup وteardown التي يوفّرها ActiveSupport::Callbacks.
 
يجب أن يظهر اختبارنا كالتالي، تجاهل الاختبارات الأخرى في الوقت الحالي، فنحن نتركهم للإيجاز.
 
require 'test_helper'
require 'test_helper'
 
class ArticlesControllerTest < ActionDispatch::IntegrationTest
class ArticlesControllerTest < ActionDispatch::IntegrationTest
 
  # يستدعى قبل كل عملية اختبار فردية
 # called before every single test
  setup do
 
    @article = articles(:one)
 setup do
  end
 
@article = articles(:one)
  # يستدعى بعد كل عملية اختبار فردية
 
  teardown do
 end
    # فمن الأفضل إعادة ضبطها بعد ذلك ،cache عندما يستعمل المتحكم
 
    Rails.cache.clear
 # called after every single test
  end
 
 teardown do
  test "should show article" do
 
    # (setup) من الضبط @article إعادة استعمال متغير النسخة
<nowiki>#</nowiki> when controller is using cache it may be a good idea to reset it afterwards
    get article_url(@article)
 
    assert_response :success
ريلز.cache.clear
  end
 
 end
  test "should destroy article" do
 
    assert_difference('Article.count', -1) do
 test "should show article" do
      delete article_url(@article)
 
    end
<nowiki>#</nowiki> Reuse the @article instance variable from setup
 
    assert_redirected_to articles_path
get article_url(@article)
  end
 
assert_response :success
  test "should update article" do
 
    patch article_url(@article), params: { article: { title: "updated" } }
 end
 
    assert_redirected_to article_path(@article)
 test "should destroy article" do
    # إعادة تحميل الاختبار لجلب البيانات المحدثة والتأكد من تحديث العنوان
 
    @article.reload
assert_difference('Article.count', -1) do
    assert_equal "updated", @article.title
 
  end
  delete article_url(@article)
 
end
end
</syntaxhighlight>وعلى غرار الاستدعاءات الأخرى في ريلز، يمكن أيضًا استخدام التابعين <code>setup</code> و <code>teardown</code> عن طريق تمرير كتلة، أو تعبير lambda، أو اسم تابع كرمز للاستدعاء.


assert_redirected_to articles_path
=== مساعدي الاختبار ===
 
لتجنب تكرار الشيفرات البرمجيّة، يمكنك إضافة مساعدي الاختبار الخاصين بك،  ويمكن أن يكون مساعد تسجيل الدخول مثال جيد على ذلك:<syntaxhighlight lang="rails">
 end
# test/test_helper.rb
 
 test "should update article" do
 
patch article_url(@article), params: { article: { title: "updated" } }
 
assert_redirected_to article_path(@article)
 
<nowiki>#</nowiki> Reload association to fetch updated data and assert that title is updated.
 
@article.reload
 
assert_equal "updated", @article.title
 
 end
 
End
 
وعلى غرار الاستدعاءات الأخرى في ريلز، يمكن أيضًا استخدام توابع setup و teardown عن طريق تمرير كتلة ةlambda   أو اسم تابع كرمز للاستدعاء.
 
== مساعدي الاختبار ==
لتجنب تكرار الشيفرات البرمجيّة، يمكنك إضافة مساعدي الاختبار الخاصة بك،  ويمكن أن يكون مساعد تسجيل الدخول مثال جيد على ذلك:
 
<nowiki>#</nowiki> test/test_helper.rb
 
module SignInHelper
module SignInHelper
 
  def sign_in_as(user)
 def sign_in_as(user)
    post sign_in_url(email: user.email, password: user.password)
 
  end
   post sign_in_url(email: user.email, password: user.password)
 
 end
 
end
end
 
class ActionDispatch::IntegrationTest
class ActionDispatch::IntegrationTest
 
  include SignInHelper
 include SignInHelper
 
end
end
 
</syntaxhighlight><syntaxhighlight lang="rails">
require 'test_helper'
require 'test_helper'
 
class ProfileControllerTest < ActionDispatch::IntegrationTest
class ProfileControllerTest < ActionDispatch::IntegrationTest
  test "should show profile" do
    # أصبح المساعد الآن قابلًا لإعادة الاستعمال من أي حالة اختبار لمتحكم
    sign_in_as users(:david)
    get profile_url
    assert_response :success
  end
end
</syntaxhighlight>


 test "should show profile" do
== اختبار مسارات التوجيه ==
 
مثل كل شيء آخر في تطبيق ريلز، يمكنك اختبار مسارات التوجيه (routes) الخاصة بك، إذ تتوضع هذه الاختبارات في test/controllers/أو ستكون جزءًا من اختبارات وحدة التحكم.
<nowiki>#</nowiki> helper is now reusable from any controller test case
 
sign_in_as users(:david)
 
get profile_url
 
assert_response :success
 
 end
 
End
 
= اختبار المسارات =
مثل كل شيء آخر في تطبيق ريلز، يمكنك اختبار مساراتك، فاختبارات المسار موجودة في  test/controllers/ أو ستكون جزء من اختبارات وحدة التحكم.
 
ملاحظة: إذا كان تطبيقك يمتلك طرق معقّدة، فيوفّر ريلز عددًا من المساعدين المفيدين لاختبارهم.
 
لمزيد من المعلومات حول تأكيدات التوجيه المتاحة في ريلز، راجع وثائق API لـ ActionDispatch::Assertions::RoutingAssertions.
 
= اختبار العروض =
يعدّ اختبار الاستجابة لطلبك من خلال التأكيد على وجود عناصر HTML الأساسيّة ومحتواها طريقة شائعة لاختبار طرق عرض التطبيق، مثل اختبارات المسار، وتقع اختبارات العرض في test/controllers/ أو كجزء من اختبارات المتحكم.
 
يسمح لك تابع assert_select لاستعلام عناصر HTML للاستجابة باستخدام صيغة بسيطة لكنها قويّة.


هنالك نوعا من assert_select:
'''ملاحظة''': إذا كان تطبيقك يمتلك مسارات توجيه معقّدة، فيوفّر ريلز عددًا من المساعدين المفيدين لاختبارهم.


يتأكد assert_select(selector, [equality], [message]) من استيفاء شرط المساواة على العناصر المحددة من خلال المحدّد (selector)، وقد يكون هذا الأخير عبارة عن تعبير CSS محدّد (سلسلة نصيّة) أو تعبير يحتوي على قيم بديلة.
لمزيد من المعلومات حول تأكيدات التوجيه المتاحة في ريلز، راجع توثيق الواجهة البرمجية لـ <code>[http://api.rubyonrails.org/v5.2.2/classes/ActionDispatch/Assertions/RoutingAssertions.html ActionDispatch::Assertions::RoutingAssertions]</code>.


يتأكد assert_select(element, selector, [equality], [message]) من استيفاء شرط المساواة على كل العناصر المحددة من خلال المحدّد بدءًا من العنصر (مثيل Nokogiri::XML::Node أو Nokogiri::XML::NodeSet) وأطفاله.
== اختبار العروض ==
يعدّ اختبار الاستجابة لطلبك من خلال التأكيد على وجود عناصر HTML الأساسيّة ومحتواها طريقةً شائعةً لاختبار طرق عرض التطبيق. بشكل مشابه لاختبارات مسار التوجيه، تقع اختبارات العرض في test/controllers/‎ أو كجزء من اختبارات المتحكم. يسمح لك التابع <code>assert_select</code> الاستعلام عن عناصر HTML للاستجابة باستخدام صيغة بسيطة لكنها قويّة.


على سبيل المثال، يمكنك التحقق من محتويات عنصر العنوان في استجابتك باستخدام:
هنالك نوعا من <code>assert_select</code>:
* <code>assert_select(selector, [equality], [message])‎</code>: يتأكد من استيفاء شرط المساواة على العناصر المحددة من خلال المحدّد (selector)، وقد يكون هذا الأخير عبارة عن تعبير محدِّد [[CSS]] (سلسلة نصيّة) أو تعبير يحتوي على قيم بديلة.


assert_select 'title', "Welcome to ريلز Testing Guide"
* <code>assert_select(element, selector, [equality], [message])‎</code>: يتأكد من استيفاء شرط المساواة على كل العناصر المحددة من خلال المحدّد بدءًا من العنصر (نسخة من <code>Nokogiri::XML::Node</code> أو <code>Nokogiri::XML::NodeSet</code>) وأبنائه.
 
على سبيل المثال، يمكنك التحقق من محتويات عنصر العنوان في استجابتك باستخدام:<syntaxhighlight lang="rails">
يمكنك أيضًا استخدام كتل assert_select المتداخلة لتحقيق عمقًا أكبر.
assert_select 'title', "Welcome to Rails Testing Guide"
 
</syntaxhighlight>يمكنك أيضًا استخدام كتل <code>assert_select</code> المتداخلة للتحقق بعمق أكبر.
في المثال التالي، يشتغل assert_select الداخلي لـ li.menu_item ضمن مجموعة عناصر المحدّدة بواسطة الكتلة الخارجيّة:


في المثال التالي، ينفَّذ <code>assert_select</code> الداخلي من أجل <code>li.menu_item</code> ضمن مجموعة العناصر المحدّدة بواسطة الكتلة الخارجيّة:<syntaxhighlight lang="rails">
assert_select 'ul.navigation' do
assert_select 'ul.navigation' do
  assert_select 'li.menu_item'
end
</syntaxhighlight>قد تتكرّر مجموعة من العناصر المحدّدة بحيّث يمكن استدعاء <code>assert_select</code> بشكل منفصل لكل عنصر.


 assert_select 'li.menu_item'
على سبيل المثال، إذا كانت الاستجابة تحتوي على قائمتيّن مرتبتيّن تحتوي كل منهما على أربعة عناصر قائمة متداخلة، فستنجح الاختبارات التالية:<syntaxhighlight lang="rails">
 
End
 
قد تتكرّر مجموعة من العناصر المحدّدة بحيّث يمكن استدعاء assert_select بشكل منفصل لكل عنصر.
 
على سبيل المثال، إذا كانت الاستجابة تحتوي على قائمتيّن مرتبتيّن، تحتوي كل منهما على أربعة عناصر قائمة متداخلة فستمر الاختبارات التالية:
 
assert_select "ol" do |elements|
assert_select "ol" do |elements|
 
  elements.each do |element|
 elements.each do |element|
    assert_select element, "li", 4
 
  end
assert_select element, "li", 4
 
 end
 
end
end
 
assert_select "ol" do
assert_select "ol" do
  assert_select "li", 8
end
</syntaxhighlight>هذا التأكيد قوي جدًا؛ لمزيد من الاستخدام المتقدم، راجع [https://github.com/rails/rails-dom-testing/blob/master/lib/rails/dom/testing/assertions/selector_assertions.rb التوثيق الخاص به].


 assert_select "li", 8
=== تأكيدات إضافية تستند إلى العرض ===
 
End
 
هذا التأكيد قوي جدًا، لمزيد من الاستخدام المتقدم، ارجع إلى الوثائق.
 
== تأكيدات إضافيّة تستند إلى العرض ==
هنالك المزيد من التأكيدات التي تستخدم في المقام الأول في اختبار العروض:
هنالك المزيد من التأكيدات التي تستخدم في المقام الأول في اختبار العروض:
{| class="wikitable"
{| class="wikitable"
|التأكيد
!التأكيد
|الغرض
!الغرض
|-
|-
|assert_select_email
|<code>assert_select_email</code>
|يتيح لك تقديم تأكيدات على نص رسالة البريد الإلكتروني.
|يتيح لك تقديم تأكيدات على نص رسالة البريد الإلكتروني.
|-
|-
|assert_select_encoded
|<code>assert_select_encoded</code>
|يتيح لك تقديم التأكيدات على HTML المشفّر، وذلك عن طريق إلغاء تشفيرات محتويات كل عنصر ومن ثم يستدعي الكتلة مع جميع العناصر غير المشفّرة.
|يتيح لك تقديم التأكيدات على شيفرة HTML مشفّرة، وذلك عن طريق إلغاء تشفير محتويات كل عنصر ومن ثم استدعاء الكتلة مع جميع العناصر غير المشفّرة.
|-
|-
|css_select(selector) أو css_select(element, selector)
|<code>css_select(selector)‎</code> أو <code>css_select(element, selector)‎</code>
|يرّجع مصفوفة بكافة العناصر المحّددة بواسطة المحددّ، في النوع الثاني، يطابق أولًا العنصر الأساسي ويحاول مطابقة التعبير المحدّد على أحد أطفاله، وإذا لم يكن هنالك مطابقات، فسيرجع كلا المتغيريّن مصفوفة فارغة.
|يعيد مصفوفة بكافة العناصر المحّددة بواسطة المحددّ <code>selector</code>. في النوع الثاني، يطابق أولًا العنصر <code>element</code> الأساسي ويحاول مطابقة التعبير المحدّد <code>selector</code> على أحد أبنائه، وإذا لم يكن هنالك مطابقات، فسيعيد كلا المتغيريّن مصفوفة فارغة.
|}
|}
في ما يلي مثال على استخدام assert_select_email:
في ما يلي مثال على استخدام <code>assert_select_email</code>:<syntaxhighlight lang="rails">
 
assert_select_email do
assert_select_email do
  assert_select 'small', 'Please click the "Unsubscribe" link if you want to opt-out.'
end
</syntaxhighlight>


 assert_select 'small', 'Please click the "Unsubscribe" link if you want to opt-out.'
== اختبار المساعدين ==
 
End
 
= اختبار المساعدين =
المساعد هو مجرّد وحدة بسيطة حيث يمكنك تعريف التوابع المتاحة في عروضك.
المساعد هو مجرّد وحدة بسيطة حيث يمكنك تعريف التوابع المتاحة في عروضك.


من أجل اختبار المساعدين، كل ما عليك القيام به هو التحقق من أن ناتج تابع المساعد يطابق ما تتوقعه، وتقع الاختبارات المتعلّقة بالمساعدين تحت دليل test/helpers.
من أجل اختبار المساعدين، كل ما عليك القيام به هو التحقق من أن ناتج تابع المساعد يطابق ما تتوقعه، وتقع الاختبارات المتعلّقة بالمساعدين في المجلد test/helpers.
 
فإذا كان لدينا المساعد التالي:


فإذا كان لدينا المساعد التالي:<syntaxhighlight lang="rails">
module UsersHelper
module UsersHelper
 
  def link_to_user(user)
 def link_to_user(user)
    link_to "#{user.first_name} #{user.last_name}", user
 
  end
link_to "#{user.first_name} #{user.last_name}", user
end
 
</syntaxhighlight>فيمكننا اختبار ناتج هذا التابع كالتالي:<syntaxhighlight lang="rails">
 end
 
End
 
فيمكننا اختبار ناتج هذا التابع كالتالي:
 
class UsersHelperTest < ActionView::TestCase
class UsersHelperTest < ActionView::TestCase
  test "should return the user's full name" do
    user = users(:david)
    assert_dom_equal %{<a href="/user/#{user.id}">David Heinemeier Hansson</a>}, link_to_user(user)
  end
end
</syntaxhighlight>علاوة على ذلك، نظرًا لأن الصنف <code>test</code> موسع (extend) من <code>ActionView::TestCase</code>، يمكنك الوصول إلى توابع مساعدة ريلز مثل <code>link_to</code> أو <code>pluralize</code>.


 test "should return the user's full name" do
== اختبار إجراء المراسلة الخاص بك ==
يتطلب اختبار الأصناف <code>mailer</code> بعض الأدوات المحدّدة للقيام بكامل المهمة.


user = users(:david)
=== الحفاظ على Postman في التحقق ===
يجب عليك اختبار أصناف [[Rails/action mailer|Action Mailer]]  لتطبيق ريلز - مثل أي جزء آخر - للتأكد من عملها بالشكل المتوقّع.


assert_dom_equal %{<a href="/user/#{user.id}">David Heinemeier Hansson</a>}, link_to_user(user)
الهدف من اختبار الأصناف <code>mailer</code> هو التأكد من:
 
* معالجة رسائل البريد الإلكتروني (الإنشاء والإرسال).
 end
* أن يكون محتوى البريد الإلكتروني صحيحًا (الموضوع، والمُرسِل، والمحتوى، ...إلخ.)
 
End
 
علاوة على ذلك، نظرًا لأن الصنف test ممدّد من ActionView::TestCase، فيمكنك الوصول إلى توابع مساعدة ريلز مثل link_to أو pluralize.
 
= اختبار Mailer الخاص بك =
اختبار أصناف Mailer  يتطلّب بعض الأدوات المحدّدة للقيام بعمل شامل.
 
== الحفاظ على Postman في Check ==
يجب عليك اختبار فصول Mailer  لتطبيق ريلز - مثل أي جزء آخر- للتأكد من أنها تعمل كما هو متوقّع.
 
الهدف من اختبار أصناف Mailer هو التأكد من:
* معالج الرسائل الإلكترونيّة (إنشاء وإرسال).
* أن يكون محتوى البريد الإلكتروني صحيح (الموضوع، المُرسِل، الجسم، إلخ)
* إرسال رسائل البريد الإلكتروني الصحيحة في الوقت المناسب.
* إرسال رسائل البريد الإلكتروني الصحيحة في الوقت المناسب.


=== من جميع الجوانب ===
==== من جميع الجوانب ====
هنالك جانبان لاختبار البريد الإلكتروني، اختبارات الوحدة والاختبارات الوظيفيّة، وفي اختبارات الوحدة، نشغّل Mailer بعزلة مع مدخلات متحكم بها ونقارن المخرجات مع قيمة معروفة (Fixture).
هنالك جانبان لاختبار البريد الإلكتروني، اختبارات الوحدة (unit tests) والاختبارات الوظيفيّة (functional tests). ففي اختبارات الوحدة، نشغّل [[Rails/action mailer basics|المرسل]] (mailer) بعزله مع مدخلات متحكم بها ونقارن المخرجات مع قيمة معروفة (Fixture). وفي الاختبارات الوظيفيّة، لا تختبر الكثير من التفاصيل الدقيقة التي ينتجها المرسل؛ وبدلًا من ذلك، نختبر أن وحدات التحكم والنماذج الخاصة بنا تستخدم [[Rails/action mailer basics|المرسل]] بالطريقة الصحيحة، ويكون هذا الاختبار هو إثبات إرسال البريد الإلكتروني الصحيح في الوقت المناسب.
 
وفي الاختبارات الوظيفيّة لا تختبر الكثير من التفاصيل الدقيقة التي ينتجها Mailer، وبدلًا من ذلك، نختبر أن وحدات التحكم والنماذج الخاصة بنا تستخدم Mailer بالطريقة الصحيحة، ويُختبر هذا لإثبات أنه أُرسل البريد الإلكتروني الصحيح في الوقت المناسب.
 
== اختبار الوحدة ==
من أجل اختبار ما إذا كان Mailer يعمل كما هو متوقّع، يمكنك استخدام اختبارات الوحدة لمقارنة النتائج الفعليّة للـMailer مع أمثلة مكتوبة مسبقًا لما يجب إنتاجه.


=== الثأر من Fixtures ===
=== اختبار الوحدة ===
لغرض اختبار وحدة Mailer، تُستخدم Fixtures لتقديم مثال عن كيفيّة ظهور المخرجات، لأن هذه هي أمثلة رسائل بريد إلكتروني، وليس بيانات سجل نشط (Active Record) كالتوابع الأخرى، ويُحتفظ بها بصرف النظر عن Fixtures الأخرى، ويتطابق اسم المجلد الموجود في test/fixtures إلى اسم Mailer، لذلك بالنسبة إلى Mailer الذي يحمل اسم UserMailer، يجب أن يكون Fixture في مجلد test/fixtures/user_mailer.
من أجل اختبار ما إذا كان [[Rails/action mailer basics|المرسل]] يعمل كما هو متوقّع، يمكنك استخدام اختبارات الوحدة لمقارنة النتائج الفعليّة [[Rails/action mailer basics|للمرسل]] مع أمثلة مكتوبة مسبقًا لما يجب توليدها.


إذا ولّدت Mailer، فلن يقوم المولّد بإنشاء Fixtures stub لإجراءات Mailer، ويجب عليك إنشاء هذه الملفات بنفسك كما هو موضّح أعلاه.
==== الثأر من تحضيرات الاختبار ====
لغرض اختبار [[Rails/action mailer basics|وحدة الإرسال]]، تُستخدم التحضيرات (Fixtures) لتقديم مثال عن كيفيّة ظهور المخرجات.لمَّا كانت هذه هي رسائل بريد إلكتروني نموذجية (example emails) وليست بيانات [[Rails/active record|Active Record]] كالتوابع الأخرى، يُحتفظ بها بصرف النظر عن التحضيرات (Fixtures) الأخرى. ويتطابق اسم المجلد الموجود في test/fixtures مع اسم [[Rails/action mailer basics|المرسل]]، لذلك بالنسبة إلى [[Rails/action mailer basics|المرسل]] الذي يحمل الاسم <code>UserMailer</code>، يجب أن يكون التحضير (Fixture) في المجلد test/fixtures/user_mailer.


=== حالة الاختبار الأساسي ===
إذا ولّدت مرسلًا، فلن يقوم المولّد بإنشاء بذرة للتحضيرات (Fixtures stub) التي تخص [[Rails/action mailer basics|Action Mailer]]، ويجب عليك إنشاء هذه الملفات بنفسك كما هو موضّح أعلاه.
في ما يلي اختبار وحدة لاختبار Mailer يسمى UserMailer والذي يُستخدم إجراء invite الخاص به لإرسال دعوة إلى صديق، إنها نسخة معدّلة من الاختبار الأساسي الذي أنشأه المولّد لإجراء invite.


==== حالة الاختبار الأساسي ====
في ما يلي اختبار وحدة لاختبار [[Rails/action mailer basics|مرسل]] يسمى <code>UserMailer</code> والذي يُستخدم الإجراء <code>invite</code> الخاص به لإرسال دعوة إلى صديق. إليك نسخة معدّلة من الاختبار الأساسي الذي أنشأه المولّد للإجراء <code>invite</code>:<syntaxhighlight lang="rails">
require 'test_helper'
require 'test_helper'
 
class UserMailerTest < ActionMailer::TestCase
class UserMailerTest < ActionMailer::TestCase
 
  test "invite" do
 test "invite" do
    # إنشاء البريد الإلكتروني وتخزينه من أجل التحقق منه بشكل أوسع
 
    email = UserMailer.create_invite('me@example.com',
<nowiki>#</nowiki> Create the email and store it for further assertions
                                    'friend@example.com', Time.now)
 
email = UserMailer.create_invite('me@example.com',
    # إرسال البريد الإلكتروني ثم التأكد من أنه أصبح ضمن الطابور
 
    assert_emails 1 do
                                 'friend@example.com', Time.now)
      email.deliver_now
 
    end
<nowiki>#</nowiki> Send the email, then test that it got queued
 
    # اختبار محتوى البريد الإلكتروني المرسل من مطابقته لتوقعاتنا
assert_emails 1 do
    assert_equal ['me@example.com'], email.from
 
    assert_equal ['friend@example.com'], email.to
  email.deliver_now
    assert_equal 'You have been invited by me@example.com', email.subject
 
    assert_equal read_fixture('invite').join, email.body.to_s
  end
end
end
</syntaxhighlight>في هذا الاختبار، أرسلنا بريدًا إلكترونيًّا وخزّنا الكائن المعادة في المتغيّر <code>email</code>، ثم تأكدنا من إرساله (التأكيد الأول)، ومن ثم، في الدفعة الثانية من التأكيدات، تأكدنا من أن البريد الإلكتروني يحتوي بالفعل على ما نتوقعه. واستخدمنا المساعد <code>read_fixture</code> لقراءة محتوى هذا الملف.


<nowiki>#</nowiki> Test the body of the sent email contains what we expect it to
'''ملاحظة''': سيتواجد <code>email.body.to_s</code> عند وجود جزء واحد فقط (شيفرة HTML أو نص)؛ فإذا كان الإرسال يوفر الإثنيّن، يمكنك اختيار التحضير (Fixture) الخاص بك على أجزاء معيّنة باستخدام <code>email.text_part.body.to_s</code> أو <code>email.html_part.body.to_s</code>.
 
assert_equal ['me@example.com'], email.from
 
assert_equal ['friend@example.com'], email.to
 
assert_equal 'You have been invited by me@example.com', email.subject
 
assert_equal read_fixture('invite').join, email.body.to_s
 
 end
 
End
 
في الاختبار، أرسلنا البريد الإلكترونيالالكتروني وخزّنا الكائن المرجع في متغيّر email، ثم تأكدنا من Mailer (التأكيد الأول)، ومن ثم، في الدفعة الثانية من التأكيدات، تأكدنا من أن البريد الإلكتروني يحتوي بالفعل على ما نتوقعه، ونستخدم المساعد read_fixture للقراءة في محتوى من هذا الملف.
 
ملاحظة: سيتواجد email.body.to_s عند وجود جزء واحد فقط (HTML أو نص)، إذا كان الإرسال يوفر الإثنيّن، فيمكنك اختيار Fixture الخاص بك على أجزاء معيّنة باستخدام email.text_part.body.to_s أو email.html_part.body.to_s.
 
هذا محتوى Fixture invite:


إليك محتوى التحضير <code>invite</code>:<syntaxhighlight lang="text">
Hi friend@example.com,
Hi friend@example.com,
 
You have been invited.
You have been invited.
 
Cheers!
Cheers!
</syntaxhighlight>هذا هو الوقت المناسب لفهم المزيد عن كتابة اختبارات [[Rails/action mailer basics|Action Mailer]] الخاص بك. فالسطر <code>ActionMailer::Base.delivery_method = :test</code> في الملف config/environments/test.rb يضبط تابع التوصيل (delivery method) إلى وضع الاختبار (أي الوضع test) حتى لا تُسلّم الرسائل الإلكترونيّة بالفعل (هذا مفيد لتجنّب إرسال رسائل غير مرغوب فيها للمستخدمين أثناء الاختبار) ولكن بدلًا من ذلك سيضاف إلى مصفوفة (<code>ActionMailer::Base.deliveries</code>).


هذا هو الوقت المناسب لفهم المزيد عن كتابة الاختبارات للـMailer الخاصة بك، السطر ActionMailer::Base.delivery_method = :test في config/environments/test.rb يُعد تابع delivery لوضع الاختبار حتى لا تُسلّم الرسائل الإلكترونيّة بالفعل (مفيد لتجنّب إرسال رسائل غير مرغوب فيها للمستخدمين أثناء الاختبار) ولكن بدلًا من ذلك سيُلحق إلى مصفوفة (ActionMailer::Base.deliveries).
'''ملاحظة''': يعاد تعيين المصفوفة <code>ActionMailer::Base.deliveries</code> تلقائيًا في الاختبارين <code>ActionMailer::TestCase</code> و <code>ActionDispatch::IntegrationTest</code> فقط؛ وإذا رغبت بالحصول على مصفوفة نظيفة خارج هذه الحالات التجريبيّة، يمكنك إعادة تعيينها يدويًا باستخدام <code>ActionMailer::Base.deliveries.clear</code>.
 
ملاحظة: يعاد تعيين المصفوفة ActionMailer::Base.deliveries تلقائيًا في اختبارات ActionMailer::TestCase وActionDispatch::IntegrationTest فقط، وإذا رغبت بالحصول على حالة نظيفة خارج هذه الحالات التجريبيّة، فيمكنك إعادة تعيينها يدويًا باستخدام ActionMailer::Base.deliveries.clear.
 
== الاختبار الوظيفي ==
يتضمن الاختبار الوظيفي للـMailer أكثر من مجرّد التحقق من صحّة نص البريد الإلكتروني، المستلمين وما إلى ذلك، ففي اختبارات البريد الوظيفيّة تستدعي توابع التسليم والتحقق من أن رسائل البريد الإلكتروني المناسبة ضمّنت في قائمة التسليم، ومن الجيد أن نفترض أن توابع التسليم نفسها تؤدي وظيفتها، وربما قد تكون أكثر اهتمامًا بما إذا كان منطق الأعمال هو إرسال رسائل البريد الإلكتروني عندما تتوقّع منهم، فعلى سبيل المثال، يمكنك التحقق من أن عملية دعوة الصحيح تُرسل ببريد إلكتروني بشكل مناسب:


=== الاختبار الوظيفي واختبار النظام ===
يتضمن الاختبار الوظيفي لـ [[Rails/action mailer|Action Mailer]] أكثر من مجرّد التحقق من صحّة نص البريد الإلكتروني، إذ يشمل اختبار المستلمين أيضًا. ففي الاختبارات الوظيفية واختبارات النظام، نختبر إن كانت تفاعلات مستخدمٍ تودي إلى إطلاق عملية إرسال بريد إلكتروني بشكل مناسب والتأكد من تسليم البريد الإلكتروني. فعلى سبيل المثال، يمكنك التحقق من أن عملية دعوة صديق تُرسل بريدًا إلكترونيًّا مناسبًا بشكل صحيح:<syntaxhighlight lang="rails">
# اختبار تكامل
require 'test_helper'
require 'test_helper'
 
class UsersControllerTest < ActionDispatch::IntegrationTest
class UsersControllerTest < ActionDispatch::IntegrationTest
 
  include ActionMailer::TestHelper
 test "invite friend" do
 
  test "invite friend" do
assert_difference 'ActionMailer::Base.deliveries.size', +1 do
    # ActionMailer::Base.deliveries التأكد من الاختلاف في
 
    assert_emails 1 do
  post invite_friend_url, params: { email: 'friend@example.com' }
      post invite_friend_url, params: { email: 'friend@example.com' }
 
    end
  end
end
</syntaxhighlight><syntaxhighlight lang="rails">
# اختبار نظام
require 'test_helper'
class UsersTest < ActionDispatch::SystemTestCase
  driven_by :selenium, using: :headless_chrome
  include ActionMailer::TestHelper
  test "inviting a friend" do
    visit invite_users_url
    fill_in 'Email', with: 'friend@example.com'
    assert_emails 1 do
      click_on 'Invite'
    end
  end
end
end
</syntaxhighlight>'''ملاحظة''': تستفيد هذه الأمثلة من <code>ActionMailer::TestHelper</code>. أمَّا من أجل رسائل البريد الإلكتروني التي نتوقع توصيلها مباشرةً مع التابع <code>deliver_now</code>، يمكننا استعمال التابع <code>assert_emails</code>. ومن أجل رسال البريد الإلكتروني التي نتوقع توصيلها لاحقًا على أنها [[Rails/active job|ActiveJob]] مع التابع <code>deliver_later</code>، يمكننا استعمال التابع <code>assert_enqueued_emails</code>. للمزيد من المعلومات، يمكنك الاطلاع على [https://api.rubyonrails.org/classes/ActionMailer/TestHelper.html هذا التوثيق].


invite_email = ActionMailer::Base.deliveries.last
== اختبار الوظائف (Testing Jobs) ==
 
بما أن [[Rails/active job basics|وظائفك]] (jobs) المخصّصة يمكن وضعها في طوابير في مستويات مختلفة داخل التطبيق الخاص بك، فستحتاج إلى اختبار كل من الوظائف نفسها (سلوكها عندما تصبح مدرجة في الطابور) وتلك الكيانات الأخرى التي أدرجتها في الطابور بشكل صحيح.
assert_equal "You have been invited by me@example.com", invite_email.subject
 
assert_equal 'friend@example.com', invite_email.to[0]
 
assert_match(/Hi friend@example\.com/, invite_email.body.to_s)
 
 end
 
End
 
= اختبار الوظائف =
بما أن مهامك المخصّصة يمكن وضعها في طوابير في مستويات مختلفة داخل التطبيق الخاص بك، فستحتاج إلى اختبار كل من الوظائف نفسها (سلوكها عندما تصبح مدرجة في الطابور) وأن الكيانات الأخرى تُدرجها بشكل صحيح.
 
== حالة اختبار أساسيّة ==
بشكل افتراضي، عند إنشاء مهمة، سيُنشئ اختبار مرتبط أيضًا ضمن مجلد test/jobs، وهذا مثال لاختبار مهمة فوترة:


=== حالة اختبار أساسية ===
بشكل افتراضي، عند إنشاء مهمة، سيولد اختبار مرتبط أيضًا ضمن المجلد test/jobs، وهذا مثال لاختبار مهمة فوترة (billing job):<syntaxhighlight lang="rails">
require 'test_helper'
require 'test_helper'
 
class BillingJobTest < ActiveJob::TestCase
class BillingJobTest < ActiveJob::TestCase
  test 'that account is charged' do
    BillingJob.perform_now(account, product)
    assert account.reload.charged_for?(product)
  end
end
</syntaxhighlight>هذا الاختبار بسيط للغاية ويؤكد فقط أن المهمة قد أنجزت كما هو متوقع.


 test 'that account is charged' do
بشكل افتراضي، يُعيّن <code>ActiveJob::TestCase</code> محوّل الطابور إلى <code>:test</code> بحيث تُنفّذ جميع المهام بشكل داخلي، وسيضمن أيضًا أن جميع المهام التي نُفّذت سابقًا والتي في الطابور جرى مسحها قبل إجراء أي اختبار، ويمكنك بذلك أن تفترض بأمان أنه لم يُنفّذ أي مهمة بالفعل في نطاق كل اختبار.
 
BillingJob.perform_now(account, product)
 
assert account.reload.charged_for?(product)
 
 end


End
=== التأكيدات المخصصة واختبار الوظائف داخل مكونات أخرى ===
تعمل [[Rails/active job basics|Active Job]] مع مجموعة من التأكيدات المخصّصة التي يمكن استخدامها لتقليل الاختبارات. وللحصول على قائمة كاملة بالتأكيدات المتاحة، راجع وثائق واجهة برمجة التطبيقات لـ <code>[http://api.rubyonrails.org/v5.2.2/classes/ActiveJob/TestHelper.html ActiveJob::TestHelper]</code>.


هذا الاختبار بسيط للغاية ويؤكد فقط أن المهمة قد أنجزت كما هو متوقع.
من الممارسات الجيّدة التأكد من أنَّ مهامك تُضمّن بشكل صحيح أو تُنفّذ أينما استدعيتها (على سبيل المثال داخل وحدات التحكم الخاصة بك).
 
بشكل افتراضي يُعيّن ActiveJob::TestCase محوّل الطابور إلى :test بحيث تُنفّذ جميع المهام بشكل داخلي، وسيضمن أيضًا أن جميع المهام التي نُفّذت سابقًا والتي في الطابور مسحها قبل إجراء أي اختبار، ويمكنك أن تفترض بأمان أنه لم يُنفّذ أي مهمة بالفعل في نطاق كل اختبار.
 
== التأكيدات المخصّصة واختبار الوظائف داخل المكونات الأخرى ==
يعمل Active Job مع مجموعة من التأكيدات المخصّصة التي يمكن استخدامها لتقليل الاختبارات، وللحصول على قائمة كاملة بالتأكيدات المتاحة، راجع وثائق واجهة برمجة التطبيقات لـ ActiveJob::TestHelper.
 
من الممارسات الجيّدة هي التأكد من أن مهامك تُضمّن بشكل صحيح أو تُنفّذ أينما استدعيتهم (على سبيل المثال داخل وحدات التحكم الخاصة بك).
 
وهنا على وجه التحديد حيث تكون التأكيدات المخصصة التي توفّرها Active Job مفيدة للغاية، على سبيل المثال، في داخل النموذج:


وهنا على وجه التحديد حيث تكون التأكيدات المخصصة التي توفّرها [[Rails/active job|Active Job]] مفيدة للغاية. على سبيل المثال، في داخل النموذج:<syntaxhighlight lang="rails">
require 'test_helper'
require 'test_helper'
 
class ProductTest < ActiveJob::TestCase
class ProductTest < ActiveJob::TestCase
 
  test 'billing job scheduling' do
 test 'billing job scheduling' do
    assert_enqueued_with(job: BillingJob) do
 
      product.charge(account)
assert_enqueued_with(job: BillingJob) do
    end
 
  end
  product.charge(account)
 
end
end
</syntaxhighlight>


 end
== موارد اختبار إضافية ==


End
=== اختبار شيفرة برمجية معتمدة على الوقت ===
 
يوفّر ريلز توابع مساعدة مضمّنة تمكنك من التأكد من عمل تعليمات برمجيّة تعتمد على الوقت كما هو متوقّع لها.
= موارد اختبار إضافيّة =
 
== اختبار شيفرة برمجيّة معتمدة على الوقت ==
يوفّر ريلز توابع مساعدة مضمّنة التي تمكنك من التأكيد على أن التعليمات البرمجيّة التي تعتمد على الوقت تعمل كما هو متوقّع.
 
وفي ما يلي  مثال باستخدام المساعد travel_to:
 
<nowiki>#</nowiki> Lets say that a user is eligible for gifting a month after they register.


إليك مثال باستخدام المساعد <code>travel_to</code>:<syntaxhighlight lang="rails">
# لنفترض أن المستخدم مؤهل للإهداء لمدة شهر بعد تسجيله
user = User.create(name: 'Gaurish', activation_date: Date.new(2004, 10, 24))
user = User.create(name: 'Gaurish', activation_date: Date.new(2004, 10, 24))
assert_not user.applicable_for_gifting?
assert_not user.applicable_for_gifting?
travel_to Date.new(2004, 11, 24) do
travel_to Date.new(2004, 11, 24) do
 
  assert_equal Date.new(2004, 10, 24), user.activation_date  
 assert_equal Date.new(2004, 10, 24), user.activation_date # inside the `travel_to` block `Date.current` is mocked
  # `Date.current` من (mock) أنشئ نموذج ،`travel_to` داخل الكتلة
 
  assert user.applicable_for_gifting?
 assert user.applicable_for_gifting?
 
end
end
assert_equal Date.new(2004, 10, 24), user.activation_date
# فقط `travel_to`كانت التغييرات مرئية داخل الكتلة


assert_equal Date.new(2004, 10, 24), user.activation_date # The change was visible only inside the `travel_to` block.
</syntaxhighlight>الرجاء مراجعة توثيق الواجهة <code>[http://api.rubyonrails.org/v5.2.2/classes/ActiveSupport/Testing/TimeHelpers.html ActiveSupport::Testing::TimeHelpers]</code> البرمجة للحصول على معلومات تفصيليّة حول مساعدي الوقت المتاحين.
 
الرجاء مراجعة وثائق واجهة برمجة التطبيقات ActiveSupport::Testing::TimeHelpers للحصول على معلومات تفصيليّة حول المساعدي الوقت المتاحين.
 
= انطباعات =
نشجعك على المساعدة في تحسين جودة هذا الدليل.
 
يرجى المساهمة إذا رأيت أية أخطاء إملائية أو واقعيّة. للبدء، يمكنك قراءة وثائق قسم المساهمات.
 
قد تجد أيضًا محتوى غير مكتمل أو أشياء غير محدّثة، ويرجى إضافة أي وثائق مفقودة لـ master.
 
تحقق من Edge Guides أولا للتحقق مما إذا كانت المشكلات تم إصلاحها بالفعل أم لا على الفرع الرئيسي (master branch)، وتحقق من إرشادات Ruby on ريلز Guides Guidelines للأسلوب والاتفاقيات المعمول بها.


<nowiki>https://guides.rubyonريلز.org/testing.html</nowiki>
== مصادر ==
* [https://guides.rubyonrails.org/testing.html صفحة Testing Rails Applications في توثيق Ruby On Rails الرّسمي].
[[تصنيف:Rails]]
[[تصنيف:Rails Digging Deeper]]

المراجعة الحالية بتاريخ 07:14، 25 مارس 2019

يشمل هذا الدليل الآليّات المدمجة في ريلز لإجراء الاختبارت على التطبيقات. بعد قراءة هذا الدليل، ستتعلم:

  • الاصطلاح المتعلّق باختبارات ريلز.
  • كيفيّة كتابة كلٍّ من اختبارات الوحدة، الاختبارات الوظيفيّة، والاختبارات التكامليّة لتطبيقات ريلز.
  • وغيرها من الطرق والإضافات الخاصّة بالاختبارات.

ما الهدف من كتابة اختبارات لتطبيقات ريلز؟

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

مقدمة إلى الاختبارات

كان دعم الاختبارات من اللبنات الأساسيّة لريلز منذ البداية، و لم يكن أبدًا وليد لحظةٍ من الإلهام أو محاولةٍ لمجاراة الرّكب.

يهيئ ريلز للاختبارات من البداية

يُنشئ  ريلز مجلّد اختبار لك حال إنشائك مشروع ريلز مستعملًا الأمر rails new application_name. إذا عرضت محتويات هذا المجلّد، فستجد القائمة التالية:

$ ls -F test
controllers/           helpers/               mailers/               system/                test_helper.rb
fixtures/              integration/           models/                application_system_test_case.rb

من المفترض أن تحوي المجلّدات ‏helpers، و ‏‏mailers‏، و models الاختبارات الخاصّة بالدّوال المساعدة للواجهة، و Action Mailer، والنّماذج على التّوالي. بينما يحتوي المجلّد controllers  على الاختبارات الخاصّة بوحدات التحكّم، ومسارات التوجيه، والواجهات. أما المجلّد integration، فإنّه يحوي الاختبارات الخاصّة بالتفاعلات بين وحدات التحكّم.

يحتوي المجلد system على اختبارات النّظام التي تستعمل عند الاختبارات الشّاملة على المتصفّح. تمكّنك اختبارات النّظام من تجربة تطبيقك على الوجه الذي يُفترض بالمستخدمين أن يجرّبوه، كما تمكّنك أيضًا من اختبار JavaScript. هذا بالإضافة إلى أنّها تَرِث من Capybara.

تمثّل تحضيرات الاختبار (Fixtures) طريقةً لتنظيم بيانات الاختبار وتقع في المجلّد fixtures، ويُنشَأ المجلّد jobs حال تولُّيد أوّل اختبار تابع له. يحوي الملفّ test_helper.rb الضّبط الافتراضيّ للاختبارات ككُلّ، بينما يحوي الملفّ application_system_test_case.rb الضّبط الافتراضيّ لاختبارات النّظام.

بيئة الاختبارات

لكلّ تطبيق ريلز ثلاث بيئات افتراضيّة: بيئة التطوير، وبيئة الاختبار، وبيئة الإنتاج. يمكن ضبط كلّ بيئة من هذه البيئات على نحوٍ مماثل. في حالتنا هذه، يمكننا ضبط بيئة الاختبار من خلال تغيير الخيارات الموجودة في الملفّ config/environments/test.rb.

ملاحظة: تُنفَّذ اختباراتك في البيئة RAILS_ENV=test.  

ريلز مع Minitest

إن كنت تذكر، استعملنا الأمر rails generate model في دليل البدء مع ريلز. أنشأنا حينها أوّل نموذج لنا، ومن الأمور التي أنشأها أيضًا هو بذور الاختبار (test stubs) في المجلد test:

$ bin/rails generate model article title:string body:text
...
create  app/models/article.rb
create  test/models/article_test.rb
create  test/fixtures/articles.yml
...

تبدو بذرة الاختبار الافتراضيّة في الملفّ test/models/article_test.rb كالتالي:

require 'test_helper'
 
class ArticleTest < ActiveSupport::TestCase
  # test "the truth" do
  #   assert true
  # end
end

سيساعدك فحص هذا الملف سطرًا بسطر على فهمٍ أفضل لاصطلاحات وشيفرة اختبارات ريلز.

require 'test_helper'

يؤديّ تضمين الملفّ test_helper.rb إلى تحميل الضّبط الافتراضيّ لإجراء الاختبارات. من الآن فصاعدًا، سنضمّن هذا الملفّ عند كلّ الاختبارات التي نكتبها، وبهذا تكون كلّ التوابع التي نضيفها لهذا الملفّ متاحة لكلّ الاختبارات.

class ArticleTest < ActiveSupport::TestCase

يعرّف الصّنف ArticleTest حالة اختبار (test case) لأنها ترث من ActiveSupport::TestCase. وبالتالي، تملك ArticleTest كل التوابع التي تتيحها ActiveSupport::TestCase. سنرى لاحقًا في هذا الدليل بعض التوابع التي تمنحنا إيّاها.

التّوابع المعرّفة في صنفٍ يرث من Minitest::Test (الذي هو صنف أبٍ لـ ActiveSupport::TestCase) والتي تبدأ بـ test_‎ (بالأحرف الصغيرة) يُطلق عليها ببساطة "اختبار". وبهذا، التوابع المعرّفة كـ test_password و test_valid_password هي أسماء اختبارات جائزة وتشتغل آليًّا عند إجراء حالة الاختبار.

يمكن لريلز أيضًا إضافة تابع اختبار عن طريق تقديم اسمٍ للاختبار وكتلة، ليولّد بذلك اختبار Minitest::Unit عاديًّا بأسماء توابع مسبوقة بـ test_‎. وبهذا ليس عليك أن تنشغل بتسمية التوابع وتكتفي بكتابة شيءٍ من هذا القبيل:

test "the truth" do
  assert true
end

وهو تقريبًا كما لو كتبته بهذا الشكل:

def test_the_truth
  assert true
end

مع أنّه يبقى بإمكانك اتّباع الطريقة الاعتياديّة لتعريف التوابع، لكنّ استعمال الماكرو test كما سبق يضفي المزيد من الوضوح على أسماء الاختبارات.

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

بعد هذا، لنُلقِ نظرة على تحقّقنا الأوّل:

assert true

التحقّق هو سطر شيفرة يعمل على تقدير صحّة كائن (أو تعبير) ما. من أمثلة ذلك:

  • هل هذه القيمة تساوي تلك القيمة؟
  • هل هذا الكائن هو كائن عدمي nil؟
  • هل سيولّد سطر الشيفرة هذا استثتاءً؟
  • هل تفوق كلمة مرور المستخدم هذه عن 5 أحرف؟

يمكن أن يحتوي الاختبار على تحقّق واحد أو أكثر، ولا حدّ لعدد التحقّقات المسموح بها في كلّ اختبار. ينجح الاختبار فقط في حال نجاح جميع التحقّقات.

أول اختبار فاشل لك

للاطلاع على كيفيّة التبليغ عن فشل الاختبار، بإمكانك إضافة اختبار فاشل لحالة اختبار في article_test.rb.

test "should not save article without title" do
  article = Article.new
  assert_not article.save
end

لِنُجرِ الاختبار الذي أضفناه للتوّ (حيث أنّ السطر الذي عٌرّف فيه الاختبار هو السادس).

$ bin/rails test test/models/article_test.rb:6
Run options: --seed 44656
 
# Running:
 
F
 
Failure:
ArticleTest#test_should_not_save_article_without_title [/path/to/blog/test/models/article_test.rb:6]:
Expected true to be nil or false
 
 
bin/rails test test/models/article_test.rb:6
 
 
 
Finished in 0.023918s, 41.8090 runs/s, 41.8090 assertions/s.
 
1 runs, 1 assertions, 1 failures, 0 errors, 0 skips

في مخرجات الاختبار، يعبّر الحرف F عن الفشل. يمكنك رؤية اسم الفشل و التتبع الخاصّ به تحت Failure. تحتوي الأسطر التالية لذلك على التتبع التكديسيّ متبوعًا برسالة تذكر القيمة الحالية و القيمة المتوقّعة من التحقّق. تقدّم الرسائل الافتراضيّة للتحقّق ما يكفي من المعلومات التي تساعد على تحديد الخطأ. لكن لجعل رسالة فشل التحقّق أكثر وضوحًا، يوفّر التحقّق معامل رسالة اختياريًّا، كما هو مبيّن هنا:

test "should not save article without title" do
  article = Article.new
  assert_not article.save, "Saved the article without a title"
end

يُظهر إجراء الاختبار هذا رسالةً ألطف من تلك التي يظهرها المتحقّق:

Failure:
ArticleTest#test_should_not_save_article_without_title [/path/to/blog/test/models/article_test.rb:6]:
Saved the article without a title

والآن حتى نجعل هذا الاختبار ناجحًا، يمكننا إضافة مصادقة على مستوى النموذج (model level validation) للحقل title:

class Article < ApplicationRecord
  validates :title, presence: true
end

من المفترض أن ينجح الاختبار الآن. لنتأكد من ذلك بإجراء الاختبار ثانيةً:

$ bin/rails test test/models/article_test.rb:6
Run options: --seed 31252
 
# Running:
 
.
 
Finished in 0.027476s, 36.3952 runs/s, 36.3952 assertions/s.
 
1 runs, 1 assertions, 0 failures, 0 errors, 0 skips

لو لاحظت ذلك، كتبنا في البداية اختبارًا ليفشل من أجل وظيفة مرجوّة، ثم كتبنا بعدها شيفرة لتضيف هذه الوظيفة، وفي النهاية تأكّدنا أنّ اختبارنا قد نجح. تُسمّى هذه الطريقة في تطوير البرمجيّات "بالتطوير وفق الاختبار" (Test-Driven Development).

كيف يبدو الخطأ

لترى كيف يُبلَّغ عن الخطأ، إليك اختبارًا يحتوي على واحد:

test "should report error" do
  # غير معرف في حالة الاختبار some_undefined_variable إن
  some_undefined_variable
  assert true
end

ترى الآن المزيد من المخرجات في الطّرفية بعد إجراء هذا الاختبار:

$ bin/rails test test/models/article_test.rb
Run options: --seed 1808
 
# Running:
 
.E
 
Error:
ArticleTest#test_should_report_error:
NameError: undefined local variable or method 'some_undefined_variable' for #<ArticleTest:0x007fee3aa71798>
    test/models/article_test.rb:11:in 'block in <class:ArticleTest>'
 
 
bin/rails test test/models/article_test.rb:9
 
 
 
Finished in 0.040609s, 49.2500 runs/s, 24.6250 assertions/s.
 
2 runs, 1 assertions, 0 failures, 1 errors, 0 skips

لاحظ وجود الحرف E في المخرجات. يعبّر ذلك عن خطأ في الاختبار.

ملاحظة: يتوقف تنفيذ تابع الاختبار حالما يصادف خطأً أو فشلًا في التحقّق، وينتقل الاختبار إلى تنفيذ التابع التالي. تُنفّذ توابع الاختبار وفق ترتيب عشوائيّ. يمكن ضبط ترتيب الاختبار من خلال الخيار config.active_support.test_order.

عندما يفشل اختبار ما، يُعرَض لك التتبّع العكسيّ (backtrace) الخاصّ بهذا الاختبار. يعمل ريلز افتراضيًّا على ترشيح ذلك التتبّع العكسي، ويخرج فقط الأسطر الخاصّة بتطبيقك. يساعد هذا على إزالة التشويش الناجم عن إطار العمل، ويمكّنك من التركيز على شيفرتك. لكن في حالة ما أردت الاطلاع على التتبّع العكسيّ كاملًا يكفي أن تضيف الوسيط b- (أو backtrace--).

$ bin/rails test -b test/models/article_test.rb

إذا أردنا لهذا الاختبار أن ينجح، يمكن أن نُعدّله بأن يستعمل assert_raises بالشكل التالي:

test "should report error" do
  # غير معرف في مكان آخر في حالة الاختبار some_undefined_variable إن
  assert_raises(NameError) do
    some_undefined_variable
  end
end

لابدّ أن ينجح الاختبار الآن.

التحقّقات المتاحة

مما سبق، يفترض الآن أنك أخذت لمحةً خاطفةً عن التحقّقات المتاحة في ريلز. تٌعتبر التحقّقات بمثابة النحلات العاملة، إذ أنّها هي المسؤولة عن الفحص والتأكّد من أن كلّ شيءٍ يسير حسب ما خُطّط له.

ما يلي خلاصة من التحقّقات التي يمكن أن تستعملها مع Minitest، المكتبة الافتراضيّة التي تستعملها ريلز للاختبارات. المعامل [msg] هو سلسلة نصيّة اختياريّة يمكنك استعماله لجعل رسائل فشل الاختبار أكثر وضوحًا.

التحقّق الغرض
assert( test, [msg] )‎ يتأكّد من أنّ قيمة test هي true.
assert_not( test, [msg] )‎ يتأكّد من أنّ قيمة test هي false.
assert_equal( expected, actual, [msg] )‎ يتأكّد من أنّ قيمة expected == actual هي true.
assert_not_equal( expected, actual, [msg] )‎ يتأكّد من أن قيمة expected != actual هي true.
assert_same( expected, actual, [msg] )‎ يتأكّد من أنّ قيمة (expected.equal?(actual هي true.
assert_not_same( expected, actual, [msg] )‎ يتأكّد من أنّ قيمة (expected.equal?(actual هي false.
assert_nil( obj, [msg] )‎ يتأكّد من أنّ قيمة obj.nil?‎ هي true.
assert_not_nil( obj, [msg] )‎ يتأكّد من أنّ قيمة obj.nil?‎ هي false.
assert_empty( obj, [msg] )‎ يتأكّد من أنّ قيمة obj هي empty?‎.
assert_not_empty( obj, [msg] )‎ يتأكّد من أنّ قيمة obj ليست empty?‎.
assert_match( regexp, string, [msg] )‎ يتأكّد من أنّ قيمة السّلسلة النصيّة string تطابق التعبير النمطيّ regexp.
assert_no_match( regexp, string, [msg] )‎ يتأكّد من أنّ قيمة السّلسلة النصيّة string لا تطابق التعبير النمطيّ regexp.
assert_includes( collection, obj, [msg] )‎ يتأكّد من أنّ obj هو ضمن collection.
assert_not_includes( collection, obj, [msg] )‎ يتأكّد من أنّ obj ليس ضمن collection.
assert_in_delta( expected, actual, [delta], [msg] )‎ يتأكّد من أنّ الفرق بين العددين actual و expected هو أقلّ من delta.
assert_not_in_delta( expected, actual, [delta], [msg] )‎ يتأكّد من أنّ الفرق بين العددين actual و expected ليس أقلّ من delta.
assert_in_epsilon ( expected, actual, [epsilon], [msg] )‎ يتأكّد من أنّ نسبة العددين actual و expected إلى بعضهما هي أقلّ من epsilon.
assert_not_in_epsilon ( expected, actual, [epsilon], [msg] )‎ يتأكّد من أنّ نسبة العددين actual و expected إلى بعضهما ليست أقلّ من epsilon.
assert_throws( symbol, [msg] ) { block }‎ يتأكّد من أنّ الكتلة block ترمي الرمز symbol.
assert_raises( exception1, exception2, ... ) { block }‎ يتأكّد من أنّ الكتلة block ترمي إحدى الاستثناءات exception1‏،‎‎‎‎‎exception2‎ ‎‏‏‎‎، ....
assert_instance_of( class, obj, [msg] )‎ يتأكّد من أنّ obj نسخة من الصّنف class.
assert_not_instance_of( class, obj, [msg] )‎ يتأكّد من أنّ obj ليس نسخة من الصّنف class.
assert_kind_of( class, obj, [msg] )‎ يتأكّد من أنّ obj نسخة من الصّنف class أو منُحدرٌ منه.
assert_not_kind_of( class, obj, [msg] )‎ يتأكّد من أنّ obj ليس نسخة من الصّنف class ولا هو منُحدرٌ منه.
assert_respond_to( obj, symbol, [msg] )‎ يتأكّد من أنّ obj يستجيب لـ symbol.
assert_not_respond_to( obj, symbol, [msg] )‎ يتأكّد من أنّ obj لا يستجيب لـ symbol.
assert_operator( obj1, operator, [obj2], [msg] )‎ يتأكّد من أنّ قيمة obj1.operator(obj2)‎ هي true.
assert_not_operator( obj1, operator, [obj2], [msg] )‎ يتأكّد من أنّ قيمة obj1.operator(obj2)‎ هي false.
assert_predicate ( obj, predicate, [msg] )‎ يتأكّد من أنّ قيمة obj.predicate‎ هي true، مثلًا: assert_predicate str, :empty?‎.
assert_not_predicate ( obj, predicate, [msg] )‎ يتأكّد من أنّ قيمة obj.predicate‎ هي false، مثلًا: assert_not_predicate str, :empty?‎.
flunk( [msg] )‎ يتأكّد من الفشل. يساعد هذا على وسم اختبارٍ لم يكتمل بعد.

ما سبق مجموعة من التحقّقات التي تدعمها minitest. للاطّلاع على القائمة الشّاملة والأحدث، يُرجى التوجّه إلى توثيق الواجهة البرمجية للمكتبة Minitest, وبالأخصّ Minitest::Assertions.

نظرًا لطبيعة إطار الاختبارات المبنيّة على الوحدات (modular)، من الممكن أن تُنشئ تحقُّقاتٍ خاصّة بك، وهذا بالضبط ما يفعله ريلز حيث يضمّ العديد من التحقّقات الخاصّة التي تسهّل لك الكثير من الأمور.    

ملاحظة: يُعدّ إنشاء تحقّقاتك الخاصّة موضوعًا مُتقدّما لن يُتطرّق إليه في هذا الدليل.

التحقّقات الخاصّة بريلز.

يضيف ريلز عدّة تحقّقات خاصّة به لإطار عمل minitest:

التحقّق الغرض
assert_difference(expressions, difference = 1, message = nil) {...}‎ يتحقّق من الاختلاف العددي بين القيمة المعادة للتعبير expressions وبين النتيجة المقيَّمة في الكتلة المعطاة.
assert_no_difference(expressions, message = nil, &block)‎ اختبار الناتج العددي لتقييم التعبير expressions والتأكد من عدم تغيره قبل وبعد تمريره إلى الكتلة.
assert_changes(expressions, message = nil, from:, to:, &block)‎ التحقق من تغيّر ناتج تقييم التعبير expressions بعد تمريره إلى الكتلة.
assert_no_changes(expressions, message = nil, &block)‎ التحقق من عدم تغير تقييم التعبير expressions بعد تمريره إلى الكتلة.
assert_nothing_raised { block }‎ التأكد من عدم رمي الكتلة المعطاة استثناءً.
assert_recognizes(expected_options, path, extras={}, message=nil)‎ اختبار عملية إعادة التوجيه للمسار المعطى والتأكد من أنها نُفِّذت بشكل صحيح وأن الخيارات المحللة (المعطاة في expected_options الذي هو جدول Hash) تطابق المسار. بشكل أساسي، يتأكد هذا الاختبار من تعرف ريلز مسار التوجيه المعطى عبر expected_options.
assert_generates(expected_path, options, defaults={}, extras = {}, message=nil)‎ اختبار الخيارات المعطاة والتأكد من إمكانية استعمالها لتوليد المسار المعطى. هذا الاختبار هو عكس الاختبار assert_recognizes.  تستعمل المعاملات الإضافية لإخبار الطلب بأسماء وقيم معاملات الطلب الإضافية التي ستكون في سلسلة الاستعلام. يسمح المعامل message بتحديد رسالة خطأ مخصصة لعرضها عند فشل الاختبار.
assert_response(type, message = nil)‎ اختبار الرد (response) والتأكد من احتوائه عىل رمز حالة محدد. يمكنك أن تحدد ‎:success ليشير إلى المجال 200-299، أو ‎:redirect ليشير إلى المجال 300-399، أو ‎:missing ليشير إلى 404، أو ‎:error ليشير إلى المجال 500-599. يمكنك أيضًا تمرير رقم حالة مجرد أو رمزه المقابل. لمزيد من المعلومات، اطلع على قائمة رموز الحالة الكاملة وكيفية إعادة تعيينها.
assert_redirected_to(options = {}, message=nil)‎ اختبار خيارات إعادة التوجيه الممررة والتأكد من مطابقتها لتلك المُمرَّرة إلى عملية إعادة التوجيه المستدعاة في أحدث إجراء. يمكن أن تكون هذه المطابقة جزئية مثل assert_redirected_to(controller: "weblog")‎ الذي سيطابق أيَضًا إعادة التوجيه redirect_to(controller: "weblog", action: "show")‎ وهلم جرًا. يمكنك أيضًا تمرير مسارات موجهة مسماة مثل assert_redirected_to root_path وكائنات Active Record مثل assert_redirected_to @article.

ستتطرّق إلى استعمالات بعض هذه التحقّقات في الفصل القادم.

ملاحظة وجيزة حول حالات الاختبار

جميع التحقّقات الأساسيّة المُعرّفة في Minitest::Assertions مُتاحة أيضًا في الأصناف التي نستعملها في حالات الاختبار الخاصّة بنا. يُوفّر لك ريلز الأصناف الآتية لترِث منها:

تتضمّن كلّ هذه الأصناف Minitest::Assertions ممّا يُمكّننا من استعمال جميع التحقّقات الأساسيّة في الاختبارات الخاصّة بنا.

ملاحظة: لمزيد من المعلومات حول Minitest، يُرجى الرّجوع إلى التوثيق الخاصّ بها.

منفذ الاختبارات في ريلز

من الممكن تنفيذ جميع الاختبارات دفعة واحدة باستعمال الأمر bin/rails test. كما يمكننا أن ننفّذ اختبارًا واحدًا من خلال تمرير اسم الملفّ الذي يحوي حالات الاختبار للأمر bin/rails test.

$ bin/rails test test/models/article_test.rb
Run options: --seed 1559
 
# Running:
 
..
 
Finished in 0.027034s, 73.9810 runs/s, 110.9715 assertions/s.
 
2 runs, 3 assertions, 0 failures, 0 errors, 0 skips

سيُنفّذ هذا كلّ توابع الاختبار في حالة الاختبار هذه. يُمكن أيضًا تنفيذ تابع اختبار معيّن في حالة الاختبار من خلال إعطاء الراية ‎-n‎‎‎ أو ‎--name‎ بالإضافة إلى اسم تابع الاختبار.

$ bin/rails test test/models/article_test.rb -n test_the_truth
Run options: -n test_the_truth --seed 43583
 
# Running:
 
.
 
Finished tests in 0.009064s, 110.3266 tests/s, 110.3266 assertions/s.
 
1 tests, 1 assertions, 0 failures, 0 errors, 0 skips

يُمكنك أيضًا تنفيذ سطرٍ معيّن من الاختبار وذلك بإعطاء رقمه:

$ bin/rails test test/models/article_test.rb:6 # تنفيذ سطر محدد من الاختبار

بالإضافة إلى ذلك، يُمكنك تنفيذ مجلّد كامل من الاختبارات من خلال إعطاء المسار إلى هذا المجلّد:

$ bin/rails test test/controllers # تنفيذ جميع الاختبارات الواقعة في مجلد محدد

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

$ bin/rails test -h
minitest options:
    -h, --help                       Display this help.
    -s, --seed SEED                  Sets random seed. Also via env. Eg: SEED=n rake
    -v, --verbose                    Verbose. Show progress processing files.
    -n, --name PATTERN               Filter run on /regexp/ or string.
        --exclude PATTERN            Exclude /regexp/ or string from run.
 
Known extensions: rails, pride
 
Usage: bin/rails test [options] [files or directories]
You can run a single test by appending a line number to a filename:
 
    bin/rails test test/models/user_test.rb:27
 
You can run multiple files and directories at the same time:
 
    bin/rails test test/controllers test/integration/login_test.rb
 
By default test failures and errors are reported inline during a run.
 
Rails options:
    -w, --warnings                   Run with Ruby warnings enabled
    -e, --environment                Run tests in the ENV environment
    -b, --backtrace                  Show the complete backtrace
    -d, --defer-output               Output test failures and errors after the test run
    -f, --fail-fast                  Abort test run on first failure or error
    -c, --[no-]color                 Enable color in the output

قاعدة بيانات الاختبارات

لا يكاد يكون تطبيق ريلز إلّا وله تفاعل كبير مع قاعدة بيانات، لذا فلابدّ لاختباراتك من قاعدة بيانات لتتفاعل معها كذلك. لكتابة اختبارات فعّالة، عليك أن تفهم كيفيّة تهيئة قاعدة البيانات هذه وملئها بعيّنات من البيانات.

لكلّ تطبيق ريلز ثلاث بيئات افتراضيّة: بيئة التطوير، بيئة الاختبار، وبيئة الإنتاج. تُضبط قاعدة البيانات لكلٍّ من هذه البيئات في الملف config/database.yml.

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

صيانة مخطط قاعدة بيانات الاختبار

لتنفيذ الاختبارات الخاصّة بك، يجب أن تكون قاعدة البيانات ذات بنية حالّية. يبحث مساعد الاختبار عمّا إذا كان لقاعدة البيانات تهجيرات معلّقة، وذلك بمحاولة تحميل db/schema.rb أو db/structure.sql الخاصّين بك في قاعدة البيانات. فإن كانت هناك تهجيرات لا تزال معلّقة، يتمّ إطلاق خطأ. يدلّ هذا عادةً على أنَّ المخطّط الخاصّ بك لم يُهجّر كليّةً، ويمكن تحديثه عن طريق إجراء التهجيرات مقابل قاعدة بيانات التطوير (عبر الأمر bin/rails db:migrate).    

ملاحظة: إذا أُحدِثت تغييرات على تهجيرات حاليّة، فلابد من إعادة بناء قاعدة البيانات. يمكن فعل ذلك عن طريق تنفيذ الأمر bin/rails db:test:prepare.

حقيقة تحضيرات الاختبار (Fixtures)

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

ما هي معدات الاختبار؟

تحضيرات الاختبار (Fixtures) هو إطلاق مزخرف على "عيّنات البيانات" (sample data). تمكّنك تحضيرات الاختبارات (Fixtures) من تعمير قاعدة بيانات الاختبار ببيانات معرّفة مسبقًا قبل تنفيذ الاختبار. تُكتب التحضيرات بلغة YAML و تكون مستقلّة عن قاعدة البيانات. يوجد هناك ملفّ واحد لكلّ نموذج.

ملاحظة: ليست التحضيرات (Fixtures) مصمَّمة لإنشاء جميع الكائنات التي تحتاجها اختباراتك، وتكون إدارتها أفضل عندما تُستعمل مع البيانات الافتراضيّة التي يمكن تطبيقها في الحالة الشائعة.

بإمكانك إيجاد تحضيرات الاختبار (Fixtures) في المجلّد test/fixtures. عندما تنفّذ الأمر rails generate model لإنشاء نموذج جديد، يُنشئ ريلز بذورًا للحضيرات (fixture stubs) في هذا المجلّد.

YAML

تعتبر التحضيرات المكتوبة بصياغة YAML طريقةً لتمثيل عيّنات البيانات على نحوٍ يفهمه البشر. يكون لهذا النوع التحضيرات الامتداد ‎.yml (كما في users.yml).

إليك عيّنة من ملفّ تحضيرات مكتوب بصياغة YAML:

# lo & behold! I am a YAML comment!
david:
  name: David Heinemeier Hansson
  birthday: 1979-10-15
  profession: Systems development
 
steve:
  name: Steve Ross Kellock
  birthday: 1974-09-27
  profession: guy with keyboard

يُعطى لكلّ تحضير (Fixture) اسمًا متبوعًا بقائمة مزاحة للأزواج مفتاح/قيمة المفصولة عن بعضها بنقطتين رأسيّتين. تُفصل السجلّات عادة بسطر فارغ. بإمكانك وضع تعليقات في ملفّ التحضيرات (Fixture) باستخدام المحرف # كما في السطر الأوّل. إذا كنت تستخدم الارتباطات، يمكنك تعريف عقدة مرجعيّة بين كلّ تحضيرين مختلفتين. إليك مثالًا بارتباط من النوع belongs_to/has_many:

# In fixtures/categories.yml
about:
  name: About
 
# In fixtures/articles.yml
first:
  title: Welcome to Rails!
  body: Hello world!
  category: about

لاحظ أنّ قيمة المفتاح category للمقال first الموجود في fixtures/articles.yml هي about. يطلب هذا من ريلز تحميل الصنف about الموجود في fixtures/categories.yml.

ملاحظة: لتمكين الارتباطات من الإشارة إلى بعضها البعض بالاسم، يمكنك استعمال اسم التحضير (Fixture) بدل تحديد الخاصية id:‎ في التحضيرات المرتبطة بها. يعيّين ريلز بشكلٍ آليّ مفتاحًا رئيسيًّا حتى يضمن التناسق بين جميع التنفيذات. لمزيد من المعلومات حول تصرّف الارتباطات هذا، يرجى الاطلاع على توثيق الواجهة البرمجية للتحضيرات.

وجود ERB

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

<% 1000.times do |n| %>
user_<%= n %>:
  username: <%= "user#{n}" %>
  email: <%= "user#{n}@example.com" %>
<% end %>

التحضيرات في موقع العمل

يحمّل ريلز تلقائيًا جميع التحضيرات (Fixtures) من المجلّد test/fixtures بشكل افتراضي، وتتكوّن عملية التحميل من ثلاثة خطوات:

  1. إزالة أي بيانات موجودة من الجدول المقابل للتحضير (Fixture).
  2. تحميل بيانات التحضير إلى الجدّول.
  3. تفريغ بيانات التحضير إلى تابع في حالة كنت ترغب في الوصول إليه بشكل مباشر.

ملاحظة: لإزالة البيانات الموجودة في قاعدة البيانات، يحاول ريلز تعطيل مشغلات السلامة المرجعية (referential integrity triggers، مثل المفاتيح الخارجيّة وقيود التحقّق)، وإذا حصلت على أخطاء الأذونات في اختبارات التشغيل، فتأكد من امتلاك مستخدم قاعدة البيانات على امتياز تعطيل هذه المشغلات في بيئة الاختبار. (في PostgreSQL، يمكن للمستخدمين المميزين فقط تعطيل جميع المشغلات، اقرأ المزيد عن أذونات PostgreSQL هنا.)

التحضيرات هي كائنات Active Record

إن التحضيرات (Fixtures) هي أمثلة عن Active Record؛ وكما ذكرنا في النقطة الثالثة أعلاه، يمكنك الوصول إلى الكائن مباشرةً لأنه متوفّر تلقائيًا كتابع حيث أن نطاقه محلي هو حالة الاختبار. فمثلًا:

# david للتحضير الذي يدعى User هذا سيعيد الكائن
users(:david)
 
# david للتحضير it هذا سيعيد الخاصية
users(:david).id
 
# عبر User يمكن الوصول إلى التوابع الموجودة في الصنف
david = users(:david)
david.call(david.partner)

للحصول على تحضيرات متعددة في وقت واحد، يمكنك تمرير قائمة من أسماء التحضيرات مثل:

# david و steve هذا سيعيد مصفوفة تحوي التحضيرين
users(:david, :steve)

اختبار النموذج

يهدف اختبار النماذج لاختبار نماذج مختلفة من تطبيقك الخاص.

تُخزّن اختبارات نماذج ريلز في المجلّد test/models ويوفّر لك ريلز مولّدًا لإنشاء هيكل اختبار النموذج:

$ bin/rails generate test_unit:model article title:string body:text
create  test/models/article_test.rb
create  test/fixtures/articles.yml

لا تملك اختبارات النموذج صنفًا فرعيًا (superclass) خاصًّا بها مثل ActionMailer::TestCase؛ وبدلًا من ذلك، ترث من ActiveSupport::TestCase.

اختبار النظام

تسمح لك اختبارات النظام (system tests) باختبار تفاعلات المستخدم مع تطبيقك، أو تشغيل الاختبارات في متصفح حقيقي أو في متصفّح بدون رأس (headless browser، أي متصفح ويب ولكن بدون واجهة المستخدم الرسومية)؛ ومن الداخل، تستخدم اختبارات النظام Capybara.

لإنشاء اختبارات نظام ريلز، يمكنك استخدام المجلّد test/system في تطبيقك، وسيوفّر ريلز مولّدًا لإنشاء هيكل اختبار النظام:

$ bin/rails generate system_test users
      invoke test_unit
      create test/system/users_test.rb

وهذا ما سيبدو عليه اختبار نظام أُنشئ حديثًا:

require "application_system_test_case"
 
class UsersTest < ApplicationSystemTestCase
  # test "visiting the index" do
  #   visit users_url
  #
  #   assert_selector "h1", text: "Users"
  # end
end

تعمل اختبارات النظام بشكل افتراضي باستخدام المشغّل Selenium والمتصفح Chrome وشاشة بحجم 1400x1400، ويوضح القسم التالي كيفيّة تغيير الإعدادات الإفتراضيّة.

تغيير الإعدادات الافتراضية

يجعل ريلز عمليّة تغيير الإعدادات الافتراضيّة لاختبارات النظام بسيطة للغاية، فاستُخرجت جميع الإعدادات بعيدا حتى تتمكن من التركيز على كتابة الاختبارات الخاصة بك.

يُنشئ ملف جديد باسم application_system_test_case.rb في المجلد test عند إنشاء تطبيق جديد أو توليد تطبيق عبر scaffold، وهنا ستكون جميع إعدادات نظام الاختبارات موجودة.

إذا رغبّت بتغيير الإعدادات الافتراضيّة، يمكنك تغيير مشغّل نظام الاختبارات؛ فلنفترض أنك ترغب في تغيير المشغل من Selenium إلى Poltergeist، يجب عليك أولًا إضافة الجوهرة poltergeist إلى Gemfile ومن ثم، عّدل على الملف application_system_test_case.rb بالشكل التالي:

require "test_helper"
require "capybara/poltergeist"
 
class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
  driven_by :poltergeist
end

اسم المشغل هو معامل إجباري من أجل driven_by، ويمكنك إضافة المعاملات الاختيارية التالية:

  • :using: للمتصفح (يُستخدم فقط مع Selenium)، و
  • :screen_size: لتغيير حجم الشاشة للقطات الشاشة (Screenshots)، و
  • ‎:options: لتحديد وضبط الخيارات المدعومة من قبل المشغّل.
require "test_helper"
 
class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
  driven_by :selenium, using: :firefox
end

إذا أردت استخدام متصفح بلا رأس (headless browser)، فيمكنك استخدام متصفح Chrome بلا رأس أو متصفح Firefox بلا رأس وذلك عن طريق إضافة headless_chrome أو headless_firefox في المعامل :using:

require "test_helper"
 
class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
  driven_by :selenium, using: :headless_chrome
end

إذا كانت إعدادات Capybara تتطلّب ضبطًا أكثر مما توفره ريلز، فيجب إضافة هذه الإعدادات الإضافيّة إلى الملف application_system_test_case.rb.

يرجى الإطلاع على توثيق Capybara للاطلاع على الإعدادات الإضافيّة.

مساعد إلتقاط الشاشة

إنَّ ScreenshotHelper هو مساعد مصمم لالتقاط لقطات من الاختبارات الخاصة بك، وقد يكون ذلك مفيدًا في عرض المتصفح عند فشل الاختبار، أو لمشاهدة لقطات الشاشة لاحقًا لتصحيح الأخطاء.

هنالك تابعين هما: take_screenshot و take_failed_screenshot، وهذا الأخير موجود تلقائيًا في after_teardown داخل ريلز.

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

تنفيذ اختبار النظام

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

إذا استخدمت المولّد scaffold، فسيُنشئ لك هيكل اختبار للنظام تلقائيًا؛ وإذا لم تستخدمه، فابدأ بإنشاء هيكل اختبار للنظام عبر الأمر التالي:

$ bin/rails generate system_test articles

سيُنشئ لنا هذا ملف اختبار جاهز، وستكون هذه مخرجات الأمر السابق:

invoke  test_unit
create    test/system/articles_test.rb

دعونا الآن نفتح هذا الملف ونكتب اختبارنا (assertion) الأول:

require "application_system_test_case"
 
class ArticlesTest < ApplicationSystemTestCase
  test "viewing the index" do
    visit articles_path
    assert_selector "h1", text: "Articles"
  end
end

يجب أن يبحث الاختبار على العنوان h1 الموجود في صفحة المقالات الرئيسية ويمرره. شغّل الآن اختبارات النظام:

bin/rails test:system

ملاحظة: بشكل افتراضي، لن يشغّل الأمر bin/rails test اختبارات النظام الخاصة بك، لذا تأكد من تنفيذ bin/rails test:system لتشغيلها.

إنشاء اختبار نظام للمقالات

دعونا الآن نختبر سير عملية إنشاء مقال جديد في مدونتنا:

test "creating an article" do
  visit articles_path
 
  click_on "New Article"
 
  fill_in "Title", with: "Creating an Article"
  fill_in "Body", with: "Created this article successfully!"
 
  click_on "Create Article"
 
  assert_text "Creating an Article"
end

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

بعد ذلك، سيجد click_on "New Article"‎ الزر "New Article" في صفحة الرئيسية. وسيؤدي هذا إلى إعادة توجيه المتصفح إلى ‎/articles/new.

بعد ذلك سيكتب الاختبار عنوانًا ونصًا للمقال في المكان المحدد، وسينقر، بمجرّد ملء الحقول، على  "Create Article" لسيُرسَل طلب POST لإنشاء مقال جديد في قاعدة البيانات.

سيُعاد توجيهنا إلى صفحة فهرس المقالات (الصفحة الرئيسية) وهنالك سنتأكد من أن المحتوى النصي لعنوان المقال الجديد موجود في صفحة فهرس المقالات.

الغوص أكثر

يكمن جمال اختبار النظام في أنه يشبه اختبار التكامل (integration testing) من حيث أنه يختبر تفاعل المستخدم مع المتحكم، والنموذج، والعرض، ولكن اختبار النظام أكثر قوّة منه ويختبر التطبيق فعليًَا كما لو كان المستخدم الحقيقي يستخدمه؛ ويمكِّنك فعل أكثر من ذلك، إذ يمكنك من اختبار ما يمكن للمستخدم نفسه فعله عند استخدام تطبيقك مثل التعليق، حذف المقالات، نشر المقالات، ...إلخ.

اختبار التكامل

تُستخدم اختبارات التكامل (Integration tests) لاختبار كيّفيّة تفاعل أجزاء مختلفة من تطبيقك، وتُستخدم بشكل عام لاختبار سير العمل المهم داخل التطبيق.

لإنشاء اختبارات التكامل في ريلز، نستخدم المجلّد test/integration لتطبيقنا، ويوفّر ريلز مولدًا لإنشاء هيكل اختبار تكامل لنا.

$ bin/rails generate integration_test user_flows
      exists  test/integration/
      create  test/integration/user_flows_test.rb

هذا ما سيبدو عليه اختبار تكامل أنشئ حديثًا:

require 'test_helper'
 
class UserFlowsTest < ActionDispatch::IntegrationTest
  # test "the truth" do
  #   assert true
  # end
end

يرث الاختبار من ActionDispatch::IntegrationTest، ويتيح هذا لنا بعض المساعدين الإضافيين لاستخدامهم في اختبارات التكامل الخاصة بنا.

المساعدين المتاحين لاختبارات التكامل

بالإضافة إلى مساعدي الاختبارات القياسيّة، تأتي الوراثة من ActionDispatch::IntegrationTest مع بعض المساعدين الإضافيين المتوفرين عند كتابة اختبارات التكامل، دعونا نقدم لك باختصار الفئات الثلاثة التي يجب أن نختار منهم:

  1. للتعامل مع مشغل (runner) اختبار التكامل، راجع ActionDispatch::Integration::Runner.
  2. عند تنفيذ الطلبات، سيتاح لنا ActionDispatch::Integration::RequestHelpers للاستخدام.
  3. إذا أردت تعديل الجلسة، أو حالة اختبار التكامل، فألقي نظّرة على ActionDispatch::Integration::Session لمساعدتك.

تنفيذ اختبار التكامل

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

سنبدأ بتوليد هيكل اختبار تكامل خاص بنا:

$ bin/rails generate integration_test blog_flow

سينشئ لنا هذا ملف الاختبار، وسيخّرج لنا الأمر السابق ما يلي:

invoke  test_unit
create    test/integration/blog_flow_test.rb

الآن، دعونا نفتح هذا الملف ونكتب اختبارنا (assertion) الأول:

require 'test_helper'
 
class BlogFlowTest < ActionDispatch::IntegrationTest
  test "can see the welcome page" do
    get "/"
    assert_select "h1", "Welcome#index"
  end
end

سنلقي نظّرة على assert_select للاستعلام عن شيفرة HTML الناتجة عن طلب في قسم "Testing Views" الموجود في الأسفل؛ ويُستخدم لاختبار استجابة طلبنا من خلال التأكيد على وجود عناصر HTML الأساسيّة ومحتوياتها.

عندما نزور مسارنا الجذر، ينبغي أن نرى الملف welcome/index.html.erb مصيَّرًا للعرض، ولذلك يجب أن يتحقق هذا التأكيد.

إنشاء تكاملات المقالات

ما رأيك أن نختبر قدرتنا على إنشاء مقالة جديدة في مدونتنا والإطلاع على المقالة الناتجة؟ إليك الشيفرة التالية:

test "can create an article" do
  get "/articles/new"
  assert_response :success
 
  post "/articles",
    params: { article: { title: "can create", body: "article successfully." } }
  assert_response :redirect
  follow_redirect!
  assert_response :success
  assert_select "p", "Title:\n  can create"
end

دعونا نقسّم هذا الاختبار حتى نفهمه.

نبدأ باستدعاء الإجراء :new على المتحكم Articles، ويجب أن تكون نتيجة هذه الاستجابة ناجحة.

بعد ذلك، نرسل طلبًا للنشر (post) إلى الإجراء ‎:create للمتحكم Articles:

post "/articles",
  params: { article: { title: "can create", body: "article successfully." } }
assert_response :redirect
follow_redirect!

السطران التاليان للطلب هما للتعامل مع عملية إعادة التوجيه التي قمنا بإعدادها عند إنشاء مقالة جديدة.

ملاحظة: لا تنس أن تستدعي follow_redirect!‎ إذا كنت تخطط لتقديم طلبات لاحقة بعد إجراء إعادة التوجيه.

وأخيرًا، يمكننا التأكيد على أن ردنا كان ناجحًا وأن مقالتنا الجديدة قابلة للقراءة على الصفحة.

الغوص أكثر

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

الاختبارات الوظيفية لوحدات التحكم الخاصة بنا

في ريلز، اختبار الإجراءات المختلفة للمتحكم هو شكل من أشكال كتابة الاختبارات الوظيفيّة؛ تذكر أن وحدات التحكم الخاصة بك تتعامل مع طلبات الويب الواردة إلى التطبيق الخاص بك وتستجيب في النهاية مع عرض مصيَّر (rendered view). وعند كتابة الاختبارات الوظيفيّة، فإنك تختبر كيف تتعامل الإجراءات مع الطلبات والنتائج المتوقعة أو الاستجابة وفي بعض الحالات عرض شيفرة HTML.

ما يجب تضمينه في اختباراتك الوظيفية

يجب عليك اختبار أشياء مثل:

  • هل كان طلب الويب ناجحًا؟
  • هل أُعيد توجيه المستخدم إلى الصفحة الصحيحة؟
  • هل تمت مصادقة المستخدم بنجاح؟
  • هل كان الكائن الصحيح مخزّن في قالب الاستجابة؟
  • هل كانت الرسالة المناسبة معروضة للمستخدم في العرض؟

أسهل طريقة لرؤية الاختبارات الوظيفيّة في العمل هي إنشاء وحدة تحكم باستخدام المولّد scaffold:

$ bin/rails generate scaffold_controller article title:string body:text
...
create  app/controllers/articles_controller.rb
...
invoke  test_unit
create    test/controllers/articles_controller_test.rb
...

سيُنشئ هذا الشيفرة البرمجيّة للمتحكم والاختبارات للمورد Article؛ يمكنك إلقاء نظرة  على الملف articles_controller_test.rb في المجلد test/controllers. إذا كان لديك متحكمًا بالفعل وترغب فقط في إنشاء شيفرة اختبار scaffold لكل إجراء من إجراءات السبعة الافتراضيّة، فيمكنك استخدام الأمر التالي:

$ bin/rails generate test_unit:scaffold article
...
invoke  test_unit
create    test/controllers/articles_controller_test.rb
...

لنقِ نظرة على اختبار واحد من هذا القبيل وهو test_should_get_index من الملف articles_controller_test.rb:

# articles_controller_test.rb
class ArticlesControllerTest < ActionDispatch::IntegrationTest
  test "should get index" do
    get articles_url
    assert_response :success
  end
end

في الاختبار test_should_get_index، يحاكي ريلز طلبًا على الإجراء index مع التأكد من نجاح الطلب وضمان إنشاء جسم (body) الاستجابة الصحيح أيضًا.

يحصل التابع get على طلب الويب ويعبئ النتائج في ‎@response، ويقبل هذا 6 معاملات:

  • عنوان URI: لإجراء المتحكم الذي تطلبه، ويمكن أن يكون هذا على شكل سلسلة نصيّة أو مساعد مسار توجيه (على سبيل المثال articles_url).
  • Params: خيار مع جدول hash من معاملات الطلب لتمريرها إلى الإجراء (على سبيل المثال معاملات سلسلة النصية للطلب أو متغيرات المقال).
  • headers: لتعيين الرؤوس التي ستمرّر مع الطلب.
  • env: لتخصيص بيئة الطلب حسب الحاجة.
  • xhr: مهما كان الطلب هو طلب Ajax أو لا، فيمكنك تعيين هذا الخيار إلى القيمة true لجعل الطلب هو طلب Ajax.
  • as: لتشفير الطلب بنوع محتوى مختلف، وهو يدعم ‎:json بشكل افتراضي.

جميع معاملات الكلمات الرئيسيّة هذه اختياريّة.

مثال: استدعاء الإجراء ‎:show للمقال الأول، وتمرير الترويسة HTTP_REFERER:

get article_url(Article.first), headers: { "HTTP_REFERER" => "http://example.com/home" }

مثال آخر: استدعاء الإجراء ‎:update للمقال الأخير، وتمرير نص جديد للعنوان في params، وسنجعله طلب Ajax:

patch article_url(Article.last), params: { article: { title: "updated" } }, xhr: true

ملاحظة: إذا حاولت تشغيل اختبار test_should_create_article من articles_controller_test.rb، فستفشل هذه بسبب التحقق من مستوى النموذج الذي أضيف حديثًا. لنعدّل اختبار test_should_create_article في articles_controller_test.rb لتتحقق جميع اختباراتنا:

test "should create article" do
  assert_difference('Article.count') do
    post articles_url, params: { article: { body: 'Rails is awesome!', title: 'Hello Rails' } }
  end
 
  assert_redirected_to article_path(Article.last)
end

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

request.headers['Authorization'] = ActionController::HttpAuthentication::Basic.
  encode_credentials('dhh', 'secret')

أنواع الطلبات المتاحة للاختبارات الوظيفية

إذا كنت معتادًا على بروتوكول HTTP، فستعرف أن get هو نوع من أنواع الطلبات، وهنالك 6 أنواع طلبات مدعومة في اختبارات ريلز الوظيفيّة هي:

  • get
  • post
  • patch
  • put
  • head
  • delete

تحتوي جميع أنواع الطلبات على توابع مكافئة يمكنك استخدامها. وستستعمل get و post و put و delete كثيرًا في تطبيق C.R.U.D العادي.

ملاحظة: لا تتحقّق الاختبارات الوظيفيّة مما إذا كان نوع الطلب المحدد مقبولًا من قبل الإجراء أو لا، فنحن مهتمون أكثر بالنتيجة؛ توجد اختبارات طلب لهذه الحالة لجعل اختباراتك أكثر إفادة.

اختبارات طلبات AJAX) XHR)

لاختبار طلبات AJAX، يمكنك تحديد الخيار xhr: true للتوابع get، و post، و patch، و put، و delete. فعلى سبيل المثال:

test "ajax request" do
  article = articles(:one)
  get article_url(article), xhr: true
 
  assert_equal 'hello world', @response.body
  assert_equal "text/javascript", @response.content_type
end

ثلاثة جداول Hash

بعد إجراء الطلب ومعالجته، سيكون لدينا ثلاثة كائنات من Hash جاهزة للاستخدام هي:

  • cookies: لملفات تعريف الارتباط التي عيّناها.
  • flash: لأي كائنات موجودة في flash.
  • session: لأي كائنات موجودة في متغيرات الجلسة.

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

flash["gordon"]               flash[:gordon]
session["shmession"]          session[:shmession]
cookies["are_good_for_u"]     cookies[:are_good_for_u]

متغيرات النسخة المتاحة

يمكنك أيضًا الوصول إلى ثلاثة متغيّرات نسخة في اختباراتك الوظيفيّة بعد إجراء الطلب هي:

  • ‎@controller: المتحكم المعالج للطلب.
  • ‎@request: كائن الطلب.
  • ‎@response: كائن الاستجابة.
    class ArticlesControllerTest < ActionDispatch::IntegrationTest
      test "should get index" do
        get articles_url
     
        assert_equal "index", @controller.action_name
        assert_equal "application/x-www-form-urlencoded", @request.media_type
        assert_match "Articles", @response.body
      end
    end
    

إعداد الترويسات ومتغيرات CGI

يمكن تمرير ترويسات HTTP ومتغيرات CGI كترويسات مثل:

# HTTP ضبط ترويسة
get articles_url, headers: { "Content-Type": "text/plain" } 
# محاكاة الطلب مع ترويسة مخصصة
 
# CGI ضبط متغير
get articles_url, headers: { "HTTP_REFERER": "http://example.com/home" } 
# مخصص (env variable) محاكاة الطلب مع متغير بيئة

اختبار الاشعارات الوامضة

إذا كنت تتذكر، أحد الجداول hash الثلاثة آنفة الذكر كان flash.

نرغب الآن في إظهار رسالة وامضة (رسالة flash) إلى تطبيق المدونة كلما نجح أحدهم في إنشاء مقالة جديدة.

لنبدأ بإضافة هذا التأكيد إلى الاختبار test_should_create_article:

test "should create article" do
  assert_difference('Article.count') do
    post article_url, params: { article: { title: 'Some title' } }
  end
 
  assert_redirected_to article_path(Article.last)
  assert_equal 'Article was successfully created.', flash[:notice]
end

إذا أجرينا الاختبار الآن، فمن المفترض أن نرى رسالة الفشل:

$ bin/rails test test/controllers/articles_controller_test.rb -n test_should_create_article
Run options: -n test_should_create_article --seed 32266
 
# Running:
 
F
 
Finished in 0.114870s, 8.7055 runs/s, 34.8220 assertions/s.
 
  1) Failure:
ArticlesControllerTest#test_should_create_article [/test/controllers/articles_controller_test.rb:16]:
--- expected
+++ actual
@@ -1 +1 @@
-"Article was successfully created."
+nil
 
1 runs, 4 assertions, 1 failures, 0 errors, 0 skips

لننفّذ الرسالة الوامضة الآن على متحكمنا، إذ سيبدو الإجراء :create الآن كالتالي:

def create
  @article = Article.new(article_params)
 
  if @article.save
    flash[:notice] = 'Article was successfully created.'
    redirect_to @article
  else
    render 'new'
  end
end

والآن إذا أجرينا اختباراتنا، فستنجح:

$ bin/rails test test/controllers/articles_controller_test.rb -n test_should_create_article
Run options: -n test_should_create_article --seed 18981
 
# Running:
 
.
 
Finished in 0.081972s, 12.1993 runs/s, 48.7972 assertions/s.
 
1 runs, 4 assertions, 0 failures, 0 errors, 0 skips

جمع كل شيء معًا

في هذه المرحلة، يختبر متحكم مقالاتنا الإجراءات :index و :new و ‎:create. فماذا عن التعامل مع البيانات الموجودة؟

دعونا نكتب اختبار للإجراء :show:

test "should show article" do
  article = articles(:one)
  get article_url(article)
  assert_response :success
end

تذكر من مناقشتنا في وقت سابق عن تحضيرات الاختبار (Fixtures)، سيعطينا التابع ()articles إمكانيّة الوصول إلى تحضيرات مقالاتنا. فماذا عن حذف مقالة موجودة؟

test "should destroy article" do
  article = articles(:one)
  assert_difference('Article.count', -1) do
    delete article_url(article)
  end
 
  assert_redirected_to articles_path
end

يمكننا أيضًا إضافة اختبار لتحديث مقالة موجودة.

test "should update article" do
  article = articles(:one)
 
  patch article_url(article), params: { article: { title: "updated" } }
 
  assert_redirected_to article_path(article)
  # Reload association to fetch updated data and assert that title is updated.
  article.reload
  assert_equal "updated", article.title
end

لاحظ أننا بدأنا نرى بعض التكرارات في هذه الاختبارات الثلاثة، فهم يصلون إلى نفس بيانات تحضير (Fixture) المقالة، ويمكنك تجنب تكرار هذا (مبدأ لا تكرر نفسك [[[wikipedia:Don't_repeat_yourself|D.R.Y.‎]]]) عن طريق استخدام التابعين setup و teardown التي يوفّرها ActiveSupport::Callbacks. يجب أن يظهر اختبارنا كالتالي (تجاهل الاختبارات الأخرى في الوقت الحالي، فنحن نتناساهم للإيجاز):

require 'test_helper'
 
class ArticlesControllerTest < ActionDispatch::IntegrationTest
  # يستدعى قبل كل عملية اختبار فردية
  setup do
    @article = articles(:one)
  end
 
  # يستدعى بعد كل عملية اختبار فردية
  teardown do
    # فمن الأفضل إعادة ضبطها بعد ذلك ،cache عندما يستعمل المتحكم
    Rails.cache.clear
  end
 
  test "should show article" do
    # (setup) من الضبط @article إعادة استعمال متغير النسخة
    get article_url(@article)
    assert_response :success
  end
 
  test "should destroy article" do
    assert_difference('Article.count', -1) do
      delete article_url(@article)
    end
 
    assert_redirected_to articles_path
  end
 
  test "should update article" do
    patch article_url(@article), params: { article: { title: "updated" } }
 
    assert_redirected_to article_path(@article)
    # إعادة تحميل الاختبار لجلب البيانات المحدثة والتأكد من تحديث العنوان
    @article.reload
    assert_equal "updated", @article.title
  end
end

وعلى غرار الاستدعاءات الأخرى في ريلز، يمكن أيضًا استخدام التابعين setup و teardown عن طريق تمرير كتلة، أو تعبير lambda، أو اسم تابع كرمز للاستدعاء.

مساعدي الاختبار

لتجنب تكرار الشيفرات البرمجيّة، يمكنك إضافة مساعدي الاختبار الخاصين بك،  ويمكن أن يكون مساعد تسجيل الدخول مثال جيد على ذلك:

# test/test_helper.rb
 
module SignInHelper
  def sign_in_as(user)
    post sign_in_url(email: user.email, password: user.password)
  end
end
 
class ActionDispatch::IntegrationTest
  include SignInHelper
end
require 'test_helper'
 
class ProfileControllerTest < ActionDispatch::IntegrationTest
 
  test "should show profile" do
    # أصبح المساعد الآن قابلًا لإعادة الاستعمال من أي حالة اختبار لمتحكم
    sign_in_as users(:david)
 
    get profile_url
    assert_response :success
  end
end

اختبار مسارات التوجيه

مثل كل شيء آخر في تطبيق ريلز، يمكنك اختبار مسارات التوجيه (routes) الخاصة بك، إذ تتوضع هذه الاختبارات في test/controllers/‎ أو ستكون جزءًا من اختبارات وحدة التحكم.

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

لمزيد من المعلومات حول تأكيدات التوجيه المتاحة في ريلز، راجع توثيق الواجهة البرمجية لـ ActionDispatch::Assertions::RoutingAssertions.

اختبار العروض

يعدّ اختبار الاستجابة لطلبك من خلال التأكيد على وجود عناصر HTML الأساسيّة ومحتواها طريقةً شائعةً لاختبار طرق عرض التطبيق. بشكل مشابه لاختبارات مسار التوجيه، تقع اختبارات العرض في test/controllers/‎ أو كجزء من اختبارات المتحكم. يسمح لك التابع assert_select الاستعلام عن عناصر HTML للاستجابة باستخدام صيغة بسيطة لكنها قويّة.

هنالك نوعا من assert_select:

  • assert_select(selector, [equality], [message])‎: يتأكد من استيفاء شرط المساواة على العناصر المحددة من خلال المحدّد (selector)، وقد يكون هذا الأخير عبارة عن تعبير محدِّد CSS (سلسلة نصيّة) أو تعبير يحتوي على قيم بديلة.
  • assert_select(element, selector, [equality], [message])‎: يتأكد من استيفاء شرط المساواة على كل العناصر المحددة من خلال المحدّد بدءًا من العنصر (نسخة من Nokogiri::XML::Node أو Nokogiri::XML::NodeSet) وأبنائه.

على سبيل المثال، يمكنك التحقق من محتويات عنصر العنوان في استجابتك باستخدام:

assert_select 'title', "Welcome to Rails Testing Guide"

يمكنك أيضًا استخدام كتل assert_select المتداخلة للتحقق بعمق أكبر. في المثال التالي، ينفَّذ assert_select الداخلي من أجل li.menu_item ضمن مجموعة العناصر المحدّدة بواسطة الكتلة الخارجيّة:

assert_select 'ul.navigation' do
  assert_select 'li.menu_item'
end

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

assert_select "ol" do |elements|
  elements.each do |element|
    assert_select element, "li", 4
  end
end
 
assert_select "ol" do
  assert_select "li", 8
end

هذا التأكيد قوي جدًا؛ لمزيد من الاستخدام المتقدم، راجع التوثيق الخاص به.

تأكيدات إضافية تستند إلى العرض

هنالك المزيد من التأكيدات التي تستخدم في المقام الأول في اختبار العروض:

التأكيد الغرض
assert_select_email يتيح لك تقديم تأكيدات على نص رسالة البريد الإلكتروني.
assert_select_encoded يتيح لك تقديم التأكيدات على شيفرة HTML مشفّرة، وذلك عن طريق إلغاء تشفير محتويات كل عنصر ومن ثم استدعاء الكتلة مع جميع العناصر غير المشفّرة.
css_select(selector)‎ أو css_select(element, selector)‎ يعيد مصفوفة بكافة العناصر المحّددة بواسطة المحددّ selector. في النوع الثاني، يطابق أولًا العنصر element الأساسي ويحاول مطابقة التعبير المحدّد selector على أحد أبنائه، وإذا لم يكن هنالك مطابقات، فسيعيد كلا المتغيريّن مصفوفة فارغة.

في ما يلي مثال على استخدام assert_select_email:

assert_select_email do
  assert_select 'small', 'Please click the "Unsubscribe" link if you want to opt-out.'
end

اختبار المساعدين

المساعد هو مجرّد وحدة بسيطة حيث يمكنك تعريف التوابع المتاحة في عروضك.

من أجل اختبار المساعدين، كل ما عليك القيام به هو التحقق من أن ناتج تابع المساعد يطابق ما تتوقعه، وتقع الاختبارات المتعلّقة بالمساعدين في المجلد test/helpers.

فإذا كان لدينا المساعد التالي:

module UsersHelper
  def link_to_user(user)
    link_to "#{user.first_name} #{user.last_name}", user
  end
end

فيمكننا اختبار ناتج هذا التابع كالتالي:

class UsersHelperTest < ActionView::TestCase
  test "should return the user's full name" do
    user = users(:david)
 
    assert_dom_equal %{<a href="/user/#{user.id}">David Heinemeier Hansson</a>}, link_to_user(user)
  end
end

علاوة على ذلك، نظرًا لأن الصنف test موسع (extend) من ActionView::TestCase، يمكنك الوصول إلى توابع مساعدة ريلز مثل link_to أو pluralize.

اختبار إجراء المراسلة الخاص بك

يتطلب اختبار الأصناف mailer بعض الأدوات المحدّدة للقيام بكامل المهمة.

الحفاظ على Postman في التحقق

يجب عليك اختبار أصناف Action Mailer  لتطبيق ريلز - مثل أي جزء آخر - للتأكد من عملها بالشكل المتوقّع.

الهدف من اختبار الأصناف mailer هو التأكد من:

  • معالجة رسائل البريد الإلكتروني (الإنشاء والإرسال).
  • أن يكون محتوى البريد الإلكتروني صحيحًا (الموضوع، والمُرسِل، والمحتوى، ...إلخ.)
  • إرسال رسائل البريد الإلكتروني الصحيحة في الوقت المناسب.

من جميع الجوانب

هنالك جانبان لاختبار البريد الإلكتروني، اختبارات الوحدة (unit tests) والاختبارات الوظيفيّة (functional tests). ففي اختبارات الوحدة، نشغّل المرسل (mailer) بعزله مع مدخلات متحكم بها ونقارن المخرجات مع قيمة معروفة (Fixture). وفي الاختبارات الوظيفيّة، لا تختبر الكثير من التفاصيل الدقيقة التي ينتجها المرسل؛ وبدلًا من ذلك، نختبر أن وحدات التحكم والنماذج الخاصة بنا تستخدم المرسل بالطريقة الصحيحة، ويكون هذا الاختبار هو إثبات إرسال البريد الإلكتروني الصحيح في الوقت المناسب.

اختبار الوحدة

من أجل اختبار ما إذا كان المرسل يعمل كما هو متوقّع، يمكنك استخدام اختبارات الوحدة لمقارنة النتائج الفعليّة للمرسل مع أمثلة مكتوبة مسبقًا لما يجب توليدها.

الثأر من تحضيرات الاختبار

لغرض اختبار وحدة الإرسال، تُستخدم التحضيرات (Fixtures) لتقديم مثال عن كيفيّة ظهور المخرجات.لمَّا كانت هذه هي رسائل بريد إلكتروني نموذجية (example emails) وليست بيانات Active Record كالتوابع الأخرى، يُحتفظ بها بصرف النظر عن التحضيرات (Fixtures) الأخرى. ويتطابق اسم المجلد الموجود في test/fixtures مع اسم المرسل، لذلك بالنسبة إلى المرسل الذي يحمل الاسم UserMailer، يجب أن يكون التحضير (Fixture) في المجلد test/fixtures/user_mailer.

إذا ولّدت مرسلًا، فلن يقوم المولّد بإنشاء بذرة للتحضيرات (Fixtures stub) التي تخص Action Mailer، ويجب عليك إنشاء هذه الملفات بنفسك كما هو موضّح أعلاه.

حالة الاختبار الأساسي

في ما يلي اختبار وحدة لاختبار مرسل يسمى UserMailer والذي يُستخدم الإجراء invite الخاص به لإرسال دعوة إلى صديق. إليك نسخة معدّلة من الاختبار الأساسي الذي أنشأه المولّد للإجراء invite:

require 'test_helper'
 
class UserMailerTest < ActionMailer::TestCase
  test "invite" do
    # إنشاء البريد الإلكتروني وتخزينه من أجل التحقق منه بشكل أوسع
    email = UserMailer.create_invite('me@example.com',
                                     'friend@example.com', Time.now)
 
    # إرسال البريد الإلكتروني ثم التأكد من أنه أصبح ضمن الطابور
    assert_emails 1 do
      email.deliver_now
    end
 
    # اختبار محتوى البريد الإلكتروني المرسل من مطابقته لتوقعاتنا
    assert_equal ['me@example.com'], email.from
    assert_equal ['friend@example.com'], email.to
    assert_equal 'You have been invited by me@example.com', email.subject
    assert_equal read_fixture('invite').join, email.body.to_s
  end
end

في هذا الاختبار، أرسلنا بريدًا إلكترونيًّا وخزّنا الكائن المعادة في المتغيّر email، ثم تأكدنا من إرساله (التأكيد الأول)، ومن ثم، في الدفعة الثانية من التأكيدات، تأكدنا من أن البريد الإلكتروني يحتوي بالفعل على ما نتوقعه. واستخدمنا المساعد read_fixture لقراءة محتوى هذا الملف.

ملاحظة: سيتواجد email.body.to_s عند وجود جزء واحد فقط (شيفرة HTML أو نص)؛ فإذا كان الإرسال يوفر الإثنيّن، يمكنك اختيار التحضير (Fixture) الخاص بك على أجزاء معيّنة باستخدام email.text_part.body.to_s أو email.html_part.body.to_s.

إليك محتوى التحضير invite:

Hi friend@example.com,
 
You have been invited.
 
Cheers!

هذا هو الوقت المناسب لفهم المزيد عن كتابة اختبارات Action Mailer الخاص بك. فالسطر ActionMailer::Base.delivery_method = :test في الملف config/environments/test.rb يضبط تابع التوصيل (delivery method) إلى وضع الاختبار (أي الوضع test) حتى لا تُسلّم الرسائل الإلكترونيّة بالفعل (هذا مفيد لتجنّب إرسال رسائل غير مرغوب فيها للمستخدمين أثناء الاختبار) ولكن بدلًا من ذلك سيضاف إلى مصفوفة (ActionMailer::Base.deliveries).

ملاحظة: يعاد تعيين المصفوفة ActionMailer::Base.deliveries تلقائيًا في الاختبارين ActionMailer::TestCase و ActionDispatch::IntegrationTest فقط؛ وإذا رغبت بالحصول على مصفوفة نظيفة خارج هذه الحالات التجريبيّة، يمكنك إعادة تعيينها يدويًا باستخدام ActionMailer::Base.deliveries.clear.

الاختبار الوظيفي واختبار النظام

يتضمن الاختبار الوظيفي لـ Action Mailer أكثر من مجرّد التحقق من صحّة نص البريد الإلكتروني، إذ يشمل اختبار المستلمين أيضًا. ففي الاختبارات الوظيفية واختبارات النظام، نختبر إن كانت تفاعلات مستخدمٍ تودي إلى إطلاق عملية إرسال بريد إلكتروني بشكل مناسب والتأكد من تسليم البريد الإلكتروني. فعلى سبيل المثال، يمكنك التحقق من أن عملية دعوة صديق تُرسل بريدًا إلكترونيًّا مناسبًا بشكل صحيح:

# اختبار تكامل
require 'test_helper'
 
class UsersControllerTest < ActionDispatch::IntegrationTest
  include ActionMailer::TestHelper
 
  test "invite friend" do
    # ActionMailer::Base.deliveries التأكد من الاختلاف في
    assert_emails 1 do
      post invite_friend_url, params: { email: 'friend@example.com' }
    end
  end
end
# اختبار نظام
require 'test_helper'
 
class UsersTest < ActionDispatch::SystemTestCase
  driven_by :selenium, using: :headless_chrome
  include ActionMailer::TestHelper
 
  test "inviting a friend" do
    visit invite_users_url
    fill_in 'Email', with: 'friend@example.com'
    assert_emails 1 do
      click_on 'Invite'
    end
  end
end

ملاحظة: تستفيد هذه الأمثلة من ActionMailer::TestHelper. أمَّا من أجل رسائل البريد الإلكتروني التي نتوقع توصيلها مباشرةً مع التابع deliver_now، يمكننا استعمال التابع assert_emails. ومن أجل رسال البريد الإلكتروني التي نتوقع توصيلها لاحقًا على أنها ActiveJob مع التابع deliver_later، يمكننا استعمال التابع assert_enqueued_emails. للمزيد من المعلومات، يمكنك الاطلاع على هذا التوثيق.

اختبار الوظائف (Testing Jobs)

بما أن وظائفك (jobs) المخصّصة يمكن وضعها في طوابير في مستويات مختلفة داخل التطبيق الخاص بك، فستحتاج إلى اختبار كل من الوظائف نفسها (سلوكها عندما تصبح مدرجة في الطابور) وتلك الكيانات الأخرى التي أدرجتها في الطابور بشكل صحيح.

حالة اختبار أساسية

بشكل افتراضي، عند إنشاء مهمة، سيولد اختبار مرتبط أيضًا ضمن المجلد test/jobs، وهذا مثال لاختبار مهمة فوترة (billing job):

require 'test_helper'
 
class BillingJobTest < ActiveJob::TestCase
  test 'that account is charged' do
    BillingJob.perform_now(account, product)
    assert account.reload.charged_for?(product)
  end
end

هذا الاختبار بسيط للغاية ويؤكد فقط أن المهمة قد أنجزت كما هو متوقع.

بشكل افتراضي، يُعيّن ActiveJob::TestCase محوّل الطابور إلى :test بحيث تُنفّذ جميع المهام بشكل داخلي، وسيضمن أيضًا أن جميع المهام التي نُفّذت سابقًا والتي في الطابور جرى مسحها قبل إجراء أي اختبار، ويمكنك بذلك أن تفترض بأمان أنه لم يُنفّذ أي مهمة بالفعل في نطاق كل اختبار.

التأكيدات المخصصة واختبار الوظائف داخل مكونات أخرى

تعمل Active Job مع مجموعة من التأكيدات المخصّصة التي يمكن استخدامها لتقليل الاختبارات. وللحصول على قائمة كاملة بالتأكيدات المتاحة، راجع وثائق واجهة برمجة التطبيقات لـ ActiveJob::TestHelper.

من الممارسات الجيّدة التأكد من أنَّ مهامك تُضمّن بشكل صحيح أو تُنفّذ أينما استدعيتها (على سبيل المثال داخل وحدات التحكم الخاصة بك).

وهنا على وجه التحديد حيث تكون التأكيدات المخصصة التي توفّرها Active Job مفيدة للغاية. على سبيل المثال، في داخل النموذج:

require 'test_helper'
 
class ProductTest < ActiveJob::TestCase
  test 'billing job scheduling' do
    assert_enqueued_with(job: BillingJob) do
      product.charge(account)
    end
  end
end

موارد اختبار إضافية

اختبار شيفرة برمجية معتمدة على الوقت

يوفّر ريلز توابع مساعدة مضمّنة تمكنك من التأكد من عمل تعليمات برمجيّة تعتمد على الوقت كما هو متوقّع لها.

إليك مثال باستخدام المساعد travel_to:

# لنفترض أن المستخدم مؤهل للإهداء لمدة شهر بعد تسجيله
user = User.create(name: 'Gaurish', activation_date: Date.new(2004, 10, 24))
assert_not user.applicable_for_gifting?
travel_to Date.new(2004, 11, 24) do
  assert_equal Date.new(2004, 10, 24), user.activation_date 
  # `Date.current` من (mock) أنشئ نموذج ،`travel_to` داخل الكتلة
  assert user.applicable_for_gifting?
end
assert_equal Date.new(2004, 10, 24), user.activation_date 
# فقط `travel_to`كانت التغييرات مرئية داخل الكتلة

الرجاء مراجعة توثيق الواجهة ActiveSupport::Testing::TimeHelpers البرمجة للحصول على معلومات تفصيليّة حول مساعدي الوقت المتاحين.

مصادر