Как протестировать проблему в Rails

105

Учитывая, что у меня есть Personableпроблема с моим приложением Rails 4, у которого есть full_nameметод, как мне протестировать это с помощью RSpec?

проблемы / personable.rb

module Personable
  extend ActiveSupport::Concern

  def full_name
    "#{first_name} #{last_name}"
  end
end
Кайл Декот
источник
Какую среду тестирования вы используете? Также помните, что Personable - это обычный модуль Ruby. Протестируйте его так же, как любой другой миксин.
Ли Джарвис
Не ActiveSupport::Concernубирали из Rails? Я думал, что это было недавно.
Рассел
@LeeJarvis Я использую Rspec вместе с FactoryGirl
Кайл Декот
4
@Russell Я согласен. Тем не менее, я бы не стал помогать кому-то с их вопросами только потому, что они следовали способу Rails делать что-то, с чем я не согласен. В любом случае, это своего рода уход от темы этого вопроса :-)
Ли Джарвис

Ответы:

176

Найденный вами метод, безусловно, сработает для проверки некоторой функциональности, но кажется довольно хрупким - ваш фиктивный класс (на самом деле только Structв вашем решении) может или не может вести себя как настоящий класс, который includeвас беспокоит. Кроме того, если вы пытаетесь протестировать проблемы модели, вы не сможете делать такие вещи, как проверка достоверности объектов или вызывать обратные вызовы ActiveRecord, если вы не настроите базу данных соответствующим образом (потому что ваш фиктивный класс не будет иметь поддержку таблицы базы данных Это). Более того, вы захотите не только протестировать проблему, но и проверить ее поведение в спецификациях вашей модели.

Так почему бы не убить двух зайцев одним выстрелом? Используя общие группы примеров RSpec , вы можете проверить свои опасения относительно реальных классов, которые их используют (например, моделей), и вы сможете тестировать их везде, где они используются. И вам нужно только один раз написать тесты, а затем просто включить их в любую спецификацию модели, которая использует вашу проблему. В вашем случае это может выглядеть примерно так:

# app/models/concerns/personable.rb
module Personable
  extend ActiveSupport::Concern

  def full_name
    "#{first_name} #{last_name}"
  end
end

# spec/concerns/personable_spec.rb
require 'spec_helper'

shared_examples_for "personable" do
  let(:model) { described_class } # the class that includes the concern

  it "has a full name" do
    person = FactoryBot.build(model.to_s.underscore.to_sym, first_name: "Stewart", last_name: "Home")
    expect(person.full_name).to eq("Stewart Home")
  end
end

# spec/models/master_spec.rb
require 'spec_helper'
require Rails.root.join "spec/concerns/personable_spec.rb"

describe Master do
  it_behaves_like "personable"
end

# spec/models/apprentice_spec.rb
require 'spec_helper'

describe Apprentice do
  it_behaves_like "personable"
end

Преимущества этого подхода становятся еще более очевидными, когда вы начинаете делать что-то интересное, например, вызывать обратные вызовы AR, где ничего, кроме объекта AR, просто не подойдет.

Джош Лейтцель
источник
2
Одним из недостатков этого является то, что он замедляется parallel_tests. Я думаю, что лучше будет иметь отдельные тесты вместо использования shared_examples_forи it_behaves_like.
Артем Калинчук
6
@ArtemKalinchuk Я не уверен, что это правда, per github.com/grosser/parallel_tests/issues/168 parallel_tests основаны на файле, поэтому общие примеры не должны замедлять его. Я бы также сказал, что правильно сгруппированные общие поведения важнее скорости тестирования.
Aaron K
8
Не забудьте включить concernsкаталог в свой spec_helper.rb github.com/rspec/rspec-core/issues/407#issuecomment-1409871
Ziggy
1
Я не смог найти ничего о включении каталога проблем в эту ссылку. Не могли бы вы пояснить, как это делается? Я не могу заставить свой тест RSpec распознать модуль в одной из моих проблем.
Джейк Смит
4
Не добавляйте _specк имени файла, которое содержит shared_examples_for (в данном случае personable_spec.rb), иначе вы получите вводящее в заблуждение предупреждающее сообщение - github.com/rspec/rspec-core/issues/828 .
Lalu
64

В ответ на полученные мной комментарии вот что я в итоге сделал (если у кого-то есть улучшения, не стесняйтесь публиковать их) :

спецификация / проблемы / personable_spec.rb

require 'spec_helper'

describe Personable do
  let(:test_class) { Struct.new(:first_name, :last_name) { include Personable } }
  let(:personable) { test_class.new("Stewart", "Home") }

  it "has a full_name" do
    expect(personable.full_name).to eq("#{personable.first_name} #{personable.last_name}")
  end
end
Кайл Декот
источник
1
Да, это нарушит работу других тестов, если они будут проверять реальный класс с именем Person. Отредактирую, чтобы исправить.
Рассел
Это не работает. Это дает мне ошибку:undefined method 'full_name' for #<struct first_name="Stewart", last_name="Home">
Кайл Декот
Попробуйте включить Personable, а не расширять его. Обновлю ответ.
Рассел
Теперь отлично работает. Спасибо, что указали мне правильное направление и помогли рефакторинг @Russell
Kyle Decot
Отлично работает и красиво выглядит
Эдвард
7

Еще одна мысль - использовать гем with_model для тестирования подобных вещей. Я сам искал, чтобы проверить проблему, и видел, как гем pg_search делал это . Это кажется намного лучше, чем тестирование на отдельных моделях, поскольку они могут измениться, и неплохо определить то, что вам понадобится, в вашей спецификации.

лобати
источник