Note that there are some explanatory texts on larger screens.

plurals
  1. POIs it a bad practice to randomly-generate test data?
    primarykey
    data
    text
    <p>Since I've started using rspec, I've had a problem with the notion of fixtures. My primary concerns are this:</p> <ol> <li><p>I use testing to reveal surprising behavior. I'm not always clever enough to enumerate every possible edge case for the examples I'm testing. Using hard-coded fixtures seems limiting because it only tests my code with the very specific cases that I've imagined. (Admittedly, my imagination is also limiting with respect to which cases I test.)</p></li> <li><p>I use testing to as a form of documentation for the code. If I have hard-coded fixture values, it's hard to reveal what a particular test is trying to demonstrate. For example:</p> <pre><code>describe Item do describe '#most_expensive' do it 'should return the most expensive item' do Item.most_expensive.price.should == 100 # OR #Item.most_expensive.price.should == Item.find(:expensive).price # OR #Item.most_expensive.id.should == Item.find(:expensive).id end end end </code></pre> <p>Using the first method gives the reader no indication what the most expensive item is, only that its price is 100. All three methods ask the reader to take it on faith that the fixture <code>:expensive</code> is the most expensive one listed in <code>fixtures/items.yml</code>. A careless programmer could break tests by creating an <code>Item</code> in <code>before(:all)</code>, or by inserting another fixture into <code>fixtures/items.yml</code>. If that is a large file, it could take a long time to figure out what the problem is.</p></li> </ol> <p>One thing I've started to do is add a <code>#generate_random</code> method to all of my models. This method is only available when I am running my specs. For example:</p> <pre><code>class Item def self.generate_random(params={}) Item.create( :name =&gt; params[:name] || String.generate_random, :price =&gt; params[:price] || rand(100) ) end end </code></pre> <p>(The specific details of how I do this are actually a bit cleaner. I have a class that handles the generation and cleanup of all models, but this code is clear enough for my example.) So in the above example, I might test as follows. A warning for the feint of heart: my code relies heavily on use of <code>before(:all)</code>:</p> <pre><code>describe Item do describe '#most_expensive' do before(:all) do @items = [] 3.times { @items &lt;&lt; Item.generate_random } @items &lt;&lt; Item.generate_random({:price =&gt; 50}) end it 'should return the most expensive item' do sorted = @items.sort { |a, b| b.price &lt;=&gt; a.price } expensive = Item.most_expensive expensive.should be(sorted[0]) expensive.price.should &gt;= 50 end end end </code></pre> <p>This way, my tests better reveal surprising behavior. When I generate data this way, I occasionally stumble upon an edge case where my code does not behave as expected, but which I wouldn't have caught if I were only using fixtures. For example, in the case of <code>#most_expensive</code>, if I forgot to handle the special case where multiple items share the most expensive price, my test would occasionally fail at the first <code>should</code>. Seeing the non-deterministic failures in AutoSpec would clue me in that something was wrong. If I were only using fixtures, it might take much longer to discover such a bug.</p> <p>My tests also do a slightly better job of demonstrating in code what the expected behavior is. My test makes it clear that sorted is an array of items sorted in descending order by price. Since I expect <code>#most_expensive</code> to be equal to the first element of that array, it's even more obvious what the expected behavior of <code>most_expensive</code> is.</p> <p>So, is this a bad practice? Is my fear of fixtures an irrational one? Is writing a <code>generate_random</code> method for each Model too much work? Or does this work?</p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload