diff --git a/spec/controllers/api/v1/accounts/statuses_controller_spec.rb b/spec/requests/api/v1/accounts/statuses_spec.rb
similarity index 71%
rename from spec/controllers/api/v1/accounts/statuses_controller_spec.rb
rename to spec/requests/api/v1/accounts/statuses_spec.rb
index 102e3b8e9..371867b21 100644
--- a/spec/controllers/api/v1/accounts/statuses_controller_spec.rb
+++ b/spec/requests/api/v1/accounts/statuses_spec.rb
@@ -2,20 +2,16 @@
 
 require 'rails_helper'
 
-describe Api::V1::Accounts::StatusesController do
-  render_views
+describe 'API V1 Accounts Statuses' do
+  let(:user) { Fabricate(:user) }
+  let(:scopes) { 'read:statuses' }
+  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
+  let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
 
-  let(:user)  { Fabricate(:user) }
-  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:statuses') }
-
-  before do
-    allow(controller).to receive(:doorkeeper_token) { token }
-  end
-
-  describe 'GET #index' do
+  describe 'GET /api/v1/accounts/:account_id/statuses' do
     it 'returns expected headers', :aggregate_failures do
       Fabricate(:status, account: user.account)
-      get :index, params: { account_id: user.account.id, limit: 1 }
+      get "/api/v1/accounts/#{user.account.id}/statuses", params: { limit: 1 }, headers: headers
 
       expect(response).to have_http_status(200)
       expect(links_from_header.size)
@@ -24,7 +20,7 @@ describe Api::V1::Accounts::StatusesController do
 
     context 'with only media' do
       it 'returns http success' do
-        get :index, params: { account_id: user.account.id, only_media: true }
+        get "/api/v1/accounts/#{user.account.id}/statuses", params: { only_media: true }, headers: headers
 
         expect(response).to have_http_status(200)
       end
@@ -36,7 +32,7 @@ describe Api::V1::Accounts::StatusesController do
 
       before do
         Fabricate(:status, account: user.account, thread: Fabricate(:status)) # Reply to another user
-        get :index, params: { account_id: user.account.id, exclude_replies: true }
+        get "/api/v1/accounts/#{user.account.id}/statuses", params: { exclude_replies: true }, headers: headers
       end
 
       it 'returns posts along with self replies', :aggregate_failures do
@@ -57,7 +53,7 @@ describe Api::V1::Accounts::StatusesController do
       end
 
       it 'returns http success and includes a header link' do
-        get :index, params: { account_id: user.account.id, pinned: true }
+        get "/api/v1/accounts/#{user.account.id}/statuses", params: { pinned: true }, headers: headers
 
         expect(response).to have_http_status(200)
         expect(links_from_header.size)
@@ -79,7 +75,7 @@ describe Api::V1::Accounts::StatusesController do
       end
 
       it 'returns http success and header pagination links to prev and next' do
-        get :index, params: { account_id: user.account.id, pinned: true }
+        get "/api/v1/accounts/#{user.account.id}/statuses", params: { pinned: true }, headers: headers
 
         expect(response).to have_http_status(200)
         expect(links_from_header.size)
@@ -109,15 +105,19 @@ describe Api::V1::Accounts::StatusesController do
       end
 
       it 'returns http success' do
-        get :index, params: { account_id: account.id, pinned: true }
+        get "/api/v1/accounts/#{account.id}/statuses", params: { pinned: true }, headers: headers
+
         expect(response).to have_http_status(200)
       end
 
       context 'when user does not follow account' do
         it 'lists the public status only' do
-          get :index, params: { account_id: account.id, pinned: true }
-          json = body_as_json
-          expect(json.map { |item| item[:id].to_i }).to eq [status.id]
+          get "/api/v1/accounts/#{account.id}/statuses", params: { pinned: true }, headers: headers
+
+          expect(body_as_json)
+            .to contain_exactly(
+              a_hash_including(id: status.id.to_s)
+            )
         end
       end
 
@@ -127,9 +127,13 @@ describe Api::V1::Accounts::StatusesController do
         end
 
         it 'lists both the public and the private statuses' do
-          get :index, params: { account_id: account.id, pinned: true }
-          json = body_as_json
-          expect(json.map { |item| item[:id].to_i }).to contain_exactly(status.id, private_status.id)
+          get "/api/v1/accounts/#{account.id}/statuses", params: { pinned: true }, headers: headers
+
+          expect(body_as_json)
+            .to contain_exactly(
+              a_hash_including(id: status.id.to_s),
+              a_hash_including(id: private_status.id.to_s)
+            )
         end
       end
     end
diff --git a/spec/controllers/api/v1/admin/trends/statuses_controller_spec.rb b/spec/requests/api/v1/admin/trends/statuses_spec.rb
similarity index 63%
rename from spec/controllers/api/v1/admin/trends/statuses_controller_spec.rb
rename to spec/requests/api/v1/admin/trends/statuses_spec.rb
index 4d80055ac..04aa0465f 100644
--- a/spec/controllers/api/v1/admin/trends/statuses_controller_spec.rb
+++ b/spec/requests/api/v1/admin/trends/statuses_spec.rb
@@ -2,31 +2,26 @@
 
 require 'rails_helper'
 
-describe Api::V1::Admin::Trends::StatusesController do
-  render_views
-
+describe 'API V1 Admin Trends Statuses' do
   let(:role)   { UserRole.find_by(name: 'Admin') }
   let(:user)   { Fabricate(:user, role: role) }
   let(:scopes) { 'admin:read admin:write' }
   let(:token)   { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
   let(:account) { Fabricate(:account) }
   let(:status)  { Fabricate(:status) }
+  let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
 
-  before do
-    allow(controller).to receive(:doorkeeper_token) { token }
-  end
-
-  describe 'GET #index' do
+  describe 'GET /api/v1/admin/trends/statuses' do
     it 'returns http success' do
-      get :index, params: { account_id: account.id, limit: 2 }
+      get '/api/v1/admin/trends/statuses', params: { account_id: account.id, limit: 2 }, headers: headers
 
       expect(response).to have_http_status(200)
     end
   end
 
-  describe 'POST #approve' do
+  describe 'POST /api/v1/admin/trends/statuses/:id/approve' do
     before do
-      post :approve, params: { id: status.id }
+      post "/api/v1/admin/trends/statuses/#{status.id}/approve", headers: headers
     end
 
     it_behaves_like 'forbidden for wrong scope', 'write:statuses'
@@ -37,9 +32,9 @@ describe Api::V1::Admin::Trends::StatusesController do
     end
   end
 
-  describe 'POST #reject' do
+  describe 'POST /api/v1/admin/trends/statuses/:id/unapprove' do
     before do
-      post :reject, params: { id: status.id }
+      post "/api/v1/admin/trends/statuses/#{status.id}/reject", headers: headers
     end
 
     it_behaves_like 'forbidden for wrong scope', 'write:statuses'
diff --git a/spec/controllers/api/v1/admin/trends/tags_controller_spec.rb b/spec/requests/api/v1/admin/trends/tags_spec.rb
similarity index 64%
rename from spec/controllers/api/v1/admin/trends/tags_controller_spec.rb
rename to spec/requests/api/v1/admin/trends/tags_spec.rb
index 0b8eb8c3b..b1437dad8 100644
--- a/spec/controllers/api/v1/admin/trends/tags_controller_spec.rb
+++ b/spec/requests/api/v1/admin/trends/tags_spec.rb
@@ -2,31 +2,26 @@
 
 require 'rails_helper'
 
-describe Api::V1::Admin::Trends::TagsController do
-  render_views
-
+describe 'API V1 Admin Trends Tags' do
   let(:role)   { UserRole.find_by(name: 'Admin') }
   let(:user)   { Fabricate(:user, role: role) }
   let(:scopes) { 'admin:read admin:write' }
   let(:token)   { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
   let(:account) { Fabricate(:account) }
   let(:tag)     { Fabricate(:tag) }
+  let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
 
-  before do
-    allow(controller).to receive(:doorkeeper_token) { token }
-  end
-
-  describe 'GET #index' do
+  describe 'GET /api/v1/admin/trends/tags' do
     it 'returns http success' do
-      get :index, params: { account_id: account.id, limit: 2 }
+      get '/api/v1/admin/trends/tags', params: { account_id: account.id, limit: 2 }, headers: headers
 
       expect(response).to have_http_status(200)
     end
   end
 
-  describe 'POST #approve' do
+  describe 'POST /api/v1/admin/trends/tags/:id/approve' do
     before do
-      post :approve, params: { id: tag.id }
+      post "/api/v1/admin/trends/tags/#{tag.id}/approve", headers: headers
     end
 
     it_behaves_like 'forbidden for wrong scope', 'write:statuses'
@@ -37,9 +32,9 @@ describe Api::V1::Admin::Trends::TagsController do
     end
   end
 
-  describe 'POST #reject' do
+  describe 'POST /api/v1/admin/trends/tags/:id/reject' do
     before do
-      post :reject, params: { id: tag.id }
+      post "/api/v1/admin/trends/tags/#{tag.id}/reject", headers: headers
     end
 
     it_behaves_like 'forbidden for wrong scope', 'write:statuses'
diff --git a/spec/controllers/api/v1/announcements/reactions_controller_spec.rb b/spec/requests/api/v1/announcements/reactions_spec.rb
similarity index 64%
rename from spec/controllers/api/v1/announcements/reactions_controller_spec.rb
rename to spec/requests/api/v1/announcements/reactions_spec.rb
index c1debc33f..ffacb2b0a 100644
--- a/spec/controllers/api/v1/announcements/reactions_controller_spec.rb
+++ b/spec/requests/api/v1/announcements/reactions_spec.rb
@@ -2,27 +2,26 @@
 
 require 'rails_helper'
 
-RSpec.describe Api::V1::Announcements::ReactionsController do
-  render_views
-
+RSpec.describe 'API V1 Announcements Reactions' do
   let(:user)   { Fabricate(:user) }
   let(:scopes) { 'write:favourites' }
   let(:token)  { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
+  let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
 
   let!(:announcement) { Fabricate(:announcement) }
 
-  describe 'PUT #update' do
+  describe 'PUT /api/v1/announcements/:announcement_id/reactions/:id' do
     context 'without token' do
       it 'returns http unauthorized' do
-        put :update, params: { announcement_id: announcement.id, id: '😂' }
+        put "/api/v1/announcements/#{announcement.id}/reactions/#{escaped_emoji}"
+
         expect(response).to have_http_status 401
       end
     end
 
     context 'with token' do
       before do
-        allow(controller).to receive(:doorkeeper_token) { token }
-        put :update, params: { announcement_id: announcement.id, id: '😂' }
+        put "/api/v1/announcements/#{announcement.id}/reactions/#{escaped_emoji}", headers: headers
       end
 
       it 'creates reaction', :aggregate_failures do
@@ -32,22 +31,21 @@ RSpec.describe Api::V1::Announcements::ReactionsController do
     end
   end
 
-  describe 'DELETE #destroy' do
+  describe 'DELETE /api/v1/announcements/:announcement_id/reactions/:id' do
     before do
       announcement.announcement_reactions.create!(account: user.account, name: '😂')
     end
 
     context 'without token' do
       it 'returns http unauthorized' do
-        delete :destroy, params: { announcement_id: announcement.id, id: '😂' }
+        delete "/api/v1/announcements/#{announcement.id}/reactions/#{escaped_emoji}"
         expect(response).to have_http_status 401
       end
     end
 
     context 'with token' do
       before do
-        allow(controller).to receive(:doorkeeper_token) { token }
-        delete :destroy, params: { announcement_id: announcement.id, id: '😂' }
+        delete "/api/v1/announcements/#{announcement.id}/reactions/#{escaped_emoji}", headers: headers
       end
 
       it 'creates reaction', :aggregate_failures do
@@ -56,4 +54,8 @@ RSpec.describe Api::V1::Announcements::ReactionsController do
       end
     end
   end
+
+  def escaped_emoji
+    CGI.escape('😂')
+  end
 end
diff --git a/spec/controllers/api/v1/announcements_controller_spec.rb b/spec/requests/api/v1/announcements_spec.rb
similarity index 59%
rename from spec/controllers/api/v1/announcements_controller_spec.rb
rename to spec/requests/api/v1/announcements_spec.rb
index 95ce8fd9f..1624b7601 100644
--- a/spec/controllers/api/v1/announcements_controller_spec.rb
+++ b/spec/requests/api/v1/announcements_spec.rb
@@ -2,27 +2,26 @@
 
 require 'rails_helper'
 
-RSpec.describe Api::V1::AnnouncementsController do
-  render_views
-
-  let(:user)   { Fabricate(:user) }
-  let(:scopes) { 'read' }
-  let(:token)  { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
+RSpec.describe 'API V1 Announcements' do
+  let(:user)    { Fabricate(:user) }
+  let(:scopes)  { 'read' }
+  let(:token)   { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
+  let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
 
   let!(:announcement) { Fabricate(:announcement) }
 
-  describe 'GET #index' do
+  describe 'GET /api/v1/announcements' do
     context 'without token' do
       it 'returns http unprocessable entity' do
-        get :index
+        get '/api/v1/announcements'
+
         expect(response).to have_http_status 422
       end
     end
 
     context 'with token' do
       before do
-        allow(controller).to receive(:doorkeeper_token) { token }
-        get :index
+        get '/api/v1/announcements', headers: headers
       end
 
       it 'returns http success' do
@@ -31,10 +30,11 @@ RSpec.describe Api::V1::AnnouncementsController do
     end
   end
 
-  describe 'POST #dismiss' do
+  describe 'POST /api/v1/announcements/:id/dismiss' do
     context 'without token' do
       it 'returns http unauthorized' do
-        post :dismiss, params: { id: announcement.id }
+        post "/api/v1/announcements/#{announcement.id}/dismiss"
+
         expect(response).to have_http_status 401
       end
     end
@@ -43,8 +43,7 @@ RSpec.describe Api::V1::AnnouncementsController do
       let(:scopes) { 'write:accounts' }
 
       before do
-        allow(controller).to receive(:doorkeeper_token) { token }
-        post :dismiss, params: { id: announcement.id }
+        post "/api/v1/announcements/#{announcement.id}/dismiss", headers: headers
       end
 
       it 'dismisses announcement', :aggregate_failures do
diff --git a/spec/controllers/api/v1/conversations_controller_spec.rb b/spec/requests/api/v1/conversations_spec.rb
similarity index 50%
rename from spec/controllers/api/v1/conversations_controller_spec.rb
rename to spec/requests/api/v1/conversations_spec.rb
index 2734e4a07..e2327d9a9 100644
--- a/spec/controllers/api/v1/conversations_controller_spec.rb
+++ b/spec/requests/api/v1/conversations_spec.rb
@@ -2,53 +2,48 @@
 
 require 'rails_helper'
 
-RSpec.describe Api::V1::ConversationsController do
-  render_views
-
+RSpec.describe 'API V1 Conversations' do
   let!(:user) { Fabricate(:user, account_attributes: { username: 'alice' }) }
-  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
+  let(:scopes) { 'read:statuses' }
+  let(:token)   { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
+  let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
+
   let(:other) { Fabricate(:user) }
 
-  before do
-    allow(controller).to receive(:doorkeeper_token) { token }
-  end
-
-  describe 'GET #index', :sidekiq_inline do
-    let(:scopes) { 'read:statuses' }
-
+  describe 'GET /api/v1/conversations', :sidekiq_inline do
     before do
       PostStatusService.new.call(other.account, text: 'Hey @alice', visibility: 'direct')
       PostStatusService.new.call(user.account, text: 'Hey, nobody here', visibility: 'direct')
     end
 
     it 'returns pagination headers', :aggregate_failures do
-      get :index, params: { limit: 1 }
+      get '/api/v1/conversations', params: { limit: 1 }, headers: headers
 
       expect(response).to have_http_status(200)
       expect(response.headers['Link'].links.size).to eq(2)
     end
 
     it 'returns conversations', :aggregate_failures do
-      get :index
-      json = body_as_json
-      expect(json.size).to eq 2
-      expect(json[0][:accounts].size).to eq 1
+      get '/api/v1/conversations', headers: headers
+
+      expect(body_as_json.size).to eq 2
+      expect(body_as_json[0][:accounts].size).to eq 1
     end
 
     context 'with since_id' do
       context 'when requesting old posts' do
         it 'returns conversations' do
-          get :index, params: { since_id: Mastodon::Snowflake.id_at(1.hour.ago, with_random: false) }
-          json = body_as_json
-          expect(json.size).to eq 2
+          get '/api/v1/conversations', params: { since_id: Mastodon::Snowflake.id_at(1.hour.ago, with_random: false) }, headers: headers
+
+          expect(body_as_json.size).to eq 2
         end
       end
 
       context 'when requesting posts in the future' do
         it 'returns no conversation' do
-          get :index, params: { since_id: Mastodon::Snowflake.id_at(1.hour.from_now, with_random: false) }
-          json = body_as_json
-          expect(json.size).to eq 0
+          get '/api/v1/conversations', params: { since_id: Mastodon::Snowflake.id_at(1.hour.from_now, with_random: false) }, headers: headers
+
+          expect(body_as_json.size).to eq 0
         end
       end
     end
diff --git a/spec/controllers/api/v1/filters_controller_spec.rb b/spec/requests/api/v1/filters_spec.rb
similarity index 75%
rename from spec/controllers/api/v1/filters_controller_spec.rb
rename to spec/requests/api/v1/filters_spec.rb
index b0f64ccf4..deb6e7421 100644
--- a/spec/controllers/api/v1/filters_controller_spec.rb
+++ b/spec/requests/api/v1/filters_spec.rb
@@ -2,23 +2,18 @@
 
 require 'rails_helper'
 
-RSpec.describe Api::V1::FiltersController do
-  render_views
+RSpec.describe 'API V1 Filters' do
+  let(:user)    { Fabricate(:user) }
+  let(:token)   { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
+  let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
 
-  let(:user)  { Fabricate(:user) }
-  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
-
-  before do
-    allow(controller).to receive(:doorkeeper_token) { token }
-  end
-
-  describe 'GET #index' do
+  describe 'GET /api/v1/filters' do
     let(:scopes) { 'read:filters' }
     let!(:filter) { Fabricate(:custom_filter, account: user.account) }
     let!(:custom_filter_keyword) { Fabricate(:custom_filter_keyword, custom_filter: filter) }
 
     it 'returns http success' do
-      get :index
+      get '/api/v1/filters', headers: headers
       expect(response).to have_http_status(200)
       expect(body_as_json)
         .to contain_exactly(
@@ -27,13 +22,13 @@ RSpec.describe Api::V1::FiltersController do
     end
   end
 
-  describe 'POST #create' do
+  describe 'POST /api/v1/filters' do
     let(:scopes) { 'write:filters' }
     let(:irreversible) { true }
     let(:whole_word)   { false }
 
     before do
-      post :create, params: { phrase: 'magic', context: %w(home), irreversible: irreversible, whole_word: whole_word }
+      post '/api/v1/filters', params: { phrase: 'magic', context: %w(home), irreversible: irreversible, whole_word: whole_word }, headers: headers
     end
 
     it 'creates a filter', :aggregate_failures do
@@ -64,24 +59,25 @@ RSpec.describe Api::V1::FiltersController do
     end
   end
 
-  describe 'GET #show' do
+  describe 'GET /api/v1/filters/:id' do
     let(:scopes)  { 'read:filters' }
     let(:filter)  { Fabricate(:custom_filter, account: user.account) }
     let(:keyword) { Fabricate(:custom_filter_keyword, custom_filter: filter) }
 
     it 'returns http success' do
-      get :show, params: { id: keyword.id }
+      get "/api/v1/filters/#{keyword.id}", headers: headers
+
       expect(response).to have_http_status(200)
     end
   end
 
-  describe 'PUT #update' do
+  describe 'PUT /api/v1/filters/:id' do
     let(:scopes)  { 'write:filters' }
     let(:filter)  { Fabricate(:custom_filter, account: user.account) }
     let(:keyword) { Fabricate(:custom_filter_keyword, custom_filter: filter) }
 
     before do
-      put :update, params: { id: keyword.id, phrase: 'updated' }
+      put "/api/v1/filters/#{keyword.id}", headers: headers, params: { phrase: 'updated' }
     end
 
     it 'updates the filter', :aggregate_failures do
@@ -90,13 +86,13 @@ RSpec.describe Api::V1::FiltersController do
     end
   end
 
-  describe 'DELETE #destroy' do
+  describe 'DELETE /api/v1/filters/:id' do
     let(:scopes)  { 'write:filters' }
     let(:filter)  { Fabricate(:custom_filter, account: user.account) }
     let(:keyword) { Fabricate(:custom_filter_keyword, custom_filter: filter) }
 
     before do
-      delete :destroy, params: { id: keyword.id }
+      delete "/api/v1/filters/#{keyword.id}", headers: headers
     end
 
     it 'removes the filter', :aggregate_failures do
diff --git a/spec/controllers/api/v1/polls/votes_controller_spec.rb b/spec/requests/api/v1/polls/votes_spec.rb
similarity index 61%
rename from spec/controllers/api/v1/polls/votes_controller_spec.rb
rename to spec/requests/api/v1/polls/votes_spec.rb
index 5de225a48..e2b22708b 100644
--- a/spec/controllers/api/v1/polls/votes_controller_spec.rb
+++ b/spec/requests/api/v1/polls/votes_spec.rb
@@ -2,30 +2,32 @@
 
 require 'rails_helper'
 
-RSpec.describe Api::V1::Polls::VotesController do
-  render_views
-
+RSpec.describe 'API V1 Polls Votes' do
   let(:user)   { Fabricate(:user) }
   let(:scopes) { 'write:statuses' }
   let(:token)  { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
+  let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
 
-  before { allow(controller).to receive(:doorkeeper_token) { token } }
-
-  describe 'POST #create' do
+  describe 'POST /api/v1/polls/:poll_id/votes' do
     let(:poll) { Fabricate(:poll) }
 
     before do
-      post :create, params: { poll_id: poll.id, choices: %w(1) }
+      post "/api/v1/polls/#{poll.id}/votes", params: { choices: %w(1) }, headers: headers
     end
 
     it 'creates a vote', :aggregate_failures do
       expect(response).to have_http_status(200)
-      vote = poll.votes.where(account: user.account).first
 
       expect(vote).to_not be_nil
       expect(vote.choice).to eq 1
 
       expect(poll.reload.cached_tallies).to eq [0, 1]
     end
+
+    private
+
+    def vote
+      poll.votes.where(account: user.account).first
+    end
   end
 end
diff --git a/spec/controllers/api/v1/push/subscriptions_controller_spec.rb b/spec/requests/api/v1/push/subscriptions_spec.rb
similarity index 67%
rename from spec/controllers/api/v1/push/subscriptions_controller_spec.rb
rename to spec/requests/api/v1/push/subscriptions_spec.rb
index 168191468..d699fd1e0 100644
--- a/spec/controllers/api/v1/push/subscriptions_controller_spec.rb
+++ b/spec/requests/api/v1/push/subscriptions_spec.rb
@@ -2,9 +2,7 @@
 
 require 'rails_helper'
 
-describe Api::V1::Push::SubscriptionsController do
-  render_views
-
+describe 'API V1 Push Subscriptions' do
   let(:user) { Fabricate(:user) }
   let(:create_payload) do
     {
@@ -34,15 +32,13 @@ describe Api::V1::Push::SubscriptionsController do
       },
     }.with_indifferent_access
   end
-  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'push') }
+  let(:scopes) { 'push' }
+  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
+  let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
 
-  before do
-    allow(controller).to receive(:doorkeeper_token) { token }
-  end
-
-  describe 'POST #create' do
+  describe 'POST /api/v1/push/subscription' do
     before do
-      post :create, params: create_payload
+      post '/api/v1/push/subscription', params: create_payload, headers: headers
     end
 
     it 'saves push subscriptions' do
@@ -56,19 +52,23 @@ describe Api::V1::Push::SubscriptionsController do
     end
 
     it 'replaces old subscription on repeat calls' do
-      post :create, params: create_payload
+      post '/api/v1/push/subscription', params: create_payload, headers: headers
+
       expect(Web::PushSubscription.where(endpoint: create_payload[:subscription][:endpoint]).count).to eq 1
     end
 
     it 'returns the expected JSON' do
-      expect(body_as_json.with_indifferent_access).to include({ endpoint: create_payload[:subscription][:endpoint], alerts: {}, policy: 'all' })
+      expect(body_as_json.with_indifferent_access)
+        .to include(
+          { endpoint: create_payload[:subscription][:endpoint], alerts: {}, policy: 'all' }
+        )
     end
   end
 
-  describe 'PUT #update' do
+  describe 'PUT /api/v1/push/subscription' do
     before do
-      post :create, params: create_payload
-      put :update, params: alerts_payload
+      post '/api/v1/push/subscription', params: create_payload, headers: headers
+      put '/api/v1/push/subscription', params: alerts_payload, headers: headers
     end
 
     it 'changes alert settings' do
@@ -82,14 +82,17 @@ describe Api::V1::Push::SubscriptionsController do
     end
 
     it 'returns the expected JSON' do
-      expect(body_as_json.with_indifferent_access).to include({ endpoint: create_payload[:subscription][:endpoint], alerts: alerts_payload[:data][:alerts], policy: alerts_payload[:data][:policy] })
+      expect(body_as_json.with_indifferent_access)
+        .to include(
+          { endpoint: create_payload[:subscription][:endpoint], alerts: alerts_payload[:data][:alerts], policy: alerts_payload[:data][:policy] }
+        )
     end
   end
 
-  describe 'DELETE #destroy' do
+  describe 'DELETE /api/v1/push/subscription' do
     before do
-      post :create, params: create_payload
-      delete :destroy
+      post '/api/v1/push/subscription', params: create_payload, headers: headers
+      delete '/api/v1/push/subscription', headers: headers
     end
 
     it 'removes the subscription' do
diff --git a/spec/controllers/api/v1/streaming_controller_spec.rb b/spec/requests/api/v1/streaming_spec.rb
similarity index 51%
rename from spec/controllers/api/v1/streaming_controller_spec.rb
rename to spec/requests/api/v1/streaming_spec.rb
index 099f68a74..6b550dfa6 100644
--- a/spec/controllers/api/v1/streaming_controller_spec.rb
+++ b/spec/requests/api/v1/streaming_spec.rb
@@ -2,7 +2,7 @@
 
 require 'rails_helper'
 
-describe Api::V1::StreamingController do
+describe 'API V1 Streaming' do
   around do |example|
     before = Rails.configuration.x.streaming_api_base_url
     Rails.configuration.x.streaming_api_base_url = "wss://#{Rails.configuration.x.web_domain}"
@@ -10,14 +10,13 @@ describe Api::V1::StreamingController do
     Rails.configuration.x.streaming_api_base_url = before
   end
 
-  before do
-    request.headers.merge! Host: Rails.configuration.x.web_domain
-  end
+  let(:headers) { { 'Host' => Rails.configuration.x.web_domain } }
 
   context 'with streaming api on same host' do
-    describe 'GET #index' do
+    describe 'GET /api/v1/streaming' do
       it 'raises ActiveRecord::RecordNotFound' do
-        get :index
+        get '/api/v1/streaming', headers: headers
+
         expect(response).to have_http_status(404)
       end
     end
@@ -28,20 +27,33 @@ describe Api::V1::StreamingController do
       Rails.configuration.x.streaming_api_base_url = "wss://streaming-#{Rails.configuration.x.web_domain}"
     end
 
-    describe 'GET #index' do
+    describe 'GET /api/v1/streaming' do
       it 'redirects to streaming host' do
-        get :index, params: { access_token: 'deadbeef', stream: 'public' }
-        expect(response).to have_http_status(301)
-        request_uri = URI.parse(request.url)
-        redirect_to_uri = URI.parse(response.location)
-        [:scheme, :path, :query, :fragment].each do |part|
-          expect(redirect_to_uri.send(part)).to eq(request_uri.send(part)), "redirect target #{part}"
-        end
-        expect(redirect_to_uri.host).to eq(streaming_host), 'redirect target host'
+        get '/api/v1/streaming', headers: headers, params: { access_token: 'deadbeef', stream: 'public' }
+
+        expect(response)
+          .to have_http_status(301)
+
+        expect(redirect_to_uri)
+          .to have_attributes(
+            fragment: request_uri.fragment,
+            host: eq(streaming_host),
+            path: request_uri.path,
+            query: request_uri.query,
+            scheme: request_uri.scheme
+          )
       end
 
       private
 
+      def request_uri
+        URI.parse(request.url)
+      end
+
+      def redirect_to_uri
+        URI.parse(response.location)
+      end
+
       def streaming_host
         URI.parse(Rails.configuration.x.streaming_api_base_url).host
       end