diff --git a/app/views/server/blocks/show.html.erb b/app/views/server/blocks/show.html.erb
index b56285d126ae23d1b69fca2f8add0894e7acb6bb..44e5518481def89fcc85bfa308c2466794dd3afa 100644
--- a/app/views/server/blocks/show.html.erb
+++ b/app/views/server/blocks/show.html.erb
@@ -38,7 +38,7 @@
 
 <% content_for :action_bar_right do %>
   <%= link_to t('resave'),
-              server_resave_block_path(@template),
+              resave_server_block_path(@template),
               method: :post,
               class: button_classes %>
 <% end %>
diff --git a/config/routes/server.rb b/config/routes/server.rb
index a73ae7b34377a243a0a51475e09766e810e5523d..576fba530c50028275c2641e6dd7ffdf10610731 100644
--- a/config/routes/server.rb
+++ b/config/routes/server.rb
@@ -2,12 +2,10 @@ namespace :server do
   resources :universities
   resources :languages
   resources :websites, only: :index do
-    member do
-      post :refresh
-    end
+    post :refresh, on: :member
+  end
+  resources :blocks, only: [:index, :show] do
+    post :resave, on: :member
   end
-  get 'blocks' => 'blocks#index', as: :blocks
-  get 'blocks/:id' => 'blocks#show', as: :block
-  post 'blocks/:id' => 'blocks#resave', as: :resave_block
   root to: 'dashboard#index'
 end
diff --git a/test/controllers/server/blocks_controller_test.rb b/test/controllers/server/blocks_controller_test.rb
new file mode 100644
index 0000000000000000000000000000000000000000..e3d3d4f7fddeeaf8c8818fc42dd9826dfecd0872
--- /dev/null
+++ b/test/controllers/server/blocks_controller_test.rb
@@ -0,0 +1,20 @@
+require "test_helper"
+
+class Server::BlocksControllerTest < ActionDispatch::IntegrationTest
+  include ServerSetup
+
+  def test_index
+    get server_blocks_path
+    assert_response(:success)
+  end
+
+  def test_show
+    get(server_block_path("chapter"))
+    assert_response(:success)
+  end
+
+  def test_resave
+    post(resave_server_block_path("chapter"))
+    assert_redirected_to(server_block_path("chapter"))
+  end
+end
diff --git a/test/controllers/server/dashboard_controller_test.rb b/test/controllers/server/dashboard_controller_test.rb
new file mode 100644
index 0000000000000000000000000000000000000000..6d8882ab319a6b4aee7f23d492ed9c72d5cc2854
--- /dev/null
+++ b/test/controllers/server/dashboard_controller_test.rb
@@ -0,0 +1,10 @@
+require "test_helper"
+
+class Server::DashboardControllerTest < ActionDispatch::IntegrationTest
+  include ServerSetup
+
+  def test_index
+    get server_root_path
+    assert_response(:success)
+  end
+end
diff --git a/test/controllers/server/languages_controller_test.rb b/test/controllers/server/languages_controller_test.rb
new file mode 100644
index 0000000000000000000000000000000000000000..1e1fdadd49f860ff22ec164cd82513eadb9d5829
--- /dev/null
+++ b/test/controllers/server/languages_controller_test.rb
@@ -0,0 +1,71 @@
+require "test_helper"
+
+class Server::LanguagesControllerTest < ActionDispatch::IntegrationTest
+  include ServerSetup
+
+  def test_index
+    get server_languages_path
+    assert_response(:success)
+  end
+
+  def test_show
+    get server_language_path(languages(:fr))
+    assert_response(:success)
+  end
+
+  def test_new
+    get new_server_language_path
+    assert_response(:success)
+  end
+
+  def test_edit
+    get edit_server_language_path(languages(:fr))
+    assert_response(:success)
+  end
+
+  def test_create
+    assert_difference("Language.count") do
+      post server_languages_path, params: {
+        language: {
+          name: "Español",
+          iso_code: "es"
+        }
+      }
+      language = Language.find_by(iso_code: "es")
+      assert_redirected_to(server_language_path(language))
+    end
+  end
+
+  def test_create_invalid
+    assert_no_difference("Language.count") do
+      post server_languages_path, params: {
+        language: {
+          name: "Français",
+          iso_code: "fr"
+        }
+      }
+      assert_response(:unprocessable_entity)
+    end
+  end
+
+  def test_update
+    language = languages(:fr)
+    assert(language.summernote_locale.blank?)
+    patch server_language_path(language), params: { language: { summernote_locale: "fr-FR" } }
+    assert_redirected_to(server_language_path(language))
+    assert_equal "fr-FR", language.reload.summernote_locale
+  end
+
+  def test_update_invalid
+    language = languages(:fr)
+    patch server_language_path(language), params: { language: { iso_code: "" } }
+    assert_response(:unprocessable_entity)
+  end
+
+  def test_destroy
+    assert_difference("Language.count", -1) do
+      delete server_language_path(languages(:it))
+      assert_redirected_to(server_languages_path)
+    end
+  end
+end
diff --git a/test/controllers/server/universities_controller_test.rb b/test/controllers/server/universities_controller_test.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b68f98caf705d19e40061400faa38353b04c893e
--- /dev/null
+++ b/test/controllers/server/universities_controller_test.rb
@@ -0,0 +1,73 @@
+require "test_helper"
+
+class Server::UniversitiesControllerTest < ActionDispatch::IntegrationTest
+  include ServerSetup
+
+  def test_index
+    get server_universities_path
+    assert_response(:success)
+  end
+
+  def test_show
+    get server_university_path(universities(:default_university))
+    assert_response(:success)
+  end
+
+  def test_new
+    get new_server_university_path
+    assert_response(:success)
+  end
+
+  def test_edit
+    get edit_server_university_path(universities(:default_university))
+    assert_response(:success)
+  end
+
+  def test_create
+    assert_difference("University.count") do
+      post server_universities_path, params: {
+        university: {
+          name: "Nouvelle université",
+          identifier: "my-second-university",
+          sms_sender_name: "unitest2"
+        }
+      }
+      university = University.find_by(identifier: "my-second-university")
+      assert_redirected_to(server_university_path(university))
+    end
+  end
+
+  def test_create_invalid
+    assert_no_difference("University.count") do
+      post server_universities_path, params: {
+        university: {
+          name: "Nouvelle université",
+          sms_sender_name: "unitest2"
+        }
+      }
+      assert_response(:unprocessable_entity)
+    end
+  end
+
+  def test_update
+    university = universities(:default_university)
+    assert_equal "Université de test", university.name
+    patch server_university_path(university), params: { university: { name: "Mon université" } }
+    assert_redirected_to(server_university_path(university))
+    assert_equal "Mon université", university.reload.name
+  end
+
+  def test_update_invalid
+    university = universities(:default_university)
+    patch server_university_path(university), params: { university: { identifier: "" } }
+    assert_response(:unprocessable_entity)
+  end
+
+  def test_destroy
+    assert_difference("University.count", -1) do
+      # TODO: Replace by default university with correct dependent: :destroy associations
+      delete server_university_path(universities(:stale_university))
+      assert_redirected_to(server_universities_path)
+    end
+  end
+end
diff --git a/test/controllers/server/websites_controller_test.rb b/test/controllers/server/websites_controller_test.rb
new file mode 100644
index 0000000000000000000000000000000000000000..aad867f2bf210edc879663d501dba29b97bbc4db
--- /dev/null
+++ b/test/controllers/server/websites_controller_test.rb
@@ -0,0 +1,15 @@
+require "test_helper"
+
+class Server::WebsitesControllerTest < ActionDispatch::IntegrationTest
+  include ServerSetup
+
+  def test_index
+    get server_websites_path
+    assert_response(:success)
+  end
+
+  def test_refresh
+    post(refresh_server_website_path(communication_websites(:website_with_github)))
+    assert_redirected_to(server_websites_path)
+  end
+end
diff --git a/test/fixtures/languages.yml b/test/fixtures/languages.yml
index 7927df719648b1e74869ee705ee9228d8451a56c..d185db7fb28ab5866aea2f8048a9db2730f4036c 100644
--- a/test/fixtures/languages.yml
+++ b/test/fixtures/languages.yml
@@ -16,3 +16,7 @@ en:
 fr:
   name: Français
   iso_code: fr
+
+it:
+  name: Italiano
+  iso_code: it
diff --git a/test/fixtures/universities.yml b/test/fixtures/universities.yml
index e5a7e044a4fd7d76f0288ae1b6e004fe3fec68fb..a472c793d596ee8d5ebee66ac613a32d6c9ff02d 100644
--- a/test/fixtures/universities.yml
+++ b/test/fixtures/universities.yml
@@ -29,3 +29,9 @@
 default_university:
   name: Université de test
   identifier: my-university
+  sms_sender_name: "unitest"
+
+stale_university:
+  name: Université obsolète
+  identifier: stale-university
+  sms_sender_name: "unistale"
\ No newline at end of file
diff --git a/test/support/server_setup.rb b/test/support/server_setup.rb
new file mode 100644
index 0000000000000000000000000000000000000000..288d3d376695ad39911a254668b26454c0f95a54
--- /dev/null
+++ b/test/support/server_setup.rb
@@ -0,0 +1,5 @@
+module ServerSetup
+  def setup
+    sign_in_with_2fa(server_admin)
+  end
+end
\ No newline at end of file
diff --git a/test/test_helper.rb b/test/test_helper.rb
index a4fd59f5c106518a47c6af9a725e913a017dda1a..a36ededd907f030299dd8e045655d38217823855 100644
--- a/test/test_helper.rb
+++ b/test/test_helper.rb
@@ -15,6 +15,7 @@ class ActiveSupport::TestCase
 
   setup do
     ENV.update(ENV.to_h.merge('APPLICATION_ENV' => 'test'))
+    host! "my-university#{University.test_domain}"
   end
 
   def alumnus