I'd like to know about idioms or best practices for testing a multi-step workflow using rspec.
Let's take as an example a "shopping cart" system, where the buying process might be
I've read http://eggsonbread.com/2010/03/28/my-rspec-best-practices-and-tips/ which advises i.a that each "it block" should contain only one assertion: instead of doing the computation and then testing several attributes in the same block, use a "before" inside a context to create (or retrieve) the object under test and assign it to @some_instance_variable, then write each attribute test as a separate block. That helps a little, but in a case such as outlined above where testing step n requires doing all the setup for steps [1..n-1] I find myself either duplicating setup code (obviously not good) or creating lots of helper functions with increasingly unwieldy names (def create_basket_with_three_lines_and_two_products) and calling them consecutively in each step's before block.
Any tips on how to do this less verbosely/tediously? I appreciate the general principle behind the idea that each example should not depend on state left behind by previous examples, but when you're testing a multi-step process and things can go wrong at any step, setting up the context for each step is inevitably going to require rerunning all the setup for the previous n steps, so ...
To run a single Rspec test file, you can do: rspec spec/models/your_spec. rb to run the tests in the your_spec. rb file.
RSpec is a unit test framework for the Ruby programming language. RSpec is different than traditional xUnit frameworks like JUnit because RSpec is a Behavior driven development tool. What this means is that, tests written in RSpec focus on the "behavior" of an application being tested.
Boot up your terminal and punch in gem install rspec to install RSpec. Once that's done, you can verify your version of RSpec with rspec --version , which will output the current version of each of the packaged gems. Take a minute also to hit rspec --help and look through the various options available. That's it.
Here's one possible approach -- define an object that creates the necessary state for each step and pass it forward for each successive one. Basically you need to mock/stub the method calls for all the setup conditions:
class MultiStep
def initialize(context)
@context = context
end
def init_vars
@cut = @context.instance_variable_get(:@cut)
end
def setup(step)
init_vars
method(step).call
end
def step1
@cut.stub(:foo).and_return("bar")
end
def step2
step1
@cut.stub(:foo_bar).and_return("baz_baz")
end
end
class Cut # Class Under Test
def foo
"foo"
end
def foo_bar
"foo_bar"
end
end
describe "multiple steps" do
before(:each) do
@multi_stepper = MultiStep.new(self)
@cut = Cut.new
end
it "should setup step1" do
@multi_stepper.setup(:step1)
@cut.foo.should == "bar"
@cut.foo_bar.should == "foo_bar"
end
it "should setup step2" do
@multi_stepper.setup(:step2)
@cut.foo.should == "bar"
@cut.foo_bar.should == "baz_baz"
end
end
Certainly too late for OP, but this could be handy for others - the rspec-steps gem seems to be built for this exact situation: https://github.com/LRDesign/rspec-steps
It might be worthwhile to look at https://github.com/railsware/rspec-example_steps and https://github.com/jimweirich/rspec-given as well. I settled on rspec-steps, but I was in a rush and these other options might actually be better for all I know.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With