Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Testing in ManageIQ

Testing in ManageIQ

A talk about testing with RSpec in the ManageIQ cloud management platform. A short tour of some RSpec ManageIQisms, good spec structure and anti-patterns, troubleshooting test failures, and an overview of test runtime performance.

Avatar for Chris Arcand

Chris Arcand

June 09, 2016
Tweet

More Decks by Chris Arcand

Other Decks in Programming

Transcript

  1. @chrisarcand @jrafanie • RSpec ManageIQisms • Spec structure and anti-patterns

    • Troubleshooting (“WAT!?”) • Spec performance
  2. @chrisarcand @jrafanie • RSpec ManageIQisms • Spec structure and anti-patterns

    • Troubleshooting (“WAT!?”) • Spec performance
  3. @chrisarcand @jrafanie factory_girl Technologies RSpec VCR Ruby JavaScript Jasmine *

    Also, tiny sprinkles of MiniTest in gems/pending, but…
  4. @chrisarcand @jrafanie Sweet, sweet suites • Automation • Brakeman (security

    scanner) • Javascript (Jasmine) • ManageIQ Providers - Amazon • Migrations • Replication • VMDB
  5. @chrisarcand @jrafanie Sweet, sweet suites rake test rake test:automation rake

    test:automation:setup rake test:brakeman rake test:javascript rake test:javascript:setup rake test:manageiq-providers-amazon rake test:manageiq-providers-amazon:setup rake test:migrations rake test:migrations:down rake test:migrations:setup rake test:migrations:up rake test:replication rake test:replication:setup rake test:replication:teardown rake test:replication_util rake test:vmdb rake test:vmdb:setup rake test:<suite>:setup rake test:<suite>
  6. @chrisarcand @jrafanie Sweet, sweet suites • Automation • Brakeman (security

    scanner) • Javascript (Jasmine) • ManageIQ Providers - Amazon • Migrations • Replication • VMDB
  7. @chrisarcand @jrafanie VMDB Migrations Replication Automation Controllers Helpers Initializers Mailers

    Models Requests Routing Services Views Presenters Rake Tasks REST API Helpers, helpers everywhere: AuthHelper ViewSpecHelper UiConstants ControllerSpecHelper AutomationSpecHelper MigrationSpecHelper ApiSpecHelper PresenterSpecHelper RakeTaskExampleGroup and more… Up migrations Down migrations REST API environment setup Replication setup Special migration management
  8. @chrisarcand @jrafanie VMDB Migrations Replication Automation Controllers Helpers Initializers Mailers

    Models Requests Routing Services Views Presenters Rake Tasks REST API Helpers, helpers everywhere: AuthHelper ViewSpecHelper UiConstants ControllerSpecHelper AutomationSpecHelper MigrationSpecHelper ApiSpecHelper PresenterSpecHelper RakeTaskExampleGroup and more… Up migrations Down migrations REST API environment setup Replication setup Special migration management WAT
  9. @chrisarcand @jrafanie RSpec knows what to do through metadata called

    ✨tags✨ describe "group with tagged specs" do it "slow example", :slow => true do; end it "ordinary example" do; end end ./some_spec.rb $ rspec ./some_spec.rb --tag ~slow
  10. @chrisarcand @jrafanie describe User, :type => :model do # Model-specific

    helpers, env end …
 config.include ModelHelper, :type => :model … user_spec.rb spec_helper.rb
  11. @chrisarcand @jrafanie Example: All the REST API specs live in

    ./spec/requests/api config.define_derived_metadata( :file_path => /spec\/requests\/api/) do |metadata| metadata[:rest_api] = true end config.include ApiSpecHelper, :rest_api => true config.before(:each, :rest_api => true) do init_api_spec_env end
  12. @chrisarcand @jrafanie Takeaways: 1. Spec location matters a lot. (Location

    matters in all Rails applications, but especially ManageIQ) 2. We have custom RSpec matchers and helpers for different spec types (And you should go check them out. See ./spec/spec_helper.rb)
  13. @chrisarcand @jrafanie Who tests migrations? • Manually testing is hard

    • Setup database • Migrate • Test conversion • Repeat up and down • Migration specs automate this
  14. @chrisarcand @jrafanie Migration spec tips • Don’t mix schema changes

    and data changes • Test data migrations • Migrations are snapshots in time • Don't use application models (app/models/*) • Use model stubs • good_migrations gem (soon™) • Libor
  15. @chrisarcand @jrafanie # db/migrate/20150817213409_clear_tenant_seed.rb class ClearTenantSeed < ActiveRecord::Migration class Tenant

    < ActiveRecord::Base; end def up say_with_time("...") do Tenant.update_all(:name => nil) end end end Model stub
  16. @chrisarcand @jrafanie # spec/migrations/20150817213409_clear_tenant_seed_spec.rb require_migration describe ClearTenantSeed do migration_context :up

    do it "works with one tenant" do tenant = migration_stub(:Tenant).create(…) migrate expect(tenant.reload.name).to be_nil end end end ✨ Magic
  17. @chrisarcand @jrafanie Who tests migrations? • FYI, Rails 5.0 generates

    versioned migrations class AddNameToUsers < ActiveRecord::Migration[5.0] ... end • Existing migrations use 4.2 migration code
  18. @chrisarcand @jrafanie • RSpec ManageIQisms • Spec structure and anti-patterns

    • Troubleshooting (“WAT!?”) • Spec performance
  19. @chrisarcand @jrafanie RSpec structure OH: “Don’t nest contexts EVARRRRRR!” Using

    context and describe blocks correctly will make you happier
  20. @chrisarcand @jrafanie BAD: Contextual tests at the top level! In

    the future: • Harder to understand context • Promotes inefficiencies • Therefore, harder to add tests
  21. @chrisarcand @jrafanie Harder to understand context
 Imagine seeing just this

    snippet buried in other examples as this file grows. WAT?
  22. @chrisarcand @jrafanie Harder to add tests
 I now need to

    edit the setup here just to add a new test to avoid these issues
  23. @chrisarcand @jrafanie Harder to add tests
 I now need to

    edit the setup here just to add a new test to avoid these issues WAT
  24. @chrisarcand @jrafanie Easy to add different cases for #generation Easy

    to add different cases for totally different things at top level
  25. @chrisarcand @jrafanie “I don’t understand what this test is doing.”

    “I have no idea why this test exists.” Complex setup
  26. @chrisarcand @jrafanie before(:each) do _guid_2, _server_2, @zone_2 = EvmSpecHelper.create_guid_miq_ guid,

    server, @zone = EvmSpecHelper.create_guid_miq_server_z @ems = FactoryGirl.create(:ems_vmware_with_auth…, :zone => @ other_ems = FactoryGirl.create(:ems_…_with_auth…, :zone => @ @worker_guid = MiqUUID.new_guid @worker_record = FactoryGirl.create(:miq_vim_broker_worker, :guid => …, :mi @drb_uri = "drb://127.0.0.1:12345" allow(DRb).to receive(:uri).and_return(@drb_uri) allow_any_instance_of(described_class).to receive(:sync_acti allow_any_instance_of(described_class).to receive(:sync_conf allow_any_instance_of(described_class).to receive(:set_conne allow_any_instance_of(ManageIQ::Providers::Vmware::InfraMana .to receive(:authentication_check).and_return([true, ""]) allow_any_instance_of(ManageIQ::Providers::Vmware::InfraMana .to receive(:authentication_status_ok?).and_return(true) end
  27. @chrisarcand @jrafanie • Find what you really need for setup

    • Create only what's needed • Figure out where it belongs - Abstractions can hide relevant details - Make setup methods explicit and flexible Beware of setup complexity
  28. @chrisarcand @jrafanie describe User do before do special_magic_setup_method @user =

    User.first end it “has write access when authenticated” do expect(@user).to have_write_access end it “can’t write when unauthenticated” do @user.update_attributes(…) expect(@user).to_not have_write_access end end
  29. @chrisarcand @jrafanie • instance_variable_get • .send • Many mocks, stubs,

    doubles • and the best one of all… Test smells: Write testable code
  30. @chrisarcand @jrafanie PRs that remove these are welcome! git grep

    "any_instance_of" spec |wc -l 534 ✂✂✂
  31. @chrisarcand @jrafanie RSpec anti-patterns:
 Sporadic Failures This will randomly fail.

    Why? vm1 = Vm.create(…) vm2 = Vm.create(…) expect(Vm.pluck(:id)) .to eq([vm1.id, vm2.id]) No explicit order
 Use testing constructs like match_array!
  32. @chrisarcand @jrafanie Fix sporadic failures by… • Limiting the problem

    scope - Automated bisecting - Bisecting by comments! • Ask for help in Gitter!
  33. @chrisarcand @jrafanie Caching “There are only two hard things in

    Computer Science: cache invalidation and naming things.” -- Phil Karlton http://martinfowler.com/bliki/TwoHardThings.html • Add caching only if absolutely required • Clear caches with tests - See spec/support/evm_spec_helper.rb
  34. @chrisarcand @jrafanie • RSpec ManageIQisms • Spec structure and anti-patterns

    • Troubleshooting (“WAT!?”) • Spec performance
  35. @chrisarcand @jrafanie Travis failures • Look at the travis build

    log • Read the error • Now, read the backtrace • No, really, did you read the error?
  36. @chrisarcand @jrafanie Travis failures • Check other builds on travis

    • Master could be broken! • See build history @ github
  37. @chrisarcand @jrafanie Travis failures • Look at bundle install section

    for NEW gems • Share your findings: • https://gitter.im/ManageIQ/manageiq
  38. @chrisarcand @jrafanie Travis failures • Enable travis on your fork

    • Test without waiting behind ManageIQ org • Useful for debugging Travis errors https://travis-ci.org/jrafanie/manageiq/settings
  39. @chrisarcand @jrafanie Useful RSpec CLI switches -t ~slow (--tag)filter by

    tags (~ excludes tag) -f d (--format) [p]rogress(dots), [d]ocumentation, etc. --seed 1234 Run examples with a specific test order --bisect Find the examples to recreate a sporadic bug --only-failures Run the examples that just failed --fail-fast Abort a test run on the first error
  40. @chrisarcand @jrafanie • RSpec ManageIQisms • Spec structure and anti-patterns

    • Troubleshooting (“WAT!?”) • Spec performance
  41. @chrisarcand @jrafanie Boot time improvements • Ongoing: Huge improvements to

    Rails boot time • 2.5 seconds - lazy loading message catalogs, #8525 • bundler 1.12.1+, rubygems 2.5.0+ • Future: • Ruby 2.3.x • Sprockets #211 (lazy stat assets for size) • Bundler bugfix to prevent re-resolve (#4618)
  42. @chrisarcand @jrafanie Rails boot time • Spring • https://github.com/rails/spring •

    Preloads application boot for faster development • Rake tasks • Tests • Migrations • Boot speed must still be fast
  43. @chrisarcand @jrafanie 2. Total suite run time “The tests take

    forever.” “Let’s just push it to Travis lolzzzzz”
  44. @chrisarcand @jrafanie Why? *Besides the fact that it’s a large,

    successful codebase originally written over a decade ago and we have over 12,000 individual tests
  45. @chrisarcand @jrafanie % of total suite time vs. % of

    total specs 70.3% of the test suite accounts for 11.45% of total time 25.3% of the test suite accounts for 37.43% of total time
  46. @chrisarcand @jrafanie % of total suite time vs. % of

    total specs 70.3% of the test suite accounts for 11.45% of total time 25.3% of the test suite accounts for 37.43% of total time WAT
  47. @chrisarcand @jrafanie Promotes inefficiencies
 Creating a unused second record here!

    Remember this? …and this example doesn’t even include FG associations
  48. @chrisarcand @jrafanie Good news! We’re already making improvements • Fall

    2015: Moved to RSpec 3.x 
 (core performance ++) • In the future: 
 Pluggable providers split? Flag slow specs?
  49. @chrisarcand @jrafanie ✨Run your tests in parallel!✨ $ PARALLEL=true bundle

    exec rake test PR #8868 merged over the weekend (June 5th) Paired effort over the last few weeks First championed by Joe last year! Thanks Jason for reviewing/merge!
  50. @chrisarcand @jrafanie All benchmarks on Mid-2015 Macbook Pro 2.5 GHz

    Intel Core i7, ManageIQ commit #1689382 (May 18th, 2016) Time includes full load time Local vmdb test run before: ~7.5 minutes ~25 minutes Local vmdb test run after (8 cores):