# Complex tests - use a DSL instead of fixtures

**URL:** <https://www.ruby-forum.com/t/complex-tests-use-a-dsl-instead-of-fixtures/100465>\
**Category:** Rails\
**Created:** [July 29, 2006, 3:57am UTC](https://www.ruby-forum.com/t/complex-tests-use-a-dsl-instead-of-fixtures/100465 "2006-07-29T03:57:49Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![giaco](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/g/a3d4f5/32.png) [@giaco](https://www.ruby-forum.com/u/giaco)\
**Post date:** [July 29, 2006, 3:57am UTC](https://www.ruby-forum.com/t/complex-tests-use-a-dsl-instead-of-fixtures/100465/1 "2006-07-29T03:57:49Z")

</div>

I’ve been finding I use fixtures less and less lately. I am working with  
rather complex testing of code that does data analysis. Each test may  
involve 5-10 tables and some of the data is different in nearly every  
test. I’ve found it much more readable to build a mini-DSL for the  
setting up the database rather than using a huge amount of fixtures. It  
has the added benefit of being able to see the state of your database in  
the same spot as the test. Trying to hop back and forth from the unit  
tests to 5-10+ yml files to check all that a test is doing can really be  
frustrating. The place I’m still finding them useful is for core data  
that is used in many different tests across many different test suites.

Any thoughts?

Jack

---

<div class="post-metadata">

**Author:** ![giaco](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/g/a3d4f5/32.png) [@giaco](https://www.ruby-forum.com/u/giaco)\
**Post date:** [July 29, 2006, 4:46am UTC](https://www.ruby-forum.com/t/complex-tests-use-a-dsl-instead-of-fixtures/100465/2 "2006-07-29T04:46:18Z")

</div>

I agree that flipping between multiple fixture files can be quite  
tedious. Can you provide examples of how you use the DSL instead of  
fixtures?

-Jonathan.

---

<div class="post-metadata">

**Author:** ![giaco](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/g/a3d4f5/32.png) [@giaco](https://www.ruby-forum.com/u/giaco)\
**Post date:** [July 29, 2006, 6:06am UTC](https://www.ruby-forum.com/t/complex-tests-use-a-dsl-instead-of-fixtures/100465/3 "2006-07-29T06:06:54Z")

</div>

It’d be great if you could share some examples of what you’ve done; I’m  
running into a smaller version of the same problem.

Thanks!  
Daniel

---

<div class="post-metadata">

**Author:** ![giaco](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/g/a3d4f5/32.png) [@giaco](https://www.ruby-forum.com/u/giaco)\
**Post date:** [July 29, 2006, 2:26pm UTC](https://www.ruby-forum.com/t/complex-tests-use-a-dsl-instead-of-fixtures/100465/4 "2006-07-29T14:26:45Z")

</div>

What is the meaning of ‘mini-dsl’ ??

Thanks

---

<div class="post-metadata">

**Author:** ![giaco](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/g/a3d4f5/32.png) [@giaco](https://www.ruby-forum.com/u/giaco)\
**Post date:** [July 29, 2006, 7:25pm UTC](https://www.ruby-forum.com/t/complex-tests-use-a-dsl-instead-of-fixtures/100465/5 "2006-07-29T19:25:18Z")

</div>

On Friday, July 28, 2006, at 6:06 PM, Daniel H. wrote:

> > test. I’ve found it much more readable to build a mini-DSL for the
> > 
> > * * *
> > 
> > Rails mailing list  
> > removed\_email\_address@domain.invalid  
> > [http://lists.rubyonrails.org/mailman/listinfo/rails](http://lists.rubyonrails.org/mailman/listinfo/rails)
> 
> * * *
> 
> Rails mailing list  
> removed\_email\_address@domain.invalid  
> [http://lists.rubyonrails.org/mailman/listinfo/rails](http://lists.rubyonrails.org/mailman/listinfo/rails)

This may not be quite the same thing, but I’ve been getting away from  
fixtures a lot lately. I add some code to my mocks that let me generate  
objects that match certain criteria. For example, I usually define a  
method to create a valid object and an invalid one (by AR standards).

I then create one of these objects and feed it to my tests, depending on  
what I need it to do. Since I also test the factory functions to make  
sure they actually create valid or invalid objects, I have a lot more  
confidence that the tests are working properly. Otherwise I would need  
go and maintain my fixtures frequently.

There may be some performance issues using this approach, but I haven’t  
looked into it very much. Perhaps I need to be generating the fixtures  
on the fly and re-using them throughout each test bundle.

\_Kevin  
[www.sciwerks.com](http://www.sciwerks.com)

---

<div class="post-metadata">

**Author:** ![giaco](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/g/a3d4f5/32.png) [@giaco](https://www.ruby-forum.com/u/giaco)\
**Post date:** [July 30, 2006, 10:42pm UTC](https://www.ruby-forum.com/t/complex-tests-use-a-dsl-instead-of-fixtures/100465/6 "2006-07-30T22:42:41Z")

</div>

nuno wrote:

> What is the meaning of ‘mini-dsl’ ??
> 
> Thanks

DSL = domain specific language. Mini, because in this case, it usually  
only has several methods.

Wikipedia description of DSL’s -

> **[Domain-specific language](https://en.wikipedia.org/wiki/Domain_Specific_Language)**
>
> A domain-specific language (DSL) is a computer language specialized to a particular application domain. This is in contrast to a general-purpose language (GPL), which is broadly applicable across domains. There are a wide variety of DSLs, ranging from widely used languages for common domains, such as HTML for web pages, down to languages used by only one or a few pieces of software, such as MUSH soft code. DSLs can be further subdivided by the kind of language, and include domain-specific markup ...

Jack

---

<div class="post-metadata">

**Author:** ![giaco](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/g/a3d4f5/32.png) [@giaco](https://www.ruby-forum.com/u/giaco)\
**Post date:** [July 30, 2006, 5:13am UTC](https://www.ruby-forum.com/t/complex-tests-use-a-dsl-instead-of-fixtures/100465/7 "2006-07-30T05:13:03Z")

</div>

aniel Higginbotham wrote:

> > involve 5-10 tables and some of the data is different in nearly every  
> > Jack

I can’t release the actual code, and it wouldn’t really make sense  
outside of my employer’s unique situation, but here is a rough example  
that should illustrate the idea.

The scenario is a delivery company. In particular, testing code that  
estimates the profit of jobs. In this simplified scenario we will have  
only six tables: drivers, cargo types, trucks types, customers, jobs,  
and job cargoes. Drivers contain the pay per mile. Truck types both  
contain the maintenance cost per mile of operation as well as the fuel  
efficiency. Cargoes contains the pay per mile for different cargoes.  
Customers can contain a discount percentage if they are under a bulk  
contract. A job has one driver, has one truck type, has one customer,  
and has many job cargoes. A job also contains the distance traveled and  
the current price of fuel.

Tests would something look like this:

def test\_job\_with\_customer\_discount  
driver\_pay\_per\_mile 0.43  
truck\_costs :maintenance\_per\_mile =\> 0.25, :miles\_per\_gallon =\> 7.6  
customer\_discount 0.8  
cargo :amount =\> 200, :pay =\> 7.25  
cargo :amount =\> 5, :pay =\> 30.0  
fuel\_price 3.20  
distance 850

assert\_estimated\_profit 400  
end

With fixtures you would have the following:

def test\_job\_with\_customer\_discount  
assert\_equal 400, jobs(:with\_customer\_discount).estimate\_profit  
end

But you would need to hop through 6 YML files keeping track of all FK’s  
in your head to figure out what it was actually testing against.

To make the DSL style test work you would have something like this:

def setup  
@job = Job.new  
end

def driver\_pay\_per\_mile( p )  
@job.driver = Driver.new( :pay\_per\_mile =\> p, … other required stuff  
for valid driver )  
end

def truck\_costs( hash )  
@job.truck\_type = TruckType.new( hash.merge( …other required truck  
type stuff ) )  
end

def customer\_discount( discount )  
@job.customer = Customer.new( :discount =\> discount, … other  
customer stuff )  
end

def cargo( hash )  
new\_cargo\_type = CargoType.new :pay\_per\_mile =\> hash[:pay\_per\_mile,  
… other cargo type stuff  
@job.job\_cargoes\<\< JobCargo.new( :cargo\_type =\> new\_cargo\_type,  
:amount =\> hash[:amount]  
end

def fuel\_price( price )  
@job.fuel\_price = price  
end

def distance( miles )  
@job.distance = miles  
end

def assert\_profit( p )  
assert\_equal p, @job.estimate\_profit  
end

Jack
