ריילס שוב ניצחה. הפעם עם פיצ'ר בדיקות מערכת שעובד פשוט מעולה.
בדיקות מערכת הן בדיקות המורכבות מגישה אוטומטית לאתר מתוך דפדפן אמיתי לצורך ביצוע פעולות ובדיקה בדף התוצאה או בבסיס הנתונים שהתוצאה הצפויה התרחשה. לריילס היו מספר כלים לכתיבת בדיקות כאלו, אבל בכל אחד היו את הבעיות שלו. כעת גירסא 5.1 הוסיפה מנגנון בדיקות שבא עם הפריימוורק, והוא עובד פשוט מעולה. קבלו דוגמא.
1. מה בודקים
כתבתי יישום קטן כדי שיהיה מה לבדוק. הקוד המלא בגיטהאב:
https://github.com/ynonp/rails-system-test-tutorial
התוכנית לא גדולה ומציגה רשימת הודעות וטופס לשלוח הודעה חדשה. אין ניהול משתמשים וכולם רואים את ההודעות שכולם השאירו. ככה בערך זה נראה כשזה עובד:
See the Pen EvvYVM by Ynon Perek (@ynonp) on CodePen.
בבדיקות מערכת נרצה להפעיל דפדפן אמיתי באופן אוטומטי כדי לבצע פעולות, למשל להוסיף הודעות או ללחוץ על כפתור ניקוי הודעות. אחרי זה קוד הבדיקה יסתכל על העמוד ויראה שהתוצאה הצפויה אכן התרחשה.
במקרה שלנו העמוד מאוד פשוט וקוד הממשק נראה כך:
<h1>The Wall - Message Board</h1>
<%= form_tag(thewall_path) do %>
<label>
Message Text
<%= text_field_tag(:text) %>
</label>
<%= submit_tag %>
<% end %>
<%= link_to 'Delete All', thewall_path, method: :delete %>
<ul>
<% @messages.each do |msg| %>
<li><%= msg[:text] %>
<% end %>
</ul>
2. הגדרת הבדיקות
הקובץ הראשון בפרויקט שקשור לבדיקות מערכת נקרא application_system_test_case.rb
והוא נמצא בתיקיית test
. כך נראה מימוש ברירת המחדל ביצירת פרויקט חדש:
require "test_helper"
class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
driven_by :selenium, using: :chrome, screen_size: [1400, 1400]
end
בהגדרות אלו הבדיקות יתבצעו בדפדפן כרום בגודל מסך 1400 על 1400. בהמשך נראה איך להחליף דפדפן ואף לבדוק ללא GUI.
3. קוד לבדיקה ראשונה
בדיקות מערכת נשמרות בתיקיה test/system
בקבצים ששמם מסתיים במילה _test
. בפרויקט הדוגמא יש שני קבצי בדיקות ושמותיהם add_message_test.rb
ו clear_messages_test.rb
. אפשר לחלק את הבדיקות שם גם לתתי תיקיות בפרויקטים גדולים.
כל קובץ כזה מגדיר מחלקה לבדיקות הקשורות בנושא מסוים. הבדיקות עצמן מוגדרות עם הפונקציה test. ספריית הבדיקות היא minitest וספריית התקשורת עם הדפדפן היא Capybara שבתורה עוטפת את Selenium. נתחיל עם קוד הבדיקה ואז נראה מה כל פקודה עושה:
# file: test/system/add_message_text.rb
require 'application_system_test_case'
class AddMessageTest < ApplicationSystemTestCase
test "sending a message" do
visit thewall_path
fill_in 'text', with: 'hello world 1'
find('input[type="submit"]').click
assert_selector('ul li:last-child', text: 'hello world 1')
end
test "sending another message" do
visit thewall_path
fill_in 'text', with: 'hello world 2'
find('input[type="submit"]').click
assert_selector('ul li:last-child', text: 'hello world 2')
end
end
הקובץ מכיל שתי בדיקות ושתיהן כתובות ב DSL של Capybara. משמעות הפקודות מהקובץ לפי הסדר היא:
- הפקודה
visit
פותחת עמוד בדפדפן. - הפקודה
fill_in
כותבת טקסט בשדה קלט. הפרמטר הוא ה name של שדה הקלט. - הפקודה
find
מחפשת אלמנט לפי כתיב CSS. בבדיקות שהוצגו חיפשתי את כפתור ה Submit של הטופס כדי ללחוץ עליו. - הפקודה
assert_selector
מוודאת שקיים אלמנט שמתאים לתנאים שרשמנו. במקרה של הבדיקה מוודאת שקיים אלמנט li שהוא הילד האחרון ברשימת ושהטקסט שלו הוא הטקסט שכתבנו בטופס.
בתרגום לעברית הבדיקה אומרת: כנס לדף היישום, כתוב בתיבת הטקסט את השורה hello world 1, לחץ ״שמירה״ ואז תסתכל שהשורה נכנסה לרשימת ההודעות. זה בדיוק תיאור בדיקה שהייתם נותנים לאיש QA כדי לבדוק את היישום שלכם.
ניתן למצוא רשימה של פקודות Capybara בקישור:
https://gist.github.com/zhengjia/428105
או בתיעוד בקישור:
www.rubydoc.info/github/jnicklas/capybara
4. בדיקה שניה
אתם בטח מבינים לאן זה הולך, אבל בכל זאת נתבונן בקוד הבדיקה השניה:
# file: test/system/clear_messages_test.rb
require 'application_system_test_case'
class ClearMessagesTest < ApplicationSystemTestCase
test "Delete all messages" do
visit thewall_path
assert_selector('ul li', count: 1)
find('a', :text => "Delete All").click
assert_selector('ul li', count: 0)
end
end
הבדיקה משתמשת באופן הפקודות של Capybara כדי להיכנס לעמוד, ללחוץ על כפתור המחיקה ולראות שההודעות נמחקו. הדבר היחיד כאן שאולי נראה מוזר זה שלפני ביצוע הבדיקה יש הודעה אחת. מאיפה היא הגיעה? מי שלח אותה?
5. מידע קבוע לבדיקה - Fixtures
אז קודם כל צריך להבהיר שהבדיקות מנותקות אחת מהשניה ושינויים שמתבצעים בבסיס הנתונים במסגרת בדיקה אחת לא משפיעים על בדיקות אחרות. כל בדיקה מתבצעת ב DB Transaction משלה ולאחר סיום הבדיקה מבוצע Rollback אוטומטי לשינויים. בעבר מנגנון זה היה שמור לבדיקות יחידה בלבד, ואחד החידושים של System Tests הוא יישום המנגנון גם על בדיקות בדפדפן אמיתי.
לכן המידע לא הגיע מבדיקות ישנות אלא דרך מנגנון אחר שנקרא Fixtures. מנגנון זה מאפשר להטמיע קוד ראשוני במערכת לצורך בדיקות, כדי שנבדוק על מערכת שדומה למצב העניינים האמיתי ולא על בסיס נתונים ריק. מידע זה מוגדר בקבצים בתיקיה test/fixtures
. לדוגמא עבור הודעות כך נראה הקובץ הרלוונטי:
# file: test/fixtures/messages.yml
one:
text: welcome home
בכל בדיקה תהיה הודעה אחת עם התוכן welcome home. היא מוכנסת לפני תחילת כל בדיקה באופן אוטומטי.
6. דפדפנים אחרים ובדיקה ללא ממשק
בדיקות מערכת הגיעו אלינו בתיאום מושלם עם שידרוג של כרום שמאפשר הרצה ללא ממשק גרפי. בצורה כזאת אפשר להריץ את הבדיקות בלי לראות דפדפן אבל לקבל את אותן תוצאות כאילו הדפדפן הופעל. זה יעיל למשל כשמריצים בדיקות על שרת CI במקום על מכונת הפיתוח שלכם.
כדי להוסיף דפדפנים יש להגדיר אותם בקובץ test/test_helper.rb
. כך נראה הקובץ אצלי שמוסיף תמיכה בכרום ללא ממשק:
# file: test/test_helper.rb
require File.expand_path('../../config/environment', __FILE__)
require 'rails/test_help'
require "selenium/webdriver"
Capybara.register_driver :headless_chrome do |app|
capabilities = Selenium::WebDriver::Remote::Capabilities.chrome(
chromeOptions: { args: %w(headless disable-gpu) }
)
Capybara::Selenium::Driver.new app,
browser: :chrome,
desired_capabilities: capabilities
end
class ActiveSupport::TestCase
# Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
fixtures :all
# Add more helper methods to be used by all tests here...
end
נחזור לקובץ הגדרות הבדיקות וכדי להפעיל את הבדיקה בדפדפן headless_chrome
שהגדרנו נעדכן את הקובץ כך שיכיל את התוכן הבא:
# file: test/application_system_test_case.rb
require "test_helper"
class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
driven_by :headless_chrome
end
7. סיכום ויכולות נוספות
בדיקות מערכת אוטומטיות היו אתנו הרבה זמן אבל תמיד משהו בהן הרגיש עקום. מאחר ובמערכות רבות בדיקות אלו רצות מתהליך נפרד ועל שרת נפרד, אי אפשר היה במסגרת הבדיקה לבצע שינויים ב DB או לגשת למחלקות ולפונקציות של המערכת עצמה, כלומר אלו היו בדיקות Black Box בלבד. אפילו בריילס לפני גירסא 5.1 בדיקות מערכת רצו ב Thread נפרד ולכן שינויים שהבדיקה ביצעה בבסיס הנתונים דרך הדפדפן לא היו מסונכרנים עם קוד הבדיקה עצמו.
כל זה תוקן בגירסא 5.1 וכעת פיתוח בדיקות מערכת זה פשוט כיף. יש לנו שליטה מלאה על מצב בסיס הנתונים בכל רגע, יש דפדפן אמיתי שנכנס לאתר ומבצע פעולות ותחביר DSL של Capybara שמאפשר בקלות להגדיר איזה פעולות לבצע.
וכאילו שזה לא היה מספיק, ריילס ייצור באופן אוטומטי Screenshot בכל פעם שבדיקה נכשלת וישמור אותו בתיקיה tmp/screenshots
כך שתוכלו לדעת בדיוק איך נראה מצב העניינים בדפדפן בכל כשלון ולנסות לשחזר את הבאג בעצמכם.
מה שמבדיל את Rails מספריות מתחרות זה לא יותר יכולות (כי כל יכולת כזאת אפשר למצוא בכל ספריה), אלא החיבור בין הדברים וכמה קל ומהר להתחיל לכתוב. הפתרון של Rails לבדיקות מערכת הוכיח שהקסם עדיין נשאר גם אחרי כל השנים.