RSpec: DRY way to test a set of values

I have a vote model that has a class class method. Basically, I created a mathematical equation in a spreadsheet and am trying to reproduce this in ruby. However, my first exit does not work, so I really need to start adding more subtle tests.

The way I would like to get tested is to take a stack of input and output values ​​from my spreadsheet and test them each. Thus, basically, tests can be interpreted as follows:

  inputs = [a,b,c] ... score.should == x inputs = [a,b,c,d] ... score.should == y inputs = [c,d] .... score.should == z 

However, the shortest way I actually found to write this in RSpec gives an example for each case, something like this (a simplified example, but should give you an idea):

 it "should have a score of X" do test_object = Votable.new(...) @user1.vote.create(:value=>##, :votable=>test_object) @user2.vote.create(:value=>##, :votable=>test_object) @user3.vote.create(:value=>##, :votable=>test_object) test_object.votes.score.should == X end 

So, the above works, but its loading the text for each example, as well as in order to smooth out the kinks and provide good test coverage, I would like to run about 20 or so test cases.

So seriously, there should be an easier way to set this up once, and then check out a bunch of possible I / O combinations, right? Can anyone suggest a DRY way to do such a test in RSpec?

Thanks!

+4
source share
2 answers

Yes, you could do the following metaprogramming to run a series of tests that follow the same format:

 results = { x: ['a', 'b', 'c'], y: ['a','b','c','d'] } results.each do |score, values| it "should have a score of #{score}" do test_object = Votable.new(...) values.each do |value| User.create(...).vote.create(value: value, votable: test_object) end test_object.votes.score.should == score end end 
+4
source

@Pan Thomakos:

Your answer inspired me (so I accepted it!), But I actually created something a little different from your suggestion above. I am so pleased that I thought I would share it if it would benefit anyone else.

My model used to have this method:

 def self.score dd = where( :value => -2 ).count.to_f d = where( :value => -1 ).count.to_f u = where( :value => 1 ).count.to_f uu = where( :value => 2 ).count.to_f tot = dd + d + u + uu score = (((-5*dd)+(-2*d)+(2*u)+(5*uu))/(tot+4))*20 score.round(2) end 

This worked, but it requires counting votes from the database, counting votes with each possible value (-2, -1, +1, +2), and then calculating points from these values.

Since I needed to check, there was no ActiveRecord ability to find and count query results, but my algorithm for turning these counters into an estimate, I divided this into two methods, for example:

 def self.score dd = where( :value => -2 ).count d = where( :value => -1 ).count u = where( :value => 1 ).count uu = where( :value => 2 ).count self.compute_score(dd,d,u,uu) end def self.compute_score(dd, d, u, uu) tot = [dd,d,u,uu].sum.to_f score = [-5*dd, -2*d, 2*u, 5*uu].sum / [tot,4].sum*20.0 score.round(2) end 

So now I can just test the compute_score method without creating a bunch of fake users and fake votes to test the algorithm. My test now looks like this:

 describe "score computation" do def test_score(a,b,c,d,e) Vote.compute_score(a,b,c,d).should == e end it "should be correct" do test_score(1,0,0,0,-20.0) test_score(0,1,0,0,-8.0) test_score(0,0,1,0,8.0) test_score(0,0,0,1,20.0) test_score(0,0,10,100,91.23) test_score(0,6,60,600,92.78) test_score(0,20,200,2000,93.17) end end 

In my opinion, this is super legible, and if I ask RSpec for formatted output, it will read well enough for what it is testing.

I hope this technique will be useful to others!

0
source

Source: https://habr.com/ru/post/1346401/


All Articles