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

Your Test Suite is Making Too Many Database Calls!

Your Test Suite is Making Too Many Database Calls!

Originally given at RailsConf 2022. Recording at https://www.youtube.com/watch?v=LOlG4kqfwcg

On a recent project, I sped up a test suite 15% by making a change to a single factory. This suite, like many others (including yours!), was making way too many database calls. It’s so easy to accidentally add extra queries to factories and test setup and these can compound to shockingly large numbers.

The chaos is your opportunity! Learn to profile and fix hot spots, build big-picture understanding through diagrams, and write code that is resistant to extraneous queries. This talk will equip you to take back control of your build times and maybe impress your teammates in the process.

Joël Quenneville

May 23, 2022
Tweet

More Decks by Joël Quenneville

Other Decks in Programming

Transcript

  1. View Slide

  2. View Slide

  3. View Slide

  4. View Slide

  5. View Slide

  6. View Slide

  7. View Slide

  8. # BAD

    it "#full_name concatenates first and last name" do

    user = create(

    :user,

    first_name: "Joël",

    last_name: "Quenneville"

    )

    expect(user.full_name).to eq "Joël Quenneville"

    end

    View Slide

  9. # GOOD

    it "#full_name concatenates first and last name" do

    user = User.new(

    first_name: "Joël",

    last_name: "Quenneville"

    )

    expect(user.full_name).to eq "Joël Quenneville"

    end

    View Slide

  10. View Slide

  11. View Slide

  12. View Slide

  13. View Slide

  14. View Slide

  15. View Slide

  16. View Slide

  17. View Slide

  18. View Slide

  19. let(:user1) { create(:user) }

    let(:user2) { create(:user) }

    let(:organization) { create(:organization), users: [user1, user2] }



    # this test only needs an org

    it "does something"



    # this test needs and org + some users

    it "does something whith multiple users"

    View Slide

  20. View Slide

  21. View Slide

  22. View Slide

  23. View Slide

  24. View Slide

  25. let(:contact) { create(:contact) }

    let(:user) { create(:user, admin: true, contacts: [contact]) }



    # NEEDS 1 query

    # MAKES 3 queries (2x CREATE + 1x UPDATE)

    it "does something for a regular user" do

    user.update(admin: false)

    ...

    end



    # NEEDS 2 queries

    # MAKES 2 queries

    it "does something for an admin with contact info"

    View Slide

  26. View Slide

  27. # this test only needs an org

    it "does something" do

    org = create(:organization)



    ...

    end



    # this test needs and org + some users

    it "does something whith multiple users" do

    user1 = create(:user)

    user2 = create(:user)

    org = create(:organization, users: [user1, user2]



    ...

    end

    View Slide

  28. View Slide

  29. View Slide

  30. View Slide

  31. View Slide

  32. View Slide

  33. View Slide

  34. factory :organization do

    members { create_list(:user, 3 }

    sequence(:name) { |n| "Organization #{n}" }
    end

    View Slide

  35. it "does something" do

    # this makes 4 INSERT queries

    org = create(:organization)

    expect(org.do_something).to eq "something"

    end

    View Slide

  36. View Slide

  37. factory :organization do

    members { create_list(:user, 3 }

    sequence(:name) { |n| "Organization #{n}" }
    end



    factory :user do

    association :organization

    end

    View Slide

  38. it "does something given a user" do

    user = create(:user)

    service = MyService.new(user)



    expect(service.call()).to eq "something"

    end

    View Slide

  39. View Slide

  40. View Slide

  41. factory :organization do

    sequence(:name) { |n| "Organization #{n}" }
    end

    View Slide

  42. factory :organization do

    sequence(:name) { |n| "Organization #{n}" }



    trait :with_users do

    members { create_list(:user, 3 }

    end
    end

    View Slide

  43. View Slide

  44. ActiveSupport::Notifications.

    subscribe("factory_bot.run_factory")

    View Slide

  45. [TEST PROF INFO] Factories usage



    Total: 15285

    Total top-level: 10286

    Total time: 04:31.222 (out of 07.16.124)

    Total uniq factories: 119



    total top-level total time time per call top-level time name

    6091 2715 115.7671s 0.0426s 50.2517s user

    2142 2098 93.3152s 0.0444s 92.1915s post

    ...

    View Slide

  46. View Slide

  47. View Slide

  48. class Organization < ApplicationRecord

    before_create :generate_admin



    def generate_admin

    admin = User.create!(

    email: "[email protected]#{organization.domain}",

    admin: true

    )

    self.admin = admin

    end

    end

    View Slide

  49. it "does something" do

    admin = create(:user, admin: true)

    org = create(:organization, admin: admin)



    # ...

    end

    View Slide

  50. View Slide

  51. View Slide

  52. it "does something" do

    admin = create(:user, admin: true)

    org = create(:organization)



    # CODE SMELL!

    # This now makes 3 INSERTs + 1 UPDATE calls

    org.update(admin: admin)



    # ...

    end

    View Slide

  53. View Slide

  54. View Slide

  55. View Slide

  56. factory_bot_results = {}

    config.before(:suite) do

    ActiveSupport::Notifications.subscribe("factory_bot.run_factory") do |name, start,
    finish, id, payload|

    # aggregate results

    end



    config.after(:suite) do

    puts factory_bot_results

    end

    View Slide

  57. View Slide

  58. View Slide

  59. View Slide

  60. View Slide

  61. factory :organization_with_events do

    staff { create_list(:staff, 1) }

    venues { create_list(:venue, 1)



    after(:build) do |org|

    create_list(:event, 3, venue: org.venues.first)

    end

    end

    View Slide

  62. factory :organization_with_events do

    staff { create_list(:staff, 1) }

    venues { create_list(:venue, 1)



    after(:build) do |org|

    create_list(:event, 3, venue: org.venues.first, staff: org.staff.first)

    end

    end

    View Slide

  63. View Slide

  64. View Slide

  65. View Slide

  66. View Slide

  67. View Slide

  68. View Slide