You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

本地Rails测试全通过,Github Action中关联删除测试失败求助

Rails测试本地通过但Github Action中外键约束冲突问题

问题情况

配置了Github Action,推送至main分支时自动运行Rails测试。本地执行rails test命令,74个测试全部通过;但在Github Action的工作流中,有2个涉及关联对象(带外键)删除的测试失败,报错为PostgreSQL外键约束冲突,提示Review、HalalItem对象删除时仍被Restaurant表引用。

本地测试输出

Running 74 tests in parallel using 2 processes
DEBUGGER: Attaching after process 42736 fork to child process 42753
DEBUGGER[bin/rails#42754]: Attaching after process 42736 fork to child process 42754
Started with run options --seed 10960

  74/74: [=================================] 100% Time: 00:00:04, Time: 00:00:04

Finished in 5.09323s
74 tests, 135 assertions, 0 failures, 0 errors, 0 skips

Github Action测试输出

Running 74 tests in parallel using 2 processes
DEBUGGER: Attaching after process 3336 fork to child process 3357
DEBUGGER[bin/rails#3359]: Attaching after process 3336 fork to child process 3359
Started with run options --seed 27863


Progress: |=========ERROR ReviewsControllerTest#test_should_destroy_review (3.88s)
Minitest::UnexpectedError:         DRb::DRbRemoteError: PG::ForeignKeyViolation: ERROR:  update or delete on table "reviews" violates foreign key constraint "fk_rails_7314ea0830" on table "restaurants"
        DETAIL:  Key (id)=(1) is still referenced from table "restaurants".
         (ActiveRecord::InvalidForeignKey)
            app/controllers/reviews_controller.rb:52:in `destroy'
            test/controllers/reviews_controller_test.rb:43:in `block (2 levels) in <class:ReviewsControllerTest>'
            test/controllers/reviews_controller_test.rb:42:in `block in <class:ReviewsControllerTest>'

=========ERROR HalalItemsControllerTest#test_should_destroy_halal_item (4.09s)
Minitest::UnexpectedError:         DRb::DRbRemoteError: PG::ForeignKeyViolation: ERROR:  update or delete on table "halal_items" violates foreign key constraint "fk_rails_0fa9986d41" on table "restaurants"
        DETAIL:  Key (id)=(1) is still referenced from table "restaurants".
         (ActiveRecord::InvalidForeignKey)
            app/controllers/halal_items_controller.rb:52:in `destroy'
            test/controllers/halal_items_controller_test.rb:43:in `block (2 levels) in <class:HalalItemsControllerTest>'
            test/controllers/halal_items_controller_test.rb:42:in `block in <class:HalalItemsControllerTest>'

==================================================|

Finished in 5.04433s
74 tests, 131 assertions, 0 failures, 2 errors, 0 skips

Github Action工作流配置(main.yml)

name: Ruby

on:
  push:
    branches:
      - main
jobs:
  test:
    runs-on: ubuntu-latest
    services:
      postgres:
        image: postgres:12
        ports: ['5432:5432']
        env:
          POSTGRES_DB: **redacted**
          POSTGRES_PASSWORD: **redacted**
          POSTGRES_USER: **redacted**
    steps:
      - name: Checkout repo
        uses: actions/checkout@main
      - name: Setup Ruby
        uses: ruby/setup-ruby@v1
      - name: Install dependencies
        run: |
          sudo apt-get -yqq install libpq-dev
          gem install bundler
      - name: Install gems
        run: |
          bundle install --jobs 4 --retry 3
      - name: Setup database
        env:
          DATABASE_URL: **redacted**
          RAILS_ENV: test
          DISABLE_SPRING: 1
        run: bundle exec rails db:prepare
      - name: Run tests
        env:
          DATABASE_URL: **redacted**
          RAILS_ENV: test
          DISABLE_SPRING: 1
        run: bundle exec rails test

相关模型代码

app/models/review.rb

class Review < ApplicationRecord
    belongs_to :restaurant
    belongs_to :user
    belongs_to :halal_item
end

app/models/halal_items.rb

class HalalItem < ApplicationRecord
    belongs_to :restaurant
    has_many :reviews, dependent: :nullify
end

app/models/restaurants.rb

class Restaurant < ApplicationRecord
    has_many :reviews, dependent: :nullify
    has_many :halal_items, dependent: :nullify
end

失败的测试及控制器代码

HalalItem删除测试与控制器

test/controllers/halal_items_controller_test.rb

test "should destroy halal_item" do
    assert_difference("HalalItem.count", -1) do
      delete halal_item_url(@halal_item)
    end

    assert_redirected_to halal_items_url
end

app/controllers/halal_items_controller.rb

def destroy
    @halal_item.destroy # 测试失败的代码行

    respond_to do |format|
      format.html { redirect_to halal_items_url, notice: "Halal item was successfully destroyed." }
      format.json { head :no_content }
    end
end

Review删除测试与控制器

test/controllers/reviews_controller_test.rb

test "should destroy review" do
    assert_difference("Review.count", -1) do
      delete review_url(@review)
    end

    assert_redirected_to reviews_url
end

app/controllers/reviews_controller.rb

def destroy
    @review.destroy # 测试失败的代码行

    respond_to do |format|
      format.html { redirect_to reviews_url, notice: "Review was successfully destroyed." }
      format.json { head :no_content }
    end
end

问题分析与解决步骤

核心问题排查

  1. 模型文件名不符合Rails约定:
    Rails要求模型文件名为单数小写,对应类名单数大写。当前halal_items.rb(复数)、restaurants.rb(复数)不符合约定,会导致Rails自动加载模型失败,关联逻辑无法生效。本地可能因Spring缓存或开发环境自动修复机制正常运行,但Github Action是全新环境,模型加载异常直接触发数据库外键约束错误。

  2. 数据库外键约束方向可能错误:
    报错提示Restaurant表引用了Review/HalalItem的ID,说明数据库中可能存在反向外键(比如restaurants表有review_id或halal_item_id字段并设为外键),这和模型关联逻辑完全相反。正常逻辑应该是Review/HalalItem表通过restaurant_id外键关联Restaurant表。

  3. 测试环境外键约束配置差异:
    本地若使用SQLite,默认可能未开启外键约束,导致测试不触发错误;而Github Action用PostgreSQL,外键约束严格生效。

解决步骤

  1. 修正模型文件名:

    • 将app/models/halal_items.rb重命名为app/models/halal_item.rb
    • 将app/models/restaurants.rb重命名为app/models/restaurant.rb
  2. 检查并修复数据库迁移:

    • 查看所有迁移文件,确保外键约束方向正确:
      • 创建reviews表的迁移需包含t.references :restaurant, foreign_key: true(或add_foreign_key :reviews, :restaurants)
      • 创建halal_items表的迁移需包含t.references :restaurant, foreign_key: true(或add_foreign_key :halal_items, :restaurants)
      • 删除restaurants表中指向reviews/halal_items的反向外键(如果存在)
    • 本地重新执行迁移:rails db:drop db:create db:migrate,验证测试是否能复现问题(排除本地缓存干扰)
  3. 调整Github Action数据库初始化步骤:
    将db:prepare改为db:migrate,确保迁移完全执行:

    - name: Setup database
      env:
        DATABASE_URL: **redacted**
        RAILS_ENV: test
        DISABLE_SPRING: 1
      run: bundle exec rails db:migrate
    
  4. 验证测试环境外键约束:
    检查config/database.yml的test配置,确保PostgreSQL未关闭外键约束(默认开启,无需额外配置)。

内容的提问来源于stack exchange,提问作者Fritz Duverglas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 05:25:40