diff --git a/Gemfile b/Gemfile
index 58af6c51b7744472620a49566bdd4a39191f5c5c..decbcfe63757bee990d74bf1896fa570c33b7ce9 100644
--- a/Gemfile
+++ b/Gemfile
@@ -293,6 +293,7 @@ group :development, :test do
   gem 'spinach-rails', '~> 0.2.1'
   gem 'spinach-rerun-reporter', '~> 0.0.2'
   gem 'rspec_profiling', '~> 0.0.5'
+  gem 'rspec-set', '~> 0.1.3'
 
   # Prevent occasions where minitest is not bundled in packaged versions of ruby (see #3826)
   gem 'minitest', '~> 5.7.0'
diff --git a/Gemfile.lock b/Gemfile.lock
index 83c9bb57c3b27c7d326f98313a9b6635f35012f4..fb11f5901106d5e76942e9db13d12846a70f53aa 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -659,6 +659,7 @@ GEM
       rspec-support (~> 3.5.0)
     rspec-retry (0.4.5)
       rspec-core
+    rspec-set (0.1.3)
     rspec-support (3.5.0)
     rspec_profiling (0.0.5)
       activerecord
@@ -989,6 +990,7 @@ DEPENDENCIES
   rqrcode-rails3 (~> 0.1.7)
   rspec-rails (~> 3.5.0)
   rspec-retry (~> 0.4.5)
+  rspec-set (~> 0.1.3)
   rspec_profiling (~> 0.0.5)
   rubocop (~> 0.47.1)
   rubocop-rspec (~> 1.15.0)
diff --git a/doc/development/testing.md b/doc/development/testing.md
index 2c7154f1deadbc980c9eb42c00ac2f073bd0ff05..9b0b9808827ce1b84ddf2706c620916dfbd7dc59 100644
--- a/doc/development/testing.md
+++ b/doc/development/testing.md
@@ -202,6 +202,7 @@ Please consult the [dedicated "Frontend testing" guide](./fe_guide/testing.md).
 - Try to follow the [Four-Phase Test][four-phase-test] pattern, using newlines
   to separate phases.
 - Try to use `Gitlab.config.gitlab.host` rather than hard coding `'localhost'`
+- On `before` and `after` hooks, prefer it scoped to `:context` over `:all`
 
 [four-phase-test]: https://robots.thoughtbot.com/four-phase-test
 
@@ -225,6 +226,20 @@ so we need to set some guidelines for their use going forward:
 
 [lets-not]: https://robots.thoughtbot.com/lets-not
 
+#### `set` variables
+
+In some cases there is no need to recreate the same object for tests again for
+each example. For example, a project is needed to test issues on the same
+project, one project will do for the entire file. This can be achieved by using
+`set` in the same way you would use `let`.
+
+`rspec-set` only works on ActiveRecord objects, and before new examples it
+reloads or recreates the model, _only_ if needed. That is, when you changed
+properties or destroyed the object.
+
+There is one gotcha; you can't reference a model defined in a `let` block in a
+`set` block.
+
 ### Time-sensitive tests
 
 [Timecop](https://github.com/travisjeffery/timecop) is available in our
diff --git a/spec/finders/issues_finder_spec.rb b/spec/finders/issues_finder_spec.rb
index 231fd85c4649f1524dd88a43175540d5b8eff70b..a1ae1d746afbd935131c3f3b98253bfcf34582bf 100644
--- a/spec/finders/issues_finder_spec.rb
+++ b/spec/finders/issues_finder_spec.rb
@@ -1,24 +1,24 @@
 require 'spec_helper'
 
 describe IssuesFinder do
-  let(:user) { create(:user) }
-  let(:user2) { create(:user) }
-  let(:project1) { create(:empty_project) }
-  let(:project2) { create(:empty_project) }
-  let(:milestone) { create(:milestone, project: project1) }
-  let(:label) { create(:label, project: project2) }
-  let(:issue1) { create(:issue, author: user, assignee: user, project: project1, milestone: milestone, title: 'gitlab') }
-  let(:issue2) { create(:issue, author: user, assignee: user, project: project2, description: 'gitlab') }
-  let(:issue3) { create(:issue, author: user2, assignee: user2, project: project2, title: 'tanuki', description: 'tanuki') }
+  set(:user) { create(:user) }
+  set(:user2) { create(:user) }
+  set(:project1) { create(:empty_project) }
+  set(:project2) { create(:empty_project) }
+  set(:milestone) { create(:milestone, project: project1) }
+  set(:label) { create(:label, project: project2) }
+  set(:issue1) { create(:issue, author: user, assignee: user, project: project1, milestone: milestone, title: 'gitlab') }
+  set(:issue2) { create(:issue, author: user, assignee: user, project: project2, description: 'gitlab') }
+  set(:issue3) { create(:issue, author: user2, assignee: user2, project: project2, title: 'tanuki', description: 'tanuki') }
 
   describe '#execute' do
-    let(:closed_issue) { create(:issue, author: user2, assignee: user2, project: project2, state: 'closed') }
-    let!(:label_link) { create(:label_link, label: label, target: issue2) }
+    set(:closed_issue) { create(:issue, author: user2, assignee: user2, project: project2, state: 'closed') }
+    set(:label_link) { create(:label_link, label: label, target: issue2) }
     let(:search_user) { user }
     let(:params) { {} }
     let(:issues) { IssuesFinder.new(search_user, params.reverse_merge(scope: scope, state: 'opened')).execute }
 
-    before do
+    before(:context) do
       project1.team << [user, :master]
       project2.team << [user, :developer]
       project2.team << [user2, :developer]
diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb
index 784fd1ff885ad389b100305cf975d42b324cbf67..3ca13111acb0bb898f112c8b0d49c36126b07d41 100644
--- a/spec/requests/api/issues_spec.rb
+++ b/spec/requests/api/issues_spec.rb
@@ -3,14 +3,17 @@
 describe API::Issues do
   include EmailHelpers
 
-  let(:user)        { create(:user) }
+  set(:user) { create(:user) }
+  set(:project) do
+    create(:empty_project, :public, creator_id: user.id, namespace: user.namespace)
+  end
+
   let(:user2)       { create(:user) }
   let(:non_member)  { create(:user) }
-  let(:guest)       { create(:user) }
-  let(:author)      { create(:author) }
-  let(:assignee)    { create(:assignee) }
+  set(:guest)       { create(:user) }
+  set(:author)      { create(:author) }
+  set(:assignee)    { create(:assignee) }
   let(:admin)       { create(:user, :admin) }
-  let!(:project)    { create(:empty_project, :public, creator_id: user.id, namespace: user.namespace ) }
   let(:issue_title)       { 'foo' }
   let(:issue_description) { 'closed' }
   let!(:closed_issue) do
@@ -43,19 +46,19 @@
            title: issue_title,
            description: issue_description
   end
-  let!(:label) do
+  set(:label) do
     create(:label, title: 'label', color: '#FFAABB', project: project)
   end
   let!(:label_link) { create(:label_link, label: label, target: issue) }
-  let!(:milestone) { create(:milestone, title: '1.0.0', project: project) }
-  let!(:empty_milestone) do
+  set(:milestone) { create(:milestone, title: '1.0.0', project: project) }
+  set(:empty_milestone) do
     create(:milestone, title: '2.0.0', project: project)
   end
   let!(:note) { create(:note_on_issue, author: user, project: project, noteable: issue) }
 
   let(:no_milestone_title) { URI.escape(Milestone::None.title) }
 
-  before do
+  before(:all) do
     project.team << [user, :reporter]
     project.team << [guest, :guest]
   end
@@ -70,6 +73,8 @@
     end
 
     context "when authenticated" do
+      let(:first_issue) { json_response.first }
+
       it "returns an array of issues" do
         get api("/issues", user)
 
@@ -79,46 +84,46 @@
       end
 
       it 'returns an array of closed issues' do
-        get api('/issues?state=closed', user)
+        get api('/issues', user), state: :closed
 
         expect_paginated_array_response(size: 1)
-        expect(json_response.first['id']).to eq(closed_issue.id)
+        expect(first_issue['id']).to eq(closed_issue.id)
       end
 
       it 'returns an array of opened issues' do
-        get api('/issues?state=opened', user)
+        get api('/issues', user), state: :opened
 
         expect_paginated_array_response(size: 1)
-        expect(json_response.first['id']).to eq(issue.id)
+        expect(first_issue['id']).to eq(issue.id)
       end
 
       it 'returns an array of all issues' do
-        get api('/issues?state=all', user)
+        get api('/issues', user), state: :all
 
         expect_paginated_array_response(size: 2)
-        expect(json_response.first['id']).to eq(issue.id)
+        expect(first_issue['id']).to eq(issue.id)
         expect(json_response.second['id']).to eq(closed_issue.id)
       end
 
       it 'returns issues matching given search string for title' do
-        get api("/issues?search=#{issue.title}", user)
+        get api("/issues", user), search: issue.title
 
         expect_paginated_array_response(size: 1)
         expect(json_response.first['id']).to eq(issue.id)
       end
 
       it 'returns issues matching given search string for description' do
-        get api("/issues?search=#{issue.description}", user)
+        get api("/issues", user), search: issue.description
 
         expect_paginated_array_response(size: 1)
-        expect(json_response.first['id']).to eq(issue.id)
+        expect(first_issue['id']).to eq(issue.id)
       end
 
       it 'returns an array of labeled issues' do
-        get api("/issues?labels=#{label.title}", user)
+        get api("/issues", user), labels: label.title
 
         expect_paginated_array_response(size: 1)
-        expect(json_response.first['labels']).to eq([label.title])
+        expect(first_issue['labels']).to eq([label.title])
       end
 
       it 'returns an array of labeled issues when all labels matches' do
@@ -135,13 +140,13 @@
       end
 
       it 'returns an empty array if no issue matches labels' do
-        get api('/issues?labels=foo,bar', user)
+        get api('/issues', user), labels: 'foo,bar'
 
         expect_paginated_array_response(size: 0)
       end
 
       it 'returns an array of labeled issues matching given state' do
-        get api("/issues?labels=#{label.title}&state=opened", user)
+        get api("/issues", user), labels: label.title, state: :opened
 
         expect_paginated_array_response(size: 1)
         expect(json_response.first['labels']).to eq([label.title])
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index 904e049d767173e723ed5f887c12f1c078152a39..c4bff1647b5a26a08d72182ab34c5afa50e72fbc 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -26,6 +26,7 @@
     context "when unauthenticated" do
       it "returns authentication error" do
         get api("/projects/#{project.id}/merge_requests")
+
         expect(response).to have_http_status(401)
       end
     end