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

Shared Examples

Shared Examples

Stephanie Viccari

January 05, 2021
Tweet

More Decks by Stephanie Viccari

Other Decks in Education

Transcript

  1. What are Shared Examples? Shared examples are used to to

    create reusable test examples. They are often used to: • DRY up tests • Enforce an interface 2
  2. Feature Request #1 class RevenueByLocationReport def description "Revenue for each

    location" end def headers ["Location", "Revenue"] end end 3
  3. Feature Request #1 - Tests RSpec.describe RevenueByLocationReport do describe "#description"

    do it "returns a description of the reports purpose" do report = RevenueByLocationReport.new description = report.description expect(description).to eq("Revenue for each location") end end describe "#headers" do it "returns descriptive headers" do report = RevenueByLocationReport.new headers = report.headers expect(headers).to eq(["Location", "Revenue"]) end end end 4
  4. Feature Request #2 class RevenueByProjectReport def description "Revenue for each

    project" end def headers ["Project", "Location", "Revenue"] end end 5
  5. Feature Request #2 - Tests RSpec.describe RevenueByProjectReport do describe "#description"

    do it "returns a description of the reports purpose" do report = RevenueByProjectReport.new description = report.description expect(description).to eq("Revenue for each project") end end describe "#headers" do it "returns descriptive headers" do report = RevenueByProjectReport.new headers = report.headers expect(headers).to eq(["Project", "Location", "Revenue"]) end end end 6
  6. Similar Test Examples RSpec.describe RevenueByLocationReport do describe "#description" do it

    "returns a description of the reports purpose" do # verify the description end end describe "#headers" do it "returns descriptive headers" do # verify the headers end end end RSpec.describe RevenueByProjectReport do describe "#description" do it "returns a description of the reports purpose" do # verify the description end end describe "#headers" do it "returns descriptive headers" do # verify the headers end end end 7
  7. Defining a Shared Example # spec/shared_examples/report_examples.rb RSpec.shared_examples "a report" do

    describe "#description" do it "returns a String describing the reports purpose" do report = described_class.new expect(report.description).to be_a String end end describe "#headers" do it "returns an Array of descriptive headers" do report = described_class.new expect(report.headers).to be_an Array expect(report.headers).to be_present end end end 8
  8. described_class - Given a Class RSpec.describe User do describe "described_class"

    do it "references the class being described" do puts described_class # => User puts described_class.new # => #<User:0x00007fe605872510> expect(described_class).to eq(User) # => true end end end When a class name is passed to describe, the class is available via the described_class method. 9
  9. described_class - Given a String RSpec.describe "User" do describe "described_class"

    do it "references the described class" do described_class.new # => error: undefined method on nil puts described_class.class # => NilClass end end end When a string is passed to describe, described_class is nil. 10
  10. Implicit Subject RSpec.describe Array do describe "implicit subject" do context

    "given a class" do it "sets the subject to an instance of the class" do puts subject.inspect #=> [] expect(subject).to be_empty end end end end When a Class is passed to describe, an instance of the Class is available via subject 11
  11. Explicit Subject RSpec.describe Array do subject { [1,2,3] } describe

    "explicit subject" do it "sets the subject to an instance of the class" do expect(subject).to eq([1,2,3]) end end end You can override the implicit subject by explicitly setting the subject. 13
  12. RSpec Subject vs Let Subject • memoized helper • Set

    by RSpec (or the test author) • Lazy-evaluated • Used for one-line syntax * • Used for custom matchers # * Example of one-line syntax: RSpec.describe Array do it { is_expected.to eq([]) } # is_expected calls expect(subject).to eq([]) end Let • memoized helper • Set by the test author • Lazy-evaluated • used to reduce duplication • used to create state that is reset between each example 14
  13. Report Shared Examples # spec/shared_examples/report_examples.rb RSpec.shared_examples "a report" do describe

    "description" do it "returns a String describing the reports purpose" do report = described_class.new expect(report.description).to be_a String end end describe "headers" do it "returns an Array of descriptive headers" do report = described_class.new expect(report.headers).to be_an Array expect(report.headers).to be_present end end end 15
  14. Defining a Shared Example - Refactor # spec/shared_examples/report_examples.rb RSpec.shared_examples "a

    report" do let(:report) { described_class.new } describe "description" do it "returns a String describing the reports purpose" do expect(report.description).to be_a String end end describe "headers" do it "returns an Array of descriptive headers" do expect(report.headers).to be_an Array expect(report.headers).to be_present end end end 16
  15. Revisit the Tests RSpec.describe RevenueByLocationReport do describe "description" do it

    "returns a description of the reports purpose" do # verify the description end end describe "headers" do it "returns descriptive headers" do # verify the headers end end end RSpec.describe RevenueByProjectReport do describe "description" do it "returns a description of the reports purpose" do # verify the description end end describe "headers" do it "returns descriptive headers" do # verify the headers end end end 17
  16. Use the Shared Example RSpec.describe RevenueByLocationReport do it_behaves_like "a report"

    end RSpec.describe RevenueByProjectReport do it_behaves_like "a report" end 18
  17. Calling a Shared Example include_examples "a report" (includes the example

    in the current context) # RevenueByLocationReport # description # returns a String describing the reports purpose it_behaves_like "a report" (includes the example in a nested context) # RevenueByLocationReport # behaves like a report # description # returns a String describing the reports purpose it_should_behave_like "a report" (includes the example in a nested context) # RevenueByLocationReport # it should behave like a report # description # returns a String describing the reports purpose 19
  18. Enforce an interface Shared examples are used to to create

    reusable test examples. They are often used to: • DRY up tests • Enforce an interface 20
  19. Enforce an interface module MarkdownDescription MARKDOWN_TEMPLATE = "string://<div class='markdown'><%= @body

    %></div>" def description_html Kramdown::Document.new( process_pasted_content, template: MARKDOWN_TEMPLATE, ).to_html end private HEADER_MATCH = /\n\n([A-Z &]+:)\n/ HEADER_MARKDOWN = "\n\n\\1\n\n".freeze LIST_MATCH = /\n(?:•|\*)/ LIST_MARKDOWN = "\n* ".freeze def process_pasted_content description.to_s. gsub(HEADER_MATCH, HEADER_MARKDOWN). gsub(LIST_MATCH, LIST_MARKDOWN) end end 22
  20. Define a Shared Example RSpec.shared_examples "a record with markdown description"

    do describe "#description_html" do it "method exists" do record = described_class.new expect(record).to respond_to(:description_html) end end end 23
  21. Enforce an interface - Tests RSpec.describe Product do it_behaves_like "a

    record with a markdown description" end RSpec.describe Location do it_behaves_like "a record with a markdown description" end 24
  22. Shared Examples Review • Help us reuse common test examples

    by extracting test examples to a shared location • Use shared examples sparingly as they increase indirection • Shared Examples are highly general and portable • Store shared examples within a spec/support/ shared_examples/ directory (example: /spec/shared_examples/shared_example.rb) 25
  23. shared_context vs shared_examples shared_context is an alias for shared_examples shared_example

    is used to extract a test example shared_context is used to extract test setup 26
  24. Resources RSpec Shared Examples - https://relishapp.com/rspec/rspec- core/docs/example-groups/shared- examples RSpec shared_context

    - https://github.com/rspec/rspec-core/ blob/ 922b4a6dc5ca397df19f5f282f2f6a2465b 2e24a/lib/rspec/core/ shared_example_group.rb#L100 27