2015-08-10 6 views
13

Я развиваюсь в RoR уже более года, но я только начинаю использовать тесты, используя RSpec.Как структурировать мои тестовые папки 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 

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

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

  • Моделирование потока времени, создание и обновление нескольких объектов заказов/счетов и некоторых nvoice_summaries.

Для создания и обновления этих заказов/счетов/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 

Я уже нашел драгоценный камень 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 

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

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

+0

@HunterStevens Я считаю, что ваше изменение неверно, потому что вы удалили некоторую логику о заказах. 'def create_order_n; end' IS отличается от 'def create_order_1; end' 'def create_order_2; end', так как @vincent хотел выразить необходимость сделать 2 полностью разных вещи. Вы должны быть осторожны перед редактированием, как это ... – Erowlin

+0

@ Erowlin, пожалуйста, откатите мое редактирование. Я пытался убрать очень длинный пост. Сожалею. – onebree

+0

NP, просто будьте осторожны в следующий раз;). Кстати, спасибо за ваше редактирование, это началось с хорошего намерения! – Erowlin

ответ

5

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

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

  • дб/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 
    ... 
    ... 

Если ваши данные для одного класса становятся слишком большими, это признак того, что класс слишком велик. Найдите некоторый шаблон, чтобы разбить его и проверить отдельные части. Если вы действительно не можете разбить класс (редкая ситуация), включите спецификационный файл класса в каталог спецификационных файлов, как я описал в How to break down super long specs in RSpec?.

+0

Подробнее о [Структура справочной информации RSpec здесь] (https://relishapp.com/rspec/rspec-rails/docs/directory-structure). –

0

Для выполнения этой задачи вы должны использовать 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 стремятся решить.