Rspec пусть переменная дает странный результат - PullRequest
5 голосов
/ 13 апреля 2019

У меня странная проблема с RSpec, которую я не совсем понимаю.

Это мой port_stock_spec.rb файл:

# == Schema Information
#
# Table name: port_stocks
#
#  id                :bigint(8)        not null, primary key
#  portfolio_id      :integer
#  stock_id          :integer
#  volume            :integer
#  transaction_price :float
#  current_price     :float
#  percent_change    :float
#  created_at        :datetime         not null
#  updated_at        :datetime         not null
#  current_value     :float
#  dollar_change     :float
#  total_spend       :float
#  transaction_date  :datetime
#  action            :integer
#  position          :integer          default("open")
#  ticker            :string
#  slug              :string
#

require 'rails_helper'

RSpec.describe PortStock, type: :model do
  let(:stock) { create(:stock, price: 10.00) }
  let(:portfolio) { create(:portfolio) }
  let(:port_stock_1) { create(:port_stock, stock: stock, portfolio: portfolio, transaction_price: stock.price, action: :buy, volume: 100) }

  context "associations" do
    it { should belong_to(:portfolio) }
    it { should belong_to (:stock) }
  end

  context "methods" do
    it "should accurately calculate the positive percent_change of the current PortStock" do
      port_stock_1.current_price = 20.00
      expect(port_stock_1.calculate_percent_change).to eql 100.00
    end

    it "should accurately calculate the negative percent_change of the current PortStock" do
      port_stock_1.current_price = 5.00
      expect(port_stock_1.calculate_percent_change).to eql(-50.00)
    end

    it "should accurately calculate the negative dollar_change of the current PortStock" do
      port_stock_1.current_price = 5.00
      port_stock_1.volume = 1000
      expect(port_stock_1.calculate_dollar_change).to eql (-5000.00)
    end

    # more specs that may or may no interact with the let variables.            

    it "should accurately calculate the portfolio's initial_dollar_value" do
      expect(portfolio.initial_dollar_value).to eql 1000.00
    end

end

ТогдаУ меня есть следующий метод на моей portfolio.rb модели:

  def calculate_portfolio_initial_dollar_value
    if self.portfolio.initial_dollar_value.nil?
      self.portfolio.initial_dollar_value = 0.0
    end
    self.portfolio.initial_dollar_value += (self.transaction_price * self.volume)
    self.portfolio.save!
  end

Когда я запускаю свой набор тестов, этот последний тест продолжает давать сбой, когда он не должен:

Failures:

  1) PortStock methods should accurately calculate the portfolio's initial_dollar_value
     Failure/Error: expect(portfolio.initial_dollar_value).to eql 1000.00

       expected: 1000.0
            got: 798229.0

       (compared using eql?)
     # ./spec/models/port_stock_spec.rb:77:in `block (3 levels) in <top (required)>'

Finished in 5.05 seconds (files took 3.68 seconds to load)
29 examples, 1 failure, 19 pending

ТакЯ помещаю binding.pry в блоки it последних нескольких тестов, и когда я проверяю portfolio.initial_dollar_value, он неоднократно меняет значение.

[1] pry(#<RSpec::ExampleGroups::PortStock::Methods>)> portfolio
=> #<Portfolio:0x00007fcdc5c5db28
 id: 14,
 user_id: 7,
 current_dollar_value: 2864770.0,
 percent_change: 75.02,
 created_at: Sat, 13 Apr 2019 00:36:24 UTC +00:00,
 updated_at: Sat, 13 Apr 2019 00:36:24 UTC +00:00,
 num_winners: 2,
 num_losers: 7,
 initial_dollar_value: 860679.0,
 dollar_change: 92865.0>
[2] pry(#<RSpec::ExampleGroups::PortStock::Methods>)> port_stock_1.portfolio
=> #<Portfolio:0x00007fcdc5c5db28
 id: 14,
 user_id: 7,
 current_dollar_value: 150.0,
 percent_change: -85.0,
 created_at: Sat, 13 Apr 2019 00:36:24 UTC +00:00,
 updated_at: Sat, 13 Apr 2019 00:36:42 UTC +00:00,
 num_winners: 0,
 num_losers: 1,
 initial_dollar_value: 1000.0,
 dollar_change: -850.0>
[3] pry(#<RSpec::ExampleGroups::PortStock::Methods>)> portfolio
=> #<Portfolio:0x00007fcdc5c5db28
 id: 14,
 user_id: 7,
 current_dollar_value: 150.0,
 percent_change: -85.0,
 created_at: Sat, 13 Apr 2019 00:36:24 UTC +00:00,
 updated_at: Sat, 13 Apr 2019 00:36:42 UTC +00:00,
 num_winners: 0,
 num_losers: 1,
 initial_dollar_value: 1000.0,
 dollar_change: -850.0>
[4] pry(#<RSpec::ExampleGroups::PortStock::Methods>)> portfolio
=> #<Portfolio:0x00007fcdc5c5db28
 id: 14,
 user_id: 7,
 current_dollar_value: 150.0,
 percent_change: -85.0,
 created_at: Sat, 13 Apr 2019 00:36:24 UTC +00:00,
 updated_at: Sat, 13 Apr 2019 00:36:42 UTC +00:00,
 num_winners: 0,
 num_losers: 1,
 initial_dollar_value: 1000.0,
 dollar_change: -850.0>
[5] pry(#<RSpec::ExampleGroups::PortStock::Methods>)> 

Я не понимаю, почему.

Мысли?

Редактировать 1

Это portfolio.rb Завод:

FactoryBot.define do
  factory :portfolio do
    user
    current_dollar_value { Faker::Number.number(7) }
    percent_change { Faker::Number.decimal(2) }
    num_winners { Faker::Number.number(1) }
    num_losers { Faker::Number.number(1) }
    initial_dollar_value { Faker::Number.number(6) }
    dollar_change { Faker::Number.number(5) }
  end
end

Редактировать 2

В моей модели port_stock.rb есть обратный вызов, который вызывает методы, относящиеся к portfolio_initial_dollar_value:

after_save :calculate_portfolio_initial_dollar_value

Также к другим обратным вызовам, влияющим на другие аспекты портфеля:

  after_save :update_portfolio_current_dollar_value
  after_save :update_portfolio_initial_dollar_value, if: (:total_spend_previously_changed? || :volume_previously_changed?)

  def update_portfolio_current_dollar_value
    self.portfolio.current_dollar_value = self.portfolio.port_stocks.open.map(&:current_value).sum
    self.portfolio.save!
  end

  def update_portfolio_initial_dollar_value
    self.portfolio.initial_dollar_value = self.portfolio.port_stocks.open.map { |ps| ps.volume * ps.transaction_price }.sum
    self.portfolio.save!
  end

Редактировать 3

Для полной версии файлов модели (port_stock.rb) и спецификаций (port_stock_spec.rb) проверьте из этой сущности .Я не хотел загрязнять ТАК этим полным сбросом.

Ответы [ 2 ]

4 голосов
/ 19 апреля 2019

Как указывает @grzekos, вы никогда не звоните stock или port_stock_1 во время выполнения теста it "should accurately calculate the portfolio's initial_dollar_value".

Почему?Потому что вы использовали let для настройки / создания тестовых объектов.Если вы хотите всегда настраивать / создавать stock, portfolio и port_stock_1, вы можете использовать let! ( Документация RSpec ) или использовать блок before, например:

let(:stock) { create(:stock, price: 10.00) }
let(:portfolio) { create(:portfolio) }
let(:port_stock_1) { create(:port_stock, stock: stock, portfolio: portfolio, transaction_price: stock.price, action: :buy, volume: 100) }

before do
  stock
  portfolio
  port_stock_1
end

Почему вы видите разные числа при отладке с помощью pry?

В первом тесте вы назвали объект portfolio, который был создан с помощью FactoryBot.Фабрика прислала случайное 6-значное число к атрибуту initial_dollar_value через Faker::Number.number(6).

Во втором тесте вы позвонили port_stock_1.portfolio.Теперь блок let(:port_stock_1) оценивается.Это создает объект PortStock, который в своем методе after_save обновляет initial_dollar_value из portfolio.

Все вызовы подпоследовательности portfolio или port_stock_1.portfolio не изменяют значение initial_dollar_value больше.

3 голосов
/ 19 апреля 2019

Хорошо, значит, тест на неудачу:

    it "should accurately calculate the portfolio's initial_dollar_value" do
      expect(portfolio.initial_dollar_value).to eql 1000.00
    end

Здесь я вижу, что вы создаете объект portfolio, а для initial_dollar_value (на заводе-изготовителе) установлено значение Faker::Number.number(6). Почему вы ожидаете, что он будет равен 1000,00?

Объекты stock или port_stock_1 никогда не создаются при запуске этого конкретного теста. Цитирование Rspec let документации

Используйте let, чтобы определить запомненный вспомогательный метод. Значение будет кэшировано через несколько вызовов в одном примере , но не во всех примерах . Обратите внимание, что let лениво оценивается: оно не вычисляется до первого раза вызывается определяемый им метод.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...