Подтвердить что ты не робот

Как структурировать мои тестовые папки RSpec, файлы и базу данных?

Я развиваюсь в RoR уже более года, но я только начинаю использовать тесты, используя RSpec.

Для стандартных тестов модели/контроллера у меня обычно нет проблем, но проблема в том, что я хочу протестировать некоторые сложные функциональные процессы и не знаю, как структурировать свои тестовые папки/файлы/базу данных.

Вот базовая структура для моего приложения:

class Customer
  has_one    :wallet
  has_many :orders    
  has_many :invoices, through: :orders
  has_many :invoice_summaries
end

class Wallet
  belongs_to :customer
end

class Order
  has_one    :invoice
  belongs_to :customer
end

class Invoice
  belongs_to :order
  belongs_to :invoice_summary
end

class InvoiceSummary
  belongs_to :customer
  has_many  :invoices
end

Основная проблема заключается в том, что я хочу моделировать жизненный цикл моего объекта, что означает:

  • Создание клиентов и кошельков, которые будут использоваться для всех тестов (без повторной инициализации)

  • Имитация потока времени, создание и обновление нескольких объектов заказов/счетов и некоторых счетов-фактур.

Для создания и обновления заказов/счетов/invoice_summaries мне бы хотелось иметь такие методы, как

def create_order_1
  # code specific to create my first order, return the created order
end

def create_order_2
  # code specific to create my second order, return the created order
end
.
.
.
def create_order_n
  # code specific to create my n-th order, return the created order
end

def bill_order(order_to_bill)
  # generic code to do the billing of the order passed as parameter
end

def cancel_order(order_to_cancel)
  # generic code to cancel the order passed as parameter
end

Ive уже нашел драгоценный камень Timecop для моделирования потока времени. Следовательно, я хотел бы иметь понятный окончательный тест, который выглядит как

# Code for the initialization of customers and wallets object

describe "Wallet should be equal to 0 after first day" do
  Timecop.freeze(Time.new(2015,7,1))
  first_request = create_request_1
  first_request.customer.wallet.value.should? == 0
end

describe "Wallet should be equal to -30 after second day" do
  Timecop.freeze(Time.new(2015,7,2))
  bill_order(first_request)
  second_order = create_order_2
  first_request.customer.wallet.value.should? == -30
end

describe "Wallet should be equal to -20 after third day" do 
  Timecop.freeze(Time.new(2015,7,3))
  bill_order(second_request)
  cancel_order(first_request)
  first_request.customer.wallet.value.should? == -20
end

describe "Three first day invoice_summary should have 3 invoices" do
  Timecop.freeze(Time.new(2015,7,4))
  invoice_summary = InvoiceSummary.create(
      begin_date: Date.new(2015,7,1),
      end_date: Date.new(2015, 7,3)
  ) # real InvoiceSummary method
  invoice_summary.invoices.count.should? == 3
end

У кого-нибудь уже есть такие тесты? Существуют ли хорошие методы структурирования объектов-объектов, написания тестов и т.д.?

Например, мне сказали, что хорошей идеей было бы создать создание Customer/Wallet в файле db/seed.rb, но я действительно не знаю, что с ним делать потом.

4b9b3361

Ответ 1

Полностью отвечая на ваш вопрос, вы можете заполнить и заполнить книги, поэтому я могу только набросать здесь ответ.

Что касается создания объектов для тестирования,

  • db/seeds.rb не предназначен для тестовых данных, а для статических данных, не измененных пользователями, которые необходимы для запуска приложения, будь то в разработке, тестировании или производстве. Общие примеры такого рода данных включают коды стран и роли пользователей.

  • Существует два общих подхода к созданию тестовых данных, светильников и фабрик.

    • Светильники - это готовый метод Rails. Они создаются один раз при создании тестовой базы данных. Они быстры, когда у вас много тестов, потому что они создаются только один раз в начале набора тестов. Тем не менее, они деформируют тесты, потому что они рекомендуют писать тесты вокруг существующих светильников, поэтому я настоятельно рекомендую их против.
    • Заводы - это утилиты для создания объектов. Вы создаете объекты, которые вам нужны в каждом тесте, и удаляете или откатываете их обратно в конце. Большинство проектов Rails, которые используют фабрики, используют FactoryGirl. Это то, что я рекомендую.

    Оба подхода ставят код создания объекта в его собственных файлах в другом каталоге, чем ваши спецификации, что препятствует его размножению ваших файлов спецификаций. Если вы ищете SO для "светильников или фабрик", вы найдете гораздо больше обсуждений обоих.

Ваши спецификации будут легче понять, если вы поместите все важные значения, которые в этом случае включают даты и суммы, в спецификации, где их можно увидеть и сравнить с результатами, которые вы утверждаете. (В противном случае вам нужно запомнить даты и суммы в тестовых объектах, чтобы понять спецификации.) Вы могли бы дать методы создания объекта в своих тестовых параметрах date и amount. Тогда вам может понадобиться меньше методов. Если вы использовали FactoryGirl, это может быть просто вопрос определения атрибутов каждого объекта created_at и amount. Обратите также внимание на то, что Rails имеет такие методы, как 1.day.from_now; если вы создаете объекты с датами, указанными таким образом, вам может не понадобиться timecop.

Относительно того, как выкладывать спецификации RSpec в файловой системе на верхнем уровне, просто сделайте макет таким же, как у вашего приложения Rails:

app/
  controllers/
    bars_controller.rb
    foos_controller.rb
  models/
    bar.rb
    foo.rb
    ...
  ...

spec/
  controllers/
    bars_controller_spec.rb
    foos_controller_spec.rb
    ...
  models/
    bar_spec.rb
    foo_spec.rb
    ...
  ...

Если ваши спецификации для одного класса становятся слишком большими, это признак того, что класс слишком велик. Найдите некоторый шаблон, чтобы разбить его и проверить отдельные части. Если вы действительно не можете разбить класс (редкая ситуация), превратите файл spec класса в каталог спецификационных файлов, как я описал в Как сломать сверхдлинные спецификации в RSpec?.

Ответ 2

Вы должны использовать FactoryGirl для своей задачи. Настройте его, как описано в документации, а затем просто используйте его следующим образом:

# factories.rb
factory :order do
end

# your spec
first_order = create(:order, ...) # configure parameters of order in-place

Или у конкретных фабрик обрабатываются различные типы запросов:

# factories.rb
factory :expensive_order, class: Order do
  amount 999 # have 'amount' field of Order be equal to 999
end

# your spec
first_order = create(:expensive_order)

Вы можете иметь FactoryGirl автоматически обрабатывать ваши ассоциации:

factory :order do
  association :user # automatically create User association
end

Вы описываете точную проблему, которую разработчики FactoryGirl стремятся решить.