{"id":34,"date":"2014-10-10T15:01:37","date_gmt":"2014-10-10T15:01:37","guid":{"rendered":"https:\/\/redbooth.com\/engineering\/?p=34"},"modified":"2014-10-10T15:58:08","modified_gmt":"2014-10-10T15:58:08","slug":"rspec-testing-guidelines-redbooth","status":"publish","type":"post","link":"https:\/\/redbooth.com\/engineering\/guidelines\/rspec-testing-guidelines-redbooth","title":{"rendered":"RSpec testing guidelines at Redbooth"},"content":{"rendered":"<p>As your code base grows, so does the amount of testing you need to do.<br \/>\nIt is really easy to end up having dozens of different testing strategies,<br \/>\nsome of them of dubious efficiency. If your team is also growing having <a title=\"Better Specs\" href=\"http:\/\/betterspecs.org\/\">testing guidelines<\/a>\u00a0is a must.<\/p>\n<p>This is ours at <a title=\"Redbooth\" href=\"https:\/\/redbooth.wpengine.com\" target=\"_blank\">Redbooth<\/a>:<\/p>\n<h3>Understand what a test is<\/h3>\n<p>It&#8217;s not relevant whether you are doing a smoke or a unit test. Both programs and functions present the following characteristics:<\/p>\n<ul>\n<li>An input<\/li>\n<li>An initial state<\/li>\n<li>An output<\/li>\n<li>A final state<\/li>\n<\/ul>\n<p>As a tester you want to ensure that:<\/p>\n<blockquote><p>given any of the known combinations of inputs and initial states they lead to <strong>correct<\/strong>\u00a0outputs and\/or final states.<\/p><\/blockquote>\n<p>Keeping this in mind results in writing better tests!<br \/>\nTesting the APIs is a tempting strategy but sticking to test the correctness of the different I\/O and state combinations is the most reliable one.<\/p>\n<p>If you are unit-testing pure methods you can ignore testing its state.<br \/>\nApplications talk to the external world, interacting with databases and http services imply unavoidable side effects.<br \/>\nIsolating those interactions and sticking with pure methods will lead to simpler tests.<\/p>\n<h3>How to define a test in RSpec<\/h3>\n<p>RSpec provides a convenient DSL to write tests in a declarative fashion, but most developers do not know <em>when<\/em>\u00a0to use <em>what<\/em>. Knowing the tool and using the DSL correctly helps to reduce the testing strategy fragmentation.<\/p>\n<h4>Describe<\/h4>\n<p>With `describe` you define what do you want to test. This can be a string or a class name. For unit testing we use one `describe` per method.<\/p>\n<pre>describe Task do\r\n  describe '#resolve'\r\nend\r\n<\/pre>\n<h4>It<\/h4>\n<p>With `it` you write your expectations. You test both the output and the final state of the element you are describing.<\/p>\n<pre>describe Task do\r\n  describe '#resolve' do\r\n    it 'returns true'\r\n    it 'changes the archived flag to true'\r\n    it 'increases counter of resolved tasks on the task list'\r\n    it 'returns false if already resolved'\r\n    it 'does not increase the counter of resolved tasks on the task list if already resolved'\r\n  end\r\nend\r\n<\/pre>\n<h4>Subject<\/h4>\n<p>`subject` is one of the most misused features of RSpec and one of the most useful ones.<br \/>\n`subject` is the element we are evaluating. Its called for each `it` and its result is available implicitly.<\/p>\n<pre>describe Task do\r\n  describe '#resolve' do\r\n    subject { FactoryGirl.new(:task).resolve }\r\n    it 'returns true'\r\n    it 'changes the archived flag to true'\r\n    it 'increases counter of resolved tasks on the task list'\r\n    it 'returns false if already resolved'\r\n    it 'does not increase the counter of resolved tasks on the task list if already resolved'\r\n  end\r\nend\r\n<\/pre>\n<h4>Let<\/h4>\n<p>With `let` we declare lazy variables. Those describe the state of our system and or the inputs\/outputs.<br \/>\nDo not ever have more than one statement on your `let`. Use `before` for that.<\/p>\n<pre>describe Task do\r\n  let(:task_list) { FactoryGirl.new(:task_list) }\r\n  let(:task) { FactoryGirl.new(:task, task_list: task_list) }\r\n\r\n  describe '#resolve' do\r\n    subject { task.resolve }\r\n    it 'returns true if succeeded'\r\n    it 'changes the archived flag to true if succeeded'\r\n    it 'increases counter of resolved tasks on the task list if succeeded'\r\n    it 'returns false if failed'\r\n    it 'does not change the archived flag to true if failed'\r\n    it 'does not increase the counter of resolved tasks on the task list if failed'\r\n  end\r\nend\r\n<\/pre>\n<h4>Context<\/h4>\n<p>Whenever you find yourself writing the words <em>if<\/em>\u00a0or <em>when<\/em>\u00a0in your examples you can split your spec into contexts. Those help you define groups of inputs or initial state.<\/p>\n<pre>describe Task do\r\n  let(:task_list) { FactoryGirl.new(:task_list) }\r\n  let(:task) { FactoryGirl.new(:task, resolved: resolved, task_list: task_list) }\r\n\r\n  describe '#resolve' do\r\n    subject { task.resolve }\r\n\r\n    context 'when the task is active' do\r\n      let(:resolved) { false }\r\n\r\n      it 'returns true'\r\n      it 'changes the archived flag to true'\r\n      it 'increases counter of resolved tasks on the task list'\r\n    end\r\n\r\n    context 'when the task is already resolved' do\r\n      let(:resolved) { true }\r\n\r\n      it 'returns false'\r\n      it 'does not increase the counter of resolved tasks on the task list'\r\n    end\r\n  end\r\nend\r\n<\/pre>\n<h4>Implicit subjects<\/h4>\n<p>With your spec organized with `describe`, `context` and `let` you can implement the examples. Most of the tests are over-described.<br \/>\nOnly describe the expected output or how the state must change after evaluating the `subject`. Use implicit `it` syntax for shorter output-checking specs:<\/p>\n<pre>describe Task do\r\n  let(:task_list) { FactoryGirl.new(:task_list) }\r\n  let(:task) { FactoryGirl.new(:task, resolved: resolved, task_list: task_list) }\r\n\r\n  describe '#resolve' do\r\n    subject { task.resolve }\r\n\r\n    context 'when the task is active' do\r\n      let(:resolved) { false }\r\n\r\n      it { is_expected.to be_true }\r\n      it { expect(task.resolved).to be_true }\r\n      it { expect(task_list.resolved_tasks_count).to eq(1) }\r\n    end\r\n\r\n    context 'when the task is already resolved' do\r\n      let(:resolved) { true }\r\n\r\n      it { is_expected.to be_false }\r\n      it { expect(task_list.resolved_tasks_count).to eq(1) }\r\n    end\r\n  end\r\nend\r\n<\/pre>\n<h4>Stubbing<\/h4>\n<p>Whenever you are on need to stub a method, place your `allow` statements inside a `before` clause. This is useful when the desired initial state is hard to reproduce with factories. But remember to not stub everything; you don&#8217;t need a test that describes the implementation!<\/p>\n<pre>describe Task do\r\n  let(:task_list) { FactoryGirl.new(:task_list) }\r\n  let(:task) { FactoryGirl.new(:task, resolved: resolved, task_list: task_list) }\r\n\r\n  describe '#resolve' do\r\n    subject { task.resolve }\r\n\r\n    context 'when the task_list is full' do\r\n      before { allow(task_list).to receive(:full?).and_return(true) }\r\n\r\n      it { is_expected.to be_false }\r\n      it { expect(task_list.resolved_tasks_count).to eq(0) }\r\n    end\r\n\r\n    context 'when the task is active' do\r\n      let(:resolved) { false }\r\n\r\n      it { is_expected.to be_true }\r\n      it { expect(task.resolved).to be_true }\r\n      it { expect(task_list.resolved_tasks_count).to eq(1) }\r\n    end\r\n\r\n    context 'when the task is already resolved' do\r\n      let(:resolved) { true }\r\n\r\n      it { is_expected.to be_false }\r\n      it { expect(task_list.resolved_tasks_count).to eq(1) }\r\n    end\r\n  end\r\nend\r\n<\/pre>\n<h3>Final thoughts<\/h3>\n<p>Your team needs testing guidelines.<br \/>\nTest your expectations around the output and the final state of your system.<br \/>\nInvest time in knowing your testing libraries.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>As your code base grows, so does the amount of testing you need to do. It is really easy to end up having dozens of different testing strategies, some of them of dubious efficiency. If your team is also growing having testing guidelines\u00a0is a must. This is ours at Redbooth: Understand what a test is <a class=\"read-more\" href=\"https:\/\/redbooth.com\/engineering\/guidelines\/rspec-testing-guidelines-redbooth\">&hellip;&nbsp;<span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":37,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[8],"tags":[11,10,12,9,13],"class_list":["post-34","post","type-post","status-publish","format-standard","hentry","category-guidelines","tag-rails","tag-rspec","tag-tdd","tag-test","tag-testing"],"_links":{"self":[{"href":"https:\/\/redbooth.com\/engineering\/wp-json\/wp\/v2\/posts\/34","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/redbooth.com\/engineering\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/redbooth.com\/engineering\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/redbooth.com\/engineering\/wp-json\/wp\/v2\/users\/37"}],"replies":[{"embeddable":true,"href":"https:\/\/redbooth.com\/engineering\/wp-json\/wp\/v2\/comments?post=34"}],"version-history":[{"count":0,"href":"https:\/\/redbooth.com\/engineering\/wp-json\/wp\/v2\/posts\/34\/revisions"}],"wp:attachment":[{"href":"https:\/\/redbooth.com\/engineering\/wp-json\/wp\/v2\/media?parent=34"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/redbooth.com\/engineering\/wp-json\/wp\/v2\/categories?post=34"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/redbooth.com\/engineering\/wp-json\/wp\/v2\/tags?post=34"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}