对于标准模型/控制器测试,我通常没有任何问题,但问题是我想测试一些复杂的功能过程,并不真正知道如何构建我的测试文件夹/文件/数据库.
这是我的应用程序的基本结构:
- 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.
对于订单/发票/ 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,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,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,4))
- invoice_summary = InvoiceSummary.create(
- begin_date: Date.new(2015,1),end_date: Date.new(2015,3)
- ) # real InvoiceSummary method
- invoice_summary.invoices.count.should? == 3
- end
有没有人有这样的测试?对象工厂的结构,写作测试等都有很好的做法吗?
例如,我被告知,一个好主意是将客户/电子钱包创建放在一个db / seed.rb文件中,但我不知道该怎么做.
解决方法
关于创建要测试的对象,
> db / seeds.rb不适用于测试数据,但不适用于应用程序运行,无论是在开发还是测试或生产中都是用户未更改的静态数据.这种数据的常见例子包括国家代码和用户角色.
>创建测试数据,夹具和工厂有两种一般方法.
> Fixtures是开箱即用的Rails方法.创建测试数据库时,创建它们一次.当您进行许多测试时,它们很快,因为它们只在测试套件的开头创建一次.然而,他们扭曲测试是因为它们鼓励在现有的固定装置上进行测试,所以我强烈建议您对它们进行测试.
>工厂是对象创建工具.您可以在每个测试中创建所需的对象,并将其删除或结束时回滚.使用工厂的大多数Rails项目都使用FactoryGirl.这就是我推荐的
这两种方法将对象创建代码放在自己的文件中的不同于您的规范的目录中,从而避免了您的规范文件.如果您搜索SO“固定装置或工厂”,您将会发现更多的讨论.
如果您将所有重要的值(在这种情况下包括日期和金额)放在规范中,并将其与您所声明的结果进行比较,那么您的规格将更容易理解. (否则,您需要记住测试对象中的日期和金额以了解规范.)您可以在测试日期和金额参数中给出对象创建方法.那么你可能需要更少的方法.如果您使用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文件转换成spec文件的目录,如How to break down super long specs in RSpec?所述.