Slide 1

Slide 1 text

Testing in Joe Rafaniello and Chris Arcand

Slide 2

Slide 2 text

Chris Arcand
 @chrisarcand Joe Rafaniello @jrafanie Remote - Washington, D.C. Minneapolis, MN Mahwah office

Slide 3

Slide 3 text

Application Core Team

Slide 4

Slide 4 text

✨Platform Team✨

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

@chrisarcand @jrafanie factory_girl Technologies RSpec VCR Ruby JavaScript Jasmine * Also, tiny sprinkles of MiniTest in gems/pending, but…

Slide 8

Slide 8 text

@chrisarcand @jrafanie RSpec http://rspec.info/ http://rspec.info/documentation/ https://relishapp.com/rspec

Slide 9

Slide 9 text

@chrisarcand @jrafanie Test Suites, Metadata, and ManageIQ helpers RSpec ManageIQisms

Slide 10

Slide 10 text

@chrisarcand @jrafanie Sweet, sweet suites • Automation • Brakeman (security scanner) • Javascript (Jasmine) • ManageIQ Providers - Amazon • Migrations • Replication • VMDB

Slide 11

Slide 11 text

@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::setup rake test:

Slide 12

Slide 12 text

@chrisarcand @jrafanie Sweet, sweet suites • Automation • Brakeman (security scanner) • Javascript (Jasmine) • ManageIQ Providers - Amazon • Migrations • Replication • VMDB

Slide 13

Slide 13 text

@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

Slide 14

Slide 14 text

@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

Slide 15

Slide 15 text

@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

Slide 16

Slide 16 text

@chrisarcand @jrafanie describe User, :type => :model do # Model-specific helpers, env end …
 config.include ModelHelper, :type => :model … user_spec.rb spec_helper.rb

Slide 17

Slide 17 text

@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

Slide 18

Slide 18 text

@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)

Slide 19

Slide 19 text

@chrisarcand @jrafanie Migration Specs RSpec ManageIQisms

Slide 20

Slide 20 text

@chrisarcand @jrafanie Who tests migrations? • Manually testing is hard • Setup database • Migrate • Test conversion • Repeat up and down • Migration specs automate this

Slide 21

Slide 21 text

@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

Slide 22

Slide 22 text

@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

Slide 23

Slide 23 text

@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

Slide 24

Slide 24 text

@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

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

@chrisarcand @jrafanie OH: “Don’t nest contexts EVARRRRRR!” RSpec structure

Slide 27

Slide 27 text

@chrisarcand @jrafanie RSpec structure OH: “Don’t nest contexts EVARRRRRR!” Using context and describe blocks correctly will make you happier

Slide 28

Slide 28 text

@chrisarcand @jrafanie

Slide 29

Slide 29 text

@chrisarcand @jrafanie BAD: Contextual tests at the top level! In the future: • Harder to understand context • Promotes inefficiencies • Therefore, harder to add tests

Slide 30

Slide 30 text

@chrisarcand @jrafanie

Slide 31

Slide 31 text

@chrisarcand @jrafanie Harder to understand context
 Imagine seeing just this snippet buried in other examples as this file grows. WAT?

Slide 32

Slide 32 text

@chrisarcand @jrafanie Promotes inefficiencies
 Creating an unused second record here!

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

@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

Slide 35

Slide 35 text

@chrisarcand @jrafanie Separate context from expectation…

Slide 36

Slide 36 text

@chrisarcand @jrafanie …and create a self-contained, portable set of circumstances

Slide 37

Slide 37 text

@chrisarcand @jrafanie Easy to add different cases for #generation Easy to add different cases for totally different things at top level

Slide 38

Slide 38 text

@chrisarcand @jrafanie

Slide 39

Slide 39 text

@chrisarcand @jrafanie $ rspec ./spec/some_spec.rb --format=documentation

Slide 40

Slide 40 text

@chrisarcand @jrafanie Your mileage may vary. Especially in a legacy codebase.

Slide 41

Slide 41 text

@chrisarcand @jrafanie RSpec Anti-Patterns • Complex setup • Hard-to-test code • Random test failures

Slide 42

Slide 42 text

@chrisarcand @jrafanie “I don’t understand what this test is doing.” “I have no idea why this test exists.” Complex setup

Slide 43

Slide 43 text

@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

Slide 44

Slide 44 text

@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

Slide 45

Slide 45 text

@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

Slide 46

Slide 46 text

@chrisarcand @jrafanie • instance_variable_get • .send • Many mocks, stubs, doubles • and the best one of all… Test smells: Write testable code

Slide 47

Slide 47 text

@chrisarcand @jrafanie RSpec anti-patterns *_any_instance_of expect_any_instance_of(SomeClass).to receive(:some_method) allow_any_instance_of(SomeClass).to receive(:some_method) { … }

Slide 48

Slide 48 text

@chrisarcand @jrafanie *_any_instance_of • Added ruby 2.3 support • rspec-mocks PR 1043 ☹ • rspec-mocks PR 1060 (for real)

Slide 49

Slide 49 text

@chrisarcand @jrafanie PRs that remove these are welcome! git grep "any_instance_of" spec |wc -l 534 ✂✂✂

Slide 50

Slide 50 text

@chrisarcand @jrafanie • Add public APIs • Reduce state mutation Write testable code!

Slide 51

Slide 51 text

@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!

Slide 52

Slide 52 text

@chrisarcand @jrafanie Fix sporadic failures by… • Limiting the problem scope - Automated bisecting - Bisecting by comments! • Ask for help in Gitter!

Slide 53

Slide 53 text

@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

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

@chrisarcand @jrafanie Travis failures • Look at the travis build log • Read the error • Now, read the backtrace • No, really, did you read the error?

Slide 56

Slide 56 text

@chrisarcand @jrafanie Travis failures • Check other builds on travis • Master could be broken! • See build history @ github

Slide 57

Slide 57 text

@chrisarcand @jrafanie Travis failures • Look at bundle install section for NEW gems • Share your findings: • https://gitter.im/ManageIQ/manageiq

Slide 58

Slide 58 text

@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

Slide 59

Slide 59 text

@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

Slide 60

Slide 60 text

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

Slide 61

Slide 61 text

@chrisarcand @jrafanie 1. Boot time “I can’t TDD, too slow!”

Slide 62

Slide 62 text

@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)

Slide 63

Slide 63 text

@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

Slide 64

Slide 64 text

@chrisarcand @jrafanie 2. Total suite run time “The tests take forever.” “Let’s just push it to Travis lolzzzzz”

Slide 65

Slide 65 text

@chrisarcand @jrafanie

Slide 66

Slide 66 text

@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

Slide 67

Slide 67 text

@chrisarcand @jrafanie % of specs by individual test length
 (VMDB suite)

Slide 68

Slide 68 text

@chrisarcand @jrafanie % of total suite time by individual test length

Slide 69

Slide 69 text

@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

Slide 70

Slide 70 text

@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

Slide 71

Slide 71 text

@chrisarcand @jrafanie Promotes inefficiencies
 Creating a unused second record here! Remember this? …and this example doesn’t even include FG associations

Slide 72

Slide 72 text

@chrisarcand @jrafanie Analysis helps us improve… …but there’s no silver bullet.

Slide 73

Slide 73 text

@chrisarcand @jrafanie However…

Slide 74

Slide 74 text

@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?

Slide 75

Slide 75 text

@chrisarcand @jrafanie AND… *drumroll*

Slide 76

Slide 76 text

@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!

Slide 77

Slide 77 text

@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):

Slide 78

Slide 78 text

@chrisarcand @jrafanie Travis CI before: ~30 minutes ~45 minutes Travis CI after (2 cores):

Slide 79

Slide 79 text

Thank you Questions? chrisarcand chrisarcand www.chrisarcand.com jrafanie jrafanie Slides available here