在 minitest 路由测试之前需要 运行 一些代码(插入数据库)

Need to run some code (insert into DB) before minitest route testing

由于(产品所有者的)某些要求,我不得不偏离黄金路径并为某些匹配约束的 url 覆盖资源路由。

Rails.application.routes.draw do
  CATEGORY_SLUGS = Regexp.new(Category.all.collect(&:slug).join('|'))
  get '/posts/:category', to: 'posts#index', as: :category_posts, constraints: { category: CATEGORY_SLUGS }
  resources :posts
end

和我的测试:

require 'test_helper'
class PotsControllerTest < ActionDispatch::IntegrationTest
  # routes
  test "/posts/:category" do
    Fabricate(:category, slug: 'rails')

    assert_recognizes({controller: 'posts', action: 'index', category: 'rails' }, 'posts/rails')
  end

测试显然失败了,因为在测试开始之前很久就已经绘制了路线,而 Category 在那个时候还不存在。

除了CATEGORY_SLUGS或测试中的任何东西,还有什么好的选择可以稍后绘制路线吗?

在本例中使用 Fabricate 调用进行测试设置后,您可以请求 Rails 重新加载其路由:

Rails.application.reload_routes!

这应该确保在您的 assert_recognizes 调用之前生成动态路由。

这种方法(使用来自 ActiveRecord 模型的数据在路由配置中构建约束)有严重的缺点。不仅几乎不可能正确测试它,而且每当类别更改时您还必须重新启动应用程序,以便重新构建约束。

我建议将逻辑移至控制器操作。您将为此付出很小的性能代价(渲染 Post 时需要额外的数据库查询),但这绝对是值得的。如有必要,可以通过为类别引入查询缓存来缓解这种情况。

路由:

Rails.application.routes.draw do
  resources :posts
end

控制器:

class PostsController < ActionController::Base
  def show
    category = Category.find_by_slug params[:id]
    if category
      # ... render a Category

      return
    end

    post = Post.find_by_slug params[:id]
    # ... render a Post
  end