Ruby Nation 2017: Building the New Rails System Test Framework

Ruby Nation 2017: Building the New Rails System Test Framework

At the 2014 RailsConf DHH declared system testing would be added to Rails. Three years later, Rails 5.1 makes good on that promise by introducing a new testing framework: ActionDispatch::SystemTestCase. The feature brings system testing to Rails with zero application configuration by adding Capybara integration. After a demonstration of the new framework, we'll walk through what's uniquely involved with building OSS features & how the architecture follows the Rails Doctrine. We'll take a rare look at what it takes to build a major feature for Rails, including goals, design decisions, & roadblocks.

C44e1f7e22c3f23cff7bc130871047ef?s=128

Eileen M. Uchitelle

June 16, 2017
Tweet

Transcript

  1. 1.

    module ActionDispatch class SystemTestCase < IntegrationTest include Capybara::DSL include Capybara::Minitest::Assertions

    include SystemTesting::TestHelpers::SetupAndTeardown include SystemTesting::TestHelpers::ScreenshotHelper def initialize(*) # :nodoc: super self.class.superclass.driver.use end def self.start_application # :nodoc: Capybara.app = Rack::Builder.new do map "/" do run Rails.application end end SystemTesting::Server.new.run BUILDING THE NEW RAILS System Tests
  2. 2.

    module ActionDispatch class SystemTestCase < IntegrationTest include Capybara::DSL include Capybara::Minitest::Assertions

    include SystemTesting::TestHelpers::SetupAndTeardown include SystemTesting::TestHelpers::ScreenshotHelper def initialize(*) # :nodoc: super self.class.superclass.driver.use end def self.start_application # :nodoc: Capybara.app = Rack::Builder.new do map "/" do run Rails.application end end SystemTesting::Server.new.run Hi! I’m Eileen M. Uchitelle
  3. 3.

    module ActionDispatch class SystemTestCase < IntegrationTest include Capybara::DSL include Capybara::Minitest::Assertions

    include SystemTesting::TestHelpers::SetupAndTeardown include SystemTesting::TestHelpers::ScreenshotHelper def initialize(*) # :nodoc: super self.class.superclass.driver.use end def self.start_application # :nodoc: Capybara.app = Rack::Builder.new do map "/" do run Rails.application end end SystemTesting::Server.new.run I’m a Systems Engineer @
  4. 4.

    module ActionDispatch class SystemTestCase < IntegrationTest include Capybara::DSL include Capybara::Minitest::Assertions

    include SystemTesting::TestHelpers::SetupAndTeardown include SystemTesting::TestHelpers::ScreenshotHelper def initialize(*) # :nodoc: super self.class.superclass.driver.use end def self.start_application # :nodoc: Capybara.app = Rack::Builder.new do map "/" do run Rails.application end end SystemTesting::Server.new.run I’m on the Core Team
  5. 5.

    module ActionDispatch class SystemTestCase < IntegrationTest include Capybara::DSL include Capybara::Minitest::Assertions

    include SystemTesting::TestHelpers::SetupAndTeardown include SystemTesting::TestHelpers::ScreenshotHelper def initialize(*) # :nodoc: super self.class.superclass.driver.use end def self.start_application # :nodoc: Capybara.app = Rack::Builder.new do map "/" do run Rails.application end end SystemTesting::Server.new.run You can find me online @eileencodes
  6. 6.

    module ActionDispatch class SystemTestCase < IntegrationTest include Capybara::DSL include Capybara::Minitest::Assertions

    include SystemTesting::TestHelpers::SetupAndTeardown include SystemTesting::TestHelpers::ScreenshotHelper def initialize(*) # :nodoc: super self.class.superclass.driver.use end def self.start_application # :nodoc: Capybara.app = Rack::Builder.new do map "/" do run Rails.application end end SystemTesting::Server.new.run BUILDING THE NEW RAILS System Tests
  7. 8.
  8. 9.
  9. 10.

    Today we do [Rails does] nothing to encourage full system

    tests. There's no default answer in the stack. That's a mistake we’re going to fix. “ —DHH in “TDD is dead. Long live testing.” http://david.heinemeierhansson.com/2014/tdd-is-dead-long-live-testing.html
  10. 19.

    module ActionDispatch class SystemTestCase < IntegrationTest include Capybara::DSL include Capybara::Minitest::Assertions

    include SystemTesting::TestHelpers::SetupAndTeardown include SystemTesting::TestHelpers::ScreenshotHelper def initialize(*) # :nodoc: super self.class.superclass.driver.use end def self.start_application # :nodoc: Capybara.app = Rack::Builder.new do map "/" do run Rails.application end end SystemTesting::Server.new.run SYSTEM TESTING Why did it take 3 years?
  11. 21.
  12. 22.
  13. 25.

    module ActionDispatch class SystemTestCase < IntegrationTest include Capybara::DSL include Capybara::Minitest::Assertions

    include SystemTesting::TestHelpers::SetupAndTeardown include SystemTesting::TestHelpers::ScreenshotHelper def initialize(*) # :nodoc: super self.class.superclass.driver.use end def self.start_application # :nodoc: Capybara.app = Rack::Builder.new do map "/" do run Rails.application end end SystemTesting::Server.new.run SYSTEM TESTING Guiding Principles
  14. 26.

    1. Optimize for programmer happiness 2. Convention over configuration 3.

    The menu is omakase 4. No one paradigm 5. Exalt beautiful code 6. Provide sharp knives 7. Value integrated systems 8. Progress over stability 9. Push up a big tent The Rails Doctrine
  15. 28.

    require 'test_helper' # set the driver Capybara.current_driver = :selenium #

    register the driver & browser Capybara.register_driver :selenium do |app| Capybara::Selenium::Driver.new(app, browser: :chrome).tap do |driver| driver.browser.manage.window.size = Selenium::WebDriver::Dimension.new(*[1400, 1400]) end end # set the server Capybara.server = :puma Capybara.always_include_port = true # register the server Capybara.register_server :puma do |app, port, host| Rack::Handler::Puma.run(app, Port: port, Threads: "0:1") end
  16. 35.

    module ActionDispatch class SystemTestCase < IntegrationTest include Capybara::DSL include Capybara::Minitest::Assertions

    include SystemTesting::TestHelpers::SetupAndTeardown include SystemTesting::TestHelpers::ScreenshotHelper def initialize(*) # :nodoc: super self.class.superclass.driver.use end def self.start_application # :nodoc: Capybara.app = Rack::Builder.new do map "/" do run Rails.application end end SystemTesting::Server.new.run SYSTEM TESTING Implementation & Architecture
  17. 55.
  18. 59.
  19. 60.

    # In actionpack/lib/action_dispatch/system_test_case.rb module ActionDispatch class SystemTestCase < IntegrationTest def

    initialize(*) # :nodoc: super self.class.driver.use end def self.start_application # :nodoc: Capybara.app = Rack::Builder.new do map "/" do run Rails.application end end
  20. 61.

    # In actionpack/lib/action_dispatch/system_test_case.rb module ActionDispatch class SystemTestCase < IntegrationTest def

    initialize(*) # :nodoc: super self.class.driver.use end def self.start_application # :nodoc: Capybara.app = Rack::Builder.new do map "/" do run Rails.application end end
  21. 62.

    # In actionpack/lib/action_dispatch/system_test_case.rb module ActionDispatch class SystemTestCase < IntegrationTest def

    self.start_application # :nodoc: Capybara.app = Rack::Builder.new do map "/" do run Rails.application end end SystemTesting::Server.new.run end […]
  22. 63.

    # In actionpack/lib/action_dispatch/system_test_case.rb module ActionDispatch class SystemTestCase < IntegrationTest def

    self.driven_by(driver, using: :chrome, screen_size: [1400, 1400], options: {}) @driver = SystemTesting::Driver.new(driver, using: using, screen_size: screen_size) end driven_by :selenium end SystemTestCase.start_application end
  23. 64.

    # In action_dispatch/system_testing/driver.rb module ActionDispatch module SystemTesting class Driver #

    :nodoc: def initialize(name, **options) @name = name @browser = options[:using] @screen_size = options[:screen_size] @options = options[:options] end def use register if selenium? setup end
  24. 65.

    # In actionpack/lib/action_dispatch/system_test_case.rb module ActionDispatch class SystemTestCase < IntegrationTest def

    initialize(*) # :nodoc: super self.class.driver.use end def self.start_application # :nodoc: Capybara.app = Rack::Builder.new do map "/" do run Rails.application end end
  25. 66.

    # In action_dispatch/system_testing/driver.rb module ActionDispatch module SystemTesting class Driver #

    :nodoc: def use register unless rack_test? setup end private def rack_test? @name == :rack_test end […]
  26. 67.

    # In action_dispatch/system_testing/driver.rb def register Capybara.register_driver @name do |app| case

    @name when :selenium then register_selenium(app) when :poltergeist then register_poltergeist(app) when :webkit then register_webkit(app) end end end
  27. 68.

    # In action_dispatch/system_testing/driver.rb def register Capybara.register_driver @name do |app| Capybara::Selenium::Driver.new(app,

    { browser: @browser }.merge(@options)) .tap do |driver| driver.browser.manage.window.size = Selenium::WebDriver::Dimension.new(*@screen_size) end end end
  28. 69.

    # In action_dispatch/system_testing/driver.rb module ActionDispatch module SystemTesting class Driver #

    :nodoc: def use register if selenium? setup end private def selenium? @name == :selenium end […]
  29. 70.

    # In action_dispatch/system_testing/driver.rb module ActionDispatch module SystemTesting class Driver #

    :nodoc: private def setup Capybara.current_driver = @name end end end end
  30. 71.

    module ActionDispatch class SystemTestCase < IntegrationTest include Capybara::DSL include Capybara::Minitest::Assertions

    include SystemTesting::TestHelpers::SetupAndTeardown include SystemTesting::TestHelpers::ScreenshotHelper def initialize(*) # :nodoc: super self.class.superclass.driver.use end def self.start_application # :nodoc: Capybara.app = Rack::Builder.new do map "/" do run Rails.application end end SystemTesting::Server.new.run BUILDING Open Source Features
  31. 74.
  32. 76.
  33. 77.
  34. 95.

    module ActionDispatch class SystemTestCase < IntegrationTest include Capybara::DSL include Capybara::Minitest::Assertions

    include SystemTesting::TestHelpers::SetupAndTeardown include SystemTesting::TestHelpers::ScreenshotHelper def initialize(*) # :nodoc: super self.class.superclass.driver.use end def self.start_application # :nodoc: Capybara.app = Rack::Builder.new do map "/" do run Rails.application end end SystemTesting::Server.new.run Thank you Ruby Nation!
  35. 96.

    module ActionDispatch class SystemTestCase < IntegrationTest include Capybara::DSL include Capybara::Minitest::Assertions

    include SystemTesting::TestHelpers::SetupAndTeardown include SystemTesting::TestHelpers::ScreenshotHelper def initialize(*) # :nodoc: super self.class.superclass.driver.use end def self.start_application # :nodoc: Capybara.app = Rack::Builder.new do map "/" do run Rails.application end end SystemTesting::Server.new.run Find me everywhere @eileencodes