• בלוג
  • ריילס שוב ניצחה. הפעם עם פיצ'ר בדיקות מערכת שעובד פשוט מעולה.

ריילס שוב ניצחה. הפעם עם פיצ'ר בדיקות מערכת שעובד פשוט מעולה.

בדיקות מערכת הן בדיקות המורכבות מגישה אוטומטית לאתר מתוך דפדפן אמיתי לצורך ביצוע פעולות ובדיקה בדף התוצאה או בבסיס הנתונים שהתוצאה הצפויה התרחשה. לריילס היו מספר כלים לכתיבת בדיקות כאלו, אבל בכל אחד היו את הבעיות שלו. כעת גירסא 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. משמעות הפקודות מהקובץ לפי הסדר היא:

  1. הפקודה visit פותחת עמוד בדפדפן.
  2. הפקודה fill_in כותבת טקסט בשדה קלט. הפרמטר הוא ה name של שדה הקלט.
  3. הפקודה find מחפשת אלמנט לפי כתיב CSS. בבדיקות שהוצגו חיפשתי את כפתור ה Submit של הטופס כדי ללחוץ עליו.
  4. הפקודה 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 לבדיקות מערכת הוכיח שהקסם עדיין נשאר גם אחרי כל השנים.