diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 809abbb..833d0b6 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -2,10 +2,9 @@ name: CI
 
 on:
   pull_request:
+    branches: [ master ]
   push:
-    branches:
-      - master
-
+    branches: [ master ]
 jobs:
   test:
     name: Test ruby version matrix
@@ -13,7 +12,7 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        ruby-version: ['3.0', '3.1', '3.2', '3.3', 'head', 'truffleruby-head']
+        ruby-version: ['3.0', '3.1', '3.2', '3.3', 'head', 'truffleruby-head'] # keep in sync with gemspec
     steps:
       - uses: actions/checkout@v2
       - uses: ruby/setup-ruby@v1
@@ -29,6 +28,6 @@ jobs:
       - uses: actions/checkout@v2
       - uses: ruby/setup-ruby@v1
         with:
-          ruby-version: '3.3'
+          ruby-version: '3.0' # lowest supported version, keep in sync with .rubocop.yml
           bundler-cache: true
       - run: bundle exec rake rubocop
diff --git a/.rubocop.yml b/.rubocop.yml
index 0693cab..8e1bf05 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -1,5 +1,5 @@
 AllCops:
-  TargetRubyVersion: 3.0
+  TargetRubyVersion: 3.0 # lowest supported version, see .github/workflows/ci.yml
   Include:
     - 'lib/**/*'
     - 'Rakefile'
diff --git a/Gemfile.lock b/Gemfile.lock
index 4dcea2b..3a85f61 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -6,63 +6,65 @@ PATH
 GEM
   remote: https://rubygems.org/
   specs:
-    addressable (2.6.0)
-      public_suffix (>= 2.0.2, < 4.0)
+    addressable (2.8.7)
+      public_suffix (>= 2.0.2, < 7.0)
     ast (2.4.2)
-    bump (0.8.0)
-    byebug (11.0.1)
-    coderay (1.1.2)
-    concurrent-ruby (1.1.5)
-    crack (0.4.3)
-      safe_yaml (~> 1.0.0)
-    hashdiff (0.3.9)
-    i18n (1.6.0)
+    bigdecimal (3.1.8)
+    bump (0.10.0)
+    byebug (11.1.3)
+    coderay (1.1.3)
+    concurrent-ruby (1.3.4)
+    crack (1.0.0)
+      bigdecimal
+      rexml
+    hashdiff (1.1.2)
+    i18n (1.14.6)
       concurrent-ruby (~> 1.0)
-    json (2.7.1)
+    json (2.9.0)
     language_server-protocol (3.17.0.3)
-    maxitest (3.1.0)
-      minitest (>= 5.0.0, < 5.12.0)
-    metaclass (0.0.4)
-    method_source (0.9.2)
-    minitest (5.11.3)
-    mocha (1.8.0)
-      metaclass (~> 0.0.1)
-    parallel (1.24.0)
-    parser (3.3.0.5)
+    maxitest (5.8.0)
+      minitest (>= 5.14.0, < 5.26.0)
+    method_source (1.1.0)
+    minitest (5.25.4)
+    mocha (2.7.0)
+      ruby2_keywords (>= 0.0.5)
+    parallel (1.26.3)
+    parser (3.3.6.0)
       ast (~> 2.4.1)
       racc
-    pry (0.12.2)
-      coderay (~> 1.1.0)
-      method_source (~> 0.9.0)
-    pry-byebug (3.7.0)
+    pry (0.14.2)
+      coderay (~> 1.1)
+      method_source (~> 1.0)
+    pry-byebug (3.10.1)
       byebug (~> 11.0)
-      pry (~> 0.10)
-    public_suffix (3.0.3)
-    racc (1.7.3)
+      pry (>= 0.13, < 0.15)
+    public_suffix (6.0.1)
+    racc (1.8.1)
     rainbow (3.1.1)
-    rake (12.3.2)
-    regexp_parser (2.9.0)
-    rexml (3.2.6)
-    rubocop (1.60.2)
+    rake (13.2.1)
+    regexp_parser (2.9.3)
+    rexml (3.3.9)
+    rubocop (1.69.1)
       json (~> 2.3)
       language_server-protocol (>= 3.17.0)
       parallel (~> 1.10)
       parser (>= 3.3.0.2)
       rainbow (>= 2.2.2, < 4.0)
-      regexp_parser (>= 1.8, < 3.0)
-      rexml (>= 3.2.5, < 4.0)
-      rubocop-ast (>= 1.30.0, < 2.0)
+      regexp_parser (>= 2.9.3, < 3.0)
+      rubocop-ast (>= 1.36.2, < 2.0)
       ruby-progressbar (~> 1.7)
-      unicode-display_width (>= 2.4.0, < 3.0)
-    rubocop-ast (1.30.0)
-      parser (>= 3.2.1.0)
+      unicode-display_width (>= 2.4.0, < 4.0)
+    rubocop-ast (1.36.2)
+      parser (>= 3.3.1.0)
     ruby-progressbar (1.13.0)
-    safe_yaml (1.0.5)
-    unicode-display_width (2.5.0)
-    webmock (3.5.1)
-      addressable (>= 2.3.6)
+    ruby2_keywords (0.0.5)
+    unicode-display_width (3.1.2)
+      unicode-emoji (~> 4.0, >= 4.0.4)
+    unicode-emoji (4.0.4)
+    webmock (3.24.0)
+      addressable (>= 2.8.0)
       crack (>= 0.3.2)
-      hashdiff
+      hashdiff (>= 0.4.0, < 2.0.0)
 
 PLATFORMS
   ruby
diff --git a/test/helper.rb b/test/helper.rb
index b1ea59b..4d19381 100644
--- a/test/helper.rb
+++ b/test/helper.rb
@@ -3,8 +3,9 @@
 ENV.delete('RACK_ENV')
 
 require 'bundler/setup'
+require 'maxitest/global_must'
 require 'maxitest/autorun'
-require 'mocha/setup'
+require 'mocha/minitest'
 require 'webmock/minitest'
 require 'byebug'
 require 'cgi'
diff --git a/test/verify_test.rb b/test/verify_test.rb
index 4fddd72..7ee7306 100644
--- a/test/verify_test.rb
+++ b/test/verify_test.rb
@@ -76,7 +76,8 @@ def initialize
 
       refute @controller.verify_recaptcha
       assert_equal "reCAPTCHA verification failed, please try again.", @controller.flash[:recaptcha_error]
-      assert_equal "Recaptcha failure after api call. Api reply: {\"foo\"=>\"false\", \"bar\"=>\"invalid-site-secret-key\"}.", @controller.recaptcha_failure_reason
+      reason = @controller.recaptcha_failure_reason.gsub('"=>"', '" => "') # ruby 3.4+ adds spaces
+      assert_equal "Recaptcha failure after api call. Api reply: {\"foo\" => \"false\", \"bar\" => \"invalid-site-secret-key\"}.", reason
     end
 
     it "adds an error to the model" do