วันนี้เราจะมาดู system tests ใน Rails 6 System tests ทำให้ auto-test วิธีที่ user interact กับแอปพลิเคชัน รวมถึง JavaScript ใน UI Minitest ซึ่งเป็น default testing framework ใน Rails เหมาะกับ system testing ด้วย config ที่ Rails จัดการให้ ต้องการเพียงไม่กี่ขั้นตอนถึงจะมี test แรกทำงาน
System tests เพิ่มเข้ามาใน Rails stack ตั้งแต่ Rails 5.1 ตอนผมนั่งเริ่มใช้ พบว่ายากที่จะรวบรวมข้อมูล relevant, up-to-date ที่ไม่เกี่ยวกับ RSpec นี่คือข้อมูลล่าสุดที่สุดที่ผมรวบรวมเกี่ยวกับ system tests กับ Minitest
Introduction to System Tests
ใน Rails jargon system testing หมายถึง "testing an application as a whole system" ทำโดยใช้ browser ใน tests แทนที่จะ test ส่วนๆ แยกกัน ด้วย system tests เราสามารถ test 'workflow' ทั้งหมดได้ เหมือน user ผ่าน interaction กับ app รวมถึง JavaScript parts ในทางปฏิบัติ หมายถึงเราไม่ต้องการ system test เช็ค ว่า record ถูกสร้างใน database เมื่อ user กดปุ่ม เราแค่ test ว่า record ใหม่นั้นปรากฏบนหน้าจอของพวกเขา User interaction tests แบบนี้เรียกว่า feature tests หรือ acceptance tests ต่างจาก integration tests: integration tests test behavior โดยเฉพาะของทุกส่วนของ app รวมกัน แต่ไม่ผ่าน UI
Configuration
Configuration เรียบง่ายจนสับสน
Gem Capybara ใช้ interact กับ browser ผ่าน Capybara เรา make tests visit pages, fill forms, click links และ buttons ถ้าคุณเคยทำงานกับ Capybara คุณรู้ว่าต้อง coordinate อะไรบ้างให้ทำงานกับ database cleaning strategy และ browser configuration โอเฮ่สำหรับ Rails system test setup: มันจัดการ config ทั้งหมดที่ต้องการทำงานกับ Capybara out-of-the-box เราจึงโฟกัสที่ test writing จริง ใช้ Selenium driver โดยค่าเริ่มต้น
จาก docs: "By default, ActionDispatch::SystemTestCase is driven by the Selenium driver, with the Chrome browser, and a browser size of 1400x1400." Selenium driver ต่างจาก Capybara default และเลือกเพราะมันทำงานกับ JavaScript
Settings ทั้งหมดอยู่ใน application_system_test_case.rb สิ่งเดียวที่ต้องทำคือ require นี้ในแต่ละ test file
# application_system_test_case.rb (default)
require "test_helper"
class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
driven_by :selenium, using: :chrome, screen_size: [1400, 1400]
end
ถ้า generate app ใหม่ด้วย Rails 6 นี่คือทั้งหมดที่ต้องการ แปลว่า: ข้ามคำแนะนำทั้งหมดเกี่ยวกับ setup Selenium driver ที่อินเทอร์เน็ตแสดงสำหรับ UI tests หรือ RSpec feature tests
ใน existing apps การ generate scaffold จะ auto-generate application_system_test_case.rb และทุกอย่างที่ต้องการสำหรับ system tests
ตอน Eileen Uchitelle introduce system tests Chrome ถูกเลือกเหนือ Firefox เพราะตอนนั้น FF ไม่เล่นดีกับ Selenium ตอนนี้ fixed ใน Firefox versions ใหม่ ผมเปลี่ยนเป็น FF:
# application_system_test_case.rb (change driver to Firefox)
require "test_helper"
class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
driven_by :selenium, using: :firefox, screen_size: [1400, 1400]
end
(เริ่มแรก ผมรัน $ rails db:test:prepare after สลับ driver เพื่อ clean start แต่ test runner ใหม่จัดการนั้นให้แล้ว: invoke test จะ autorun :prepare.)
ใหม่ใน Rails 6
ตั้งแต่ introduce system tests ใน 5.1 gem chromedriver-helper ถูก deprecated ใน Rails 6 apps ใหม่ ถูกแทนที่ด้วย gem webdrivers ใน Rails 5 apps ควรแทนที่ chromedriver-helper ด้วย webdrivers gem ถ้ายังไม่ทำ ด้วย webdrivers gem ไม่ต้องการ gem selenium-webdriver แล้ว webdrivers จัดการให้ Fun fact: Rails 6 ให้ทั้ง selenium-webdriver gem และ webdrivers gem
Sanity Check: สิ่งที่ไม่ต้องการอีกต่อไป
Setup Capybara เดิมทีเป็นปวดหัว ไม่ชัดเจนว่าต้อง setup อะไรจากศูนย์ รวมถึงหา database cleaning strategy ที่ถูกต้อง dependencies ของ capybara-webkit gem และ requirements ที่ไม่คาดคิดอื่นๆ ที่ต้อง figure out ก่อนเขียน test ตอนนี้ Rails ให้ทุกอย่าง มันสับสนเพราะข้อมูลใน guides และ documentation ที่ obsolete ตอนนี้ นี่คือสิ่งที่เราสามารถข้ามได้:
First, precooked Rails setup หมายถึงเราสามารถละ gems ที่มักกล่าวถึงใน testing เช่น minitest-rails-capybara หรือ extras สำหรับ Minitest: colored output, fast failing tests, running one line/test Minitest features นี้อยู่ใน test runner ที่ introduced ใน Rails 5.0
ไม่ต้องการ gem database_cleaner อีกแล้ว Rails ทำ cleanup ให้ (transactional tests)
เพราะ Rails ใช้ Selenium แทน Capybara default ไม่ต้องการ capybara-webkit และไม่ต้องทำ extras เพื่อ include testing JavaScript elements
แม้แต่ save_and_open_screenshot ของ Capybara ไม่ต้องใช้ เพราะ Rails ให้ method take_screenshot บันทึก screenshot ใน /tmp และให้ link ใน test output เพื่อเข้าถึงง่าย
เลือกระหว่าง Real Browser และ Headless Browser
ผมเพลิดเพลินดู system tests รันใน browser เห็น links ถูกคลิก forms ถูก fill แต่... มันช้า เพื่อ speed up test run ใช้ 'headless' browser: browser ที่มีการเข้าถึง app เหมือน browser ปกติ แต่ไม่มี graphical user interface หมายถึง: ทำงานเหมือนกันแต่มองเห็น tests ทำงานไม่ได้เพราะ headless driver ไม่เปิด browser window จริง
ถ้าอยาก headless มี headless_chrome และ headless_firefox ใช้ต้องเปลี่ยนเล็กน้อย:
# To change driver to headless_*
# application_system_test_case.rb (change driver to headless_*:)
require "test_helper"
class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
driven_by :selenium, using: :headless_firefox
end
Note: ใน Rails documentation และบทความหลายๆ Poltergeist ถูกระบุเป็น option สำหรับ driver มันเคย popular เป็น headless PhantomJS driver สำหรับ Capybara PhantomJS ถูก abandon แล้ว ไม่ต้องดำน้ำรายละเอียด เหตุผลที่กล่าวถึงตรงนี้คือ Poltergeist และ PhantomJS ยังถูก mention ใน Rails docs
มี customization เพิ่มเติมได้ แตเพื่อเริ่มต้น นี่คือทั้งหมดที่ต้องการ
รัน Tests
$ rails test จะรันทุก tests ยกเว้น system tests ต้องรัน $ rails test:system โดยเฉพาะ (Fun fact: คำสั่ง $ rails จะรันผ่าน bin/rails เสมอ ไม่ต้องพิมพ์ $ bin/rails อีก)
- Run all system tests:
$ rails test:system - Run test ในไฟล์เฉพาะ (ที่นี่: users_test.rb)
$ rails test/system/users_test - ... หรือ test เฉพาะ:
$ rails test test/system/users_test.rb:21 - Run all tests: รัน system tests ก่อน:
$ rails test:system test
Note: options flags ไม่ทำงานกับ test:system ถ้าอยากใช้ flags เช่น -f (fail fast) หรือ -v (verbose) ใช้ $ rails test test/system -v -f
ควร (และไม่ควร) Test อะไร
System tests complementary กับ unit tests ไม่ใช่ substitute ควร test happy path บวก maybe one path with error message หรือ redirect System tests ไม่ได้หมายถึง test edge cases ทั้งหมดใน browser แค่ cover main features
ตอนเลือก test subject ผมพยายามหา entity ที่ reflect ว่า user ใช้ app อย่างไร สำหรับ naming tests ผมยืม naming convention ของ GitLab คือ ROLE_ACTION_test.rb ซึ่งเข้ากับ approach นี้ดี เช่น user_shares_card_test.rb
General Tips and Tricks
- ถ้าใช้ Devise สำหรับ authentication สามารถใช้ Devise integration helpers login/logout test users เพิ่ม
include Devise::Test::IntegrationHelpersใน test class หรือในtest_helper.rbทำให้พร้อมใช้ในทุก test ตอนนี้สร้าง user และsign_in(@user)ถ้าวางใน methodsetupไม่ต้อง logout user ใน teardown Rails จัดการ cleanup ให้ - ทำงานกับ forms น่าจะ reference ids ที่ Rails สร้างจาก model name + field name และ button types สำหรับ
click_on(fill_in "user_email",click_on :commit) แต่ system tests เกี่ยวกับสิ่งที่ user เห็น บนหน้าจอ สมเหตุสมผลที่จะใช้ visible elements คือ texts ตัวเลือกที่สมเหตุสมผลคือมี reference keys ใน i18n locale files และใช้ keys แทน literal texts ที่เปลี่ยนแปลง (fill_in :user_email) Capybara หา text ได้เฉพาะ full I18n syntax:assert_selector "h1", text: I18n.t("activerecord.models.things") - Path helpers included by default สำหรับ libraries บางตัวต้อง include helpers ใน test class เช่น
include ActionMailer::TestHelperเพื่อใช้assert_emails - สร้าง custom classes รัน tests บน screen sizes ต่างๆ ดู guides
- Screenshot feature ใช้สำหรับ documentation และ promotional materials ได้ ถ้าทำเป็น screencast และช้าลง playback speed ได้ product video ในไม่กี่นาที!
- Rails มี generator สำหรับ system tests
- Minitest ใน Rails ต่างจาก Minitest ปกติเล็กน้อย และเพิ่ม Rails-specific methods และ assertions เช็ค Rails docs ก่อน Minitest docs
ผมสนุกกับ system tests มาก และส่วนใหญ่เพราะมันง่าย out-of-the-box
สรุป
ในบทความนี้ เราดู config ที่ Rails ส่งมาพร้อม และ customizations ขั้นต่ำที่อาจอยากเพิ่ม Minitest ดีสำหรับ system tests ไปปะปัง tips และ tricks ควรช่วยให้ system tests แรกทำงานได้ ดูพวกมันทำ magic ใน browser!