{"id":8222,"date":"2016-12-21T23:01:01","date_gmt":"2016-12-21T20:01:01","guid":{"rendered":"http:\/\/railsware.com\/blog\/?p=8222"},"modified":"2021-08-11T18:20:07","modified_gmt":"2021-08-11T15:20:07","slug":"using-configurable-shared-examples-in-rspec","status":"publish","type":"post","link":"https:\/\/railsware.com\/blog\/using-configurable-shared-examples-in-rspec\/","title":{"rendered":"Using Configurable Shared Examples in RSpec"},"content":{"rendered":"\n<p class=\"intro-text\">Shared examples are a good tool to describe some complex behavior and reuse it across different parts of a spec. Things get more complicated when you have the same behavior, but it has some slight variations for different contexts. In this case it&#8217;s easy to end-up having a bunch of separate one-spec behaviors instead of having some way to adjust those peculiarities in a simple way.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Example<\/h3>\n\n\n\n<p>To be more specific, let&#8217;s create a simple example. We want to describe behavior of different dogs. Every one of them can perform some common actions, but there&#8217;re also certain actions they don&#8217;t perform at all. For example, Snuff can bark and growl, but doesn&#8217;t like to jump. On the other hand, Scooby-Doo likes to jump and flee, but doesn&#8217;t growl. Scrappy-Doo is too small to bark, but he likes to growl a lot.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1200\" height=\"620\" src=\"https:\/\/railsware.com\/blog\/wp-content\/uploads\/2016\/12\/blog-dog-pic-big.png\" alt=\"\" class=\"wp-image-8247\" srcset=\"https:\/\/railsware.com\/blog\/wp-content\/uploads\/2016\/12\/blog-dog-pic-big.png 1200w, https:\/\/railsware.com\/blog\/wp-content\/uploads\/2016\/12\/blog-dog-pic-big-300x155.png 300w, https:\/\/railsware.com\/blog\/wp-content\/uploads\/2016\/12\/blog-dog-pic-big-768x397.png 768w, https:\/\/railsware.com\/blog\/wp-content\/uploads\/2016\/12\/blog-dog-pic-big-1024x529.png 1024w\" sizes=\"auto, (max-width: 1200px) 100vw, 1200px\" \/><\/figure>\n\n\n\n<pre class=\"wp-block-preformatted lang:ruby\">class Dog &lt; Struct.new(:able_to_growl?, :able_to_bark?, :able_to_jump?, :able_to_flee?)\nend\nsnuff = Dog.new(true, true, false, true)\nscooby_doo = Dog.new(false, true, true, true)\nscrappy_doo = Dog.new(true, false, true, true)\n\nshared_examples 'a normal dog' do\n  it { is_expected.to be_able_to_growl }\n  it { is_expected.to be_able_to_bark }\n  it { is_expected.to be_able_to_jump }\n  it { is_expected.to be_able_to_flee }\nend\n\ndescribe 'Dogs behavior' do\n  context 'Snuff' do\n    subject(:snuff) { Dog.new(true, true, false, true) }\n    it_behaves_like 'a normal dog'\n  end\n\n  context 'Scooby-Doo' do\n    subject(:scooby_doo) { Dog.new(false, true, true, true) }\n    it_behaves_like 'a normal dog'\n  end\n\n  context 'Scrappy-Doo' do\n    subject(:scrappy_doo) { Dog.new(true, false, true, true) }\n    it_behaves_like 'a normal dog'\n  end\nend\n<\/pre>\n\n\n\n<p>The spec looks great, but we should adjust &#8216;a normal dog&#8217; behavior for each character, otherwise each context will fail due to unsupported ability.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Using params in shared_examples<\/h2>\n\n\n\n<p>Luckily, RSpec supports accepting params for <span class=\"crayon-inline\">shared_examples<\/span>, so we can rewrite the spec like this:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted lang:ruby\">shared_examples 'a normal dog' do |growl: true, bark: true, jump: true|\n  it { is_expected.to be_able_to_growl } if growl\n  it { is_expected.to be_able_to_bark } if bark\n  it { is_expected.to be_able_to_jump } if jump\n  it { is_expected.to be_able_to_flee }\nend\n\ndescribe 'Dogs behavior' do\n  context 'Snuff' do\n    subject(:snuff) { Dog.new(true, true, false, true) }\n    it_behaves_like 'a normal dog', jump: false\n  end\n\n  context 'Scooby-Doo' do\n    subject(:scooby_doo) { Dog.new(false, true, true, true) }\n    it_behaves_like 'a normal dog', growl: false\n  end\n\n  context 'Scrappy-Doo' do\n    subject(:scrappy_doo) { Dog.new(true, false, true, true) }\n    it_behaves_like 'a normal dog', bark: false\n  end\nend\n<\/pre>\n\n\n\n<p>Now all specs pass successfully. We use <span class=\"crayon-inline\">shared_examples<\/span> params to pass configuration values and adjust the way it matches. Note that this won&#8217;t work with usual let-bindings. Shared examples are created and configured at the &#8220;compile time&#8221;, while let-bindings can be used only at &#8220;run time&#8221;.<\/p>\n\n\n\n<p>Another nice thing about <span class=\"crayon-inline\">shared_examples<\/span> params &#8211; is that they are accessible within tests at run-time. So, for very simple specs it&#8217;s possible to use them instead of let-bound values. Here is a simple example:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted lang:ruby\">shared_examples 'multiplies two numbers' do |x, y, result:|\n  it 'returns correct result' do\n    expect(x * y).to eq(result)\n  end\nend\n\ndescribe 'Multiplication' do\n  it_behaves_like 'multiplies two numbers', 2, 2, result: 4\n  it_behaves_like 'multiplies two numbers', 3, 5, result: 15\n  it_behaves_like 'multiplies two numbers', 10, 5, result: 50\nend\n<\/pre>\n\n\n\n<p>So, instead of using 3 let-bindings for the arguments and the result, we get a very succinct way of writing simple specs.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Shared examples are a good tool to describe some complex behavior and reuse it across different parts of a spec. Things get more complicated when you have the same behavior, but it has some slight variations for different contexts. In this case it&#8217;s easy to end-up having a bunch of separate one-spec behaviors instead of&#8230;<\/p>\n","protected":false},"author":25,"featured_media":9420,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[3],"tags":[],"coauthors":["Sergii Boiko"],"class_list":["post-8222","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-development"],"acf":[],"aioseo_notices":[],"categories_data":[{"name":"Engineering","link":"https:\/\/railsware.com\/blog?category=development"}],"post_thumbnails":"https:\/\/railsware.com\/blog\/wp-content\/uploads\/2016\/12\/blog-dog-pic-big.png","amp_enabled":true,"_links":{"self":[{"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/posts\/8222","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/users\/25"}],"replies":[{"embeddable":true,"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/comments?post=8222"}],"version-history":[{"count":25,"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/posts\/8222\/revisions"}],"predecessor-version":[{"id":13933,"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/posts\/8222\/revisions\/13933"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/media\/9420"}],"wp:attachment":[{"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/media?parent=8222"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/categories?post=8222"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/tags?post=8222"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/coauthors?post=8222"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}