【问题标题】:Minitests are flaky on pure function without side-effectsMinitests 在纯函数上很不稳定,没有副作用
【发布时间】:2020-10-26 18:35:24
【问题描述】:

由于我下周将加入 Rails 项目(耶!),所以我正在做 RomanNumerals kata 作为工具的调整,但我编写的测试随机失败(嘘!)。

  • 每次我让它们运行时,无需对代码进行任何更改,我都会得到不同的结果。
  • 我使用rails test 作为命令。
  • 这些版本是从网上新鲜出炉的。来自rails new 的所有内容都没有任何额外的宝石。 (Ruby 2.7.0,Rails 6.0.3)

在并行运行的测试和一些变量被其他测试覆盖之间似乎有些奇怪的混合。或者可能有一些奇怪的优化事情涉及缓存?


Running via Spring preloader in process 18166
Run options: --seed 29967

# Running:

.......F

Failure:
RomanNumeralHelperTest#test_18_returns_XVIII [/home/eric/rails/rome/test/helpers/roman_numeral_helper_test.rb:55]:
Expected: "XVIII"
  Actual: "IXVIII"


rails test test/helpers/roman_numeral_helper_test.rb:54

........

Finished in 0.066531s, 240.4883 runs/s, 240.4883 assertions/s.
16 runs, 16 assertions, 1 failures, 0 errors, 0 skips
Running via Spring preloader in process 18195
Run options: --seed 59433

# Running:

.............F

Failure:
RomanNumeralHelperTest#test_15_returns_XV [/home/eric/rails/rome/test/helpers/roman_numeral_helper_test.rb:51]:
Expected: "XV"
  Actual: "IXV"


rails test test/helpers/roman_numeral_helper_test.rb:50

..

Finished in 0.053298s, 300.2008 runs/s, 300.2008 assertions/s.
16 runs, 16 assertions, 1 failures, 0 errors, 0 skips
Running via Spring preloader in process 18247
Run options: --seed 14645

# Running:

................

Finished in 0.048711s, 328.4691 runs/s, 328.4691 assertions/s.
16 runs, 16 assertions, 0 failures, 0 errors, 0 skips
class RomanNumeralHelperTest < ActiveSupport::TestCase
  test "1 returns I" do
    assert_equal "I", RomanNumeralHelper.convert(1)
  end  

  test "2 returns II" do
    assert_equal "II", RomanNumeralHelper.convert(2)
  end  
  
  test "3 returns III" do
    assert_equal "III", RomanNumeralHelper.convert(3)
  end

  # Intermittent tests omitted. They go from 1 to 20.

  test "20 returns XX" do
    assert_equal "XX", RomanNumeralHelper.convert(20)
  end
end
module RomanNumeralHelper
  ROMAN_ONE = "I"
  ROMAN_FIVE = "V"
  ROMAN_TEN = "X"
  def self.convert(number)
    result = ""
    current_numeral = ""
    remaining_number = number    

    until remaining_number == 0 do
      if remaining_number > 8
        current_numeral = ROMAN_TEN
        remaining_number -= 10
      elsif remaining_number > 3
        current_numeral += ROMAN_FIVE
        remaining_number -= 5
      else
        result += ROMAN_ONE * remaining_number
        remaining_number = 0
      end      
      if remaining_number < 0
        current_numeral.prepend(ROMAN_ONE)
        remaining_number = 0
      end      

      result += current_numeral
      current_numeral = ""
    end    

    result
  end
end

【问题讨论】:

  • 我无法重现这个,它每次都返回正确的结果
  • 不久前我做了一个罗马数字单行字(如果你去掉 cmets),你可能对stackoverflow.com/a/63830535/544825 感兴趣
  • @Eyeslandic 有趣。所以这不是关于代码,而是我的设置很奇怪?您是在新环境中使用 rails new 的吗?也许我应该把它放在一个 Docker 容器中以使其具有额外的可重复性......
  • @max 很酷。不幸的是,在这种情况下,与其说是解决方案,不如说是让周围的一切正常工作。不过谢谢!
  • @Eric 是的,这只是使用 Ruby 2.6.5 的全新 Rails 6.0.3.4 设置

标签: ruby-on-rails ruby minitest


【解决方案1】:

我的错误是修改了一个常量。

current_numeral = ROMAN_TEN

current_numeral.prepend(ROMAN_ONE)

我更改了 ROMAN_TEN 引用的字符串。解决方法是:

ROMAN_TEN = "X".freeze

current_numeral = ROMAN_TEN.dup

同时使用 rubocop - 即使是在这样的小项目中 - 也会给我一个警告。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-12-04
    • 2020-06-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-02
    相关资源
    最近更新 更多