如何构建我的RSpec测试文件夹,文件和数据库?

Vin*_*res 13 ruby rspec ruby-on-rails functional-testing rspec-rails

我已经在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
Run Code Online (Sandbox Code Playgroud)

主要问题是我想模拟我的对象的生命周期,这意味着:

  • 实例化将用于所有测试的客户和钱包(无需重新初始化)

  • 模拟时间流,创建和更新多个订单/发票对象和一些invoice_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
Run Code Online (Sandbox Code Playgroud)

我已经找到了用于模拟时间流的宝石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
Run Code Online (Sandbox Code Playgroud)

有人有过这样的测试吗?是否有构建对象工厂,编写测试等的良好实践?

例如,我被告知一个好主意是将客户/电子钱包创建放在db/seed.rb文件中,但之后我真的不知道如何处理它.

Dav*_*uth 5

完全回答你的问题可以填写并填写书籍,所以我只能在这里勾勒出答案.

关于创建要测试的对象,

  • db/seeds.rb不是用于测试数据,而是用于静态数据,不是用户更改的,这是应用程序运行所需的,无论是在开发,测试还是生产中.此类数据的常见示例包括国家/地区代码和用户角色.

  • 创建测试数据,夹具和工厂有两种通用方法.

    • Fixtures是开箱即用的Rails方法.它们在创建测试数据库时创建一次.当你有很多测试时,它们很快,因为它们只在测试套件的开头创建一次.然而,他们经过测试是因为他们鼓励在现有灯具周围进行测试,因此我强烈建议不要使用它们.
    • 工厂是对象创建实用程序.您可以在每个测试中创建所需的对象,并在最后删除或回滚它们.大多数使用工厂的Rails项目都使用FactoryGirl.这就是我推荐的.

    这两种方法都将对象创建代码放在与您的规范不同的目录中的自己的文件中,这使得它不会阻止您的spec文件.如果您搜索SO以寻找"固定装置或工厂",您会发现更多关于两者的讨论.

如果您将所有重要值(在这种情况下包括日期和金额)放在可以查看它们的规范中并与您声明的结果进行比较,那么您的规范将更容易理解.(否则,您需要记住测试对象中的日期和数量以了解规范.)您可以在测试dateamount参数中提供对象创建方法.那么您可能需要更少的方法.如果您使用FactoryGirl,它可能只是指定每个对象created_atamount属性的问题.还要注意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
    ...
  ...
Run Code Online (Sandbox Code Playgroud)

如果你对单个课程的规格太大,那就表明课程太大了.找到一些模式来分解它并单独测试它们.如果你真的无法分解类(一种罕见的情况),请将类的spec文件转换为spec文件的目录,正如我在如何分解RSpec中的超长规范中描述的那样.