【问题标题】:Is this the right way to test my ruby class?这是测试我的 ruby​​ 课程的正确方法吗?
【发布时间】:2014-10-15 04:02:14
【问题描述】:

我正在构建一个投资工具并希望确保它计算出正确的到期金额

这里是一些源代码:

  def maturity_amount
    if cumulative
      amount = initial_deposit * (1 + (rate_of_interest)/4 ) ** (term * 4)
      amount.round
    else
      initial_deposit
    end
  end

所以我写了一个这样的测试:

it "can calculate interest earned" do
   investment = FactoryGirl.build(:investment, initial_deposit: 250000, rate_of_interest: 9.5, cumulative: true)
   expect(investment.maturity_amount).to eq(331335)
end

您在这里看到我将结果硬编码到测试中。这会使测试变得脆弱吗?此外,如果不每次都更新最终结果,我似乎无法轻易改变我对测试的输入。

那么有没有更好的方法来编写测试并且仍然有信心做出正确的计算?我应该嘲讽吗?还是我不应该首先编写这样的测试?如果有,为什么?

【问题讨论】:

    标签: ruby unit-testing rspec tdd


    【解决方案1】:

    由于您的到期金额的价值取决于某些条件,我认为它需要更多测试。以下是我可能会如何指定您的方法的行为(并稍微重构您的类)。
    我假设您的班级名为Investment

    class Investment
      def maturity_amount
        if cumulative
          # kept the `.round` out here in case you had other functions
          # that may take the `ceil` or `floor` etc of `interest_earned`
          interest_earned.round
        else
          initial_deposit
        end
      end
    
      # Refactor out the formula into its own method so it can
      # be tested in isolation
      def interest_earned
        initial_deposit * (1 + (rate_of_interest)/4 ) ** (term * 4)
      end
    end
    
    describe Investment do
      let(:investment) { described_class.new }
    
      describe '#maturity_amount' do
        let(:maturity_amount) { investment.maturity_amount }
    
        context 'when amount is cumulative' do
          before do
            allow(investment).to receive(:cumulative).and_return(true)
            allow(investment).to receive(:interest_earned).and_return(9999.8)
          end
    
          it 'returns the rounded value of the cumulative amount' do
            expect(maturity_amount).to eq(10000)
          end
        end
    
        context 'when amount is not cumulative' do
          before do
            allow(investment).to receive(:cumulative).and_return(false)
            allow(investment).to receive(:initial_deposit).and_return(10000)
          end
    
          it 'returns the initial deposit' do
            expect(maturity_amount).to eq(10000)
          end
        end
      end
    
      describe '#interest_earned' do
        let(:interest_earned) { investment.interest_earned }
        # you may have various circumstances/edge cases where you would like to
        # test that the interest earned calculates what you would expect, and I
        # would put them in different `context` blocks, but for simplicity's sake
        # I'll just use one simple case here
        before do
          # using the Factory values from your question...
          allow(investment).to receive(:initial_deposit).and_return(250000)
          allow(investment).to receive(:rate_of_interest).and_return(9.5)
          # not sure of your default term value would be, so just use 1
          allow(investment).to receive(:term).and_return(1)
        end
    
        it 'calculates the interest earned' do
          # something seems a bit strange here given this passing test...
          # I assume there are other values that contribute to this calculation
          # that I'm missing
          expect(interest_earned).to eq(32436584.47265625)
        end
      end
    end
    

    【讨论】:

      【解决方案2】:

      看起来不错!测试远非脆弱。仅当您更改输入或算法时,测试才会失败,而这正是它应该失败的条件。测试中不应包含任何逻辑或元编程。

      如果您没有根据输入做不同的事情,则无需编写许多具有不同输入的测试。这将增加您的测试套件的维护负担,而不会增加任何价值。

      简单更好。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-09-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多