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

如何为返回非确定性结果的Rails控制器编写测试?

Ruby on Rails 非确定性控制器的测试方案设计与实现

问题背景

  • 数据库包含Article表,字段为weight(整数)和name(字符串)
  • 库中存在10条weight和name均不重复的Article数据
  • 控制器处理带name参数的GET请求时,先筛选同名文章,再通过加权随机算法返回一条结果(权重越高被选中概率越大,例如权重1、3、5、7时,随机数范围为0-15,对应区间匹配对应文章)

现有测试思路

  • 无name参数的GET请求返回400状态码及"Bad parameters"信息
  • 带name参数的GET请求返回200状态码
  • 带不存在的name参数的GET请求返回200状态码及null
  • 验证同名文章筛选逻辑的正确性
  • 验证加权随机选择逻辑的概率符合预期(通过大样本运行统计结果,如权重1、2、3、4时,各文章选中次数需落在对应比例区间内)

核心测试点实现方案

拆分出的getSelectedArticles和randomWeight是核心业务逻辑,必须单独测试——单独测试能精准定位问题,避免集成测试因随机性导致的不稳定,也能覆盖概率逻辑的正确性。

1. 测试getSelectedArticles(筛选逻辑)

这是确定性逻辑,用单元测试验证筛选结果的准确性:

test "getSelectedArticles returns correct articles for given name" do
  # 准备测试数据
  alice_article1 = Article.create!(name: "Alice", weight: 2)
  alice_article2 = Article.create!(name: "Alice", weight: 5)
  bob_article = Article.create!(name: "Bob", weight: 3)

  # 实例化控制器并调用方法
  controller = ArticleController.new
  selected = controller.getSelectedArticles(name: "Alice")

  # 断言筛选结果符合预期
  assert_equal 2, selected.count
  assert_includes selected, alice_article1
  assert_includes selected, alice_article2
  assert_not_includes selected, bob_article
end

如果后续将该方法改为私有,可通过controller.send(:getSelectedArticles, name: "Alice")调用。

2. 测试randomWeight(加权随机逻辑)

这是非确定性逻辑,通过大样本统计+误差容忍的方式验证概率是否符合预期:

test "randomWeight selects articles with probability matching their weights" do
  # 模拟文章集合:权重分别为1、2、3、4,总权重10
  articles = [
    OpenStruct.new(weight: 1),
    OpenStruct.new(weight: 2),
    OpenStruct.new(weight: 3),
    OpenStruct.new(weight: 4)
  ]
  total_runs = 10000
  counts = Array.new(articles.size, 0)

  controller = ArticleController.new
  total_runs.times do
    index = controller.randomWeight(articles: articles)
    counts[index] += 1
  end

  # 预期概率:10%、20%、30%、40%,允许±5%的误差
  expected_ratios = [0.1, 0.2, 0.3, 0.4]
  tolerance = 0.05

  counts.each_with_index do |count, i|
    actual_ratio = count.to_f / total_runs
    expected_ratio = expected_ratios[i]
    assert_in_delta expected_ratio, actual_ratio, tolerance, 
      "Index #{i} had ratio #{actual_ratio.round(4)}, expected #{expected_ratio} ± #{tolerance}"
  end
end

3. 补充集成测试(整体流程验证)

确保控制器整体流程正常,返回结果在合理范围内:

test "returns one of the matching articles for existing name" do
  alice_article1 = Article.create!(name: "Alice", weight: 2)
  alice_article2 = Article.create!(name: "Alice", weight: 5)

  get "/article?name=Alice"
  assert_response :ok

  response_json = JSON.parse(@response.body)
  # 断言返回结果是目标name下的某一篇文章
  assert_includes [alice_article1.id, alice_article2.id], response_json["id"]
end

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 07:45:19