24

I was just wondering how others organise large spec files (especially for models) with many contexts and sections organised in describe blocks for validations and other specs that can be grouped in some meaningful way.

Do you guys keep all the specs concerning a model in the same spec file for that model, or do you split into modules in a way or another?

I have never cared too much about this so far but I am wondering what others do, as there doesn't seem to be some sort of agreement around a best practice or such.

I've got some pretty large spec files for some models that I'd like to organise into smaller files, and there is little to no functionality shared across different models so I am not sure whether shared examples would be the way to go about this (regardless reusability) or whether there is some better way. Any suggestions?

Thanks in advance.

Vito Botta
  • 395
  • 3
  • 10
  • I usually keep everything in a single file and fold/unfold blocks to navigate the code a bit better. It's a pain in huge files but I never fought my laziness to separate tests in class/instance methods, callbacks/validations etc... – apneadiving Feb 10 '12 at 13:16
  • I guess you could just use include and mixin each file into the base that way. Organize the files by class/subclass in the directory structure as is convention. I think this is what i'll do, coupled with David's answer below. Thanks for the killer question! – ipd Sep 28 '12 at 17:00

1 Answers1

23

Nested contexts can help you here, but keep it shallow (typically one level deep). There are two variables to consider in each example: givens (starting state) and what method is being invoked. You can group things by method or state:

# by method
describe Stack do
  describe "#push" do
    it "adds an element to an empty stack"
    it "adds an element to a non-empty stack"
  end

  describe "#pop" do
    it "returns nil from an empty stack"
    it "returns the last element of a non-empty stack"
    it "removes the last element from a non-empty stack"
  end
end

# by state
describe Stack do
  context "when empty" do
    specify "push adds an element"
    specify "pop returns nil"
  end

  context "when not empty" do
    specify "push adds an element"
    specify "pop returns last element"
    specify "pop removes last element"
  end
end

I've used both approaches and seen them both work really well and really badly. The key to either approach is that the examples tell a story as you read from top to bottom. As requirements evolve, this means you need to review this file, just as you do your implementation code. An easy way to check that the spec makes sense is to run it with the documentation formatter:

rspec stack_spec.rb --format documentation

This spits out all the names in order (provided you're not using --order rand):

Stack
  #push
    adds an element to an empty stack
    adds an element to a non-empty stack
  #pop
    returns nil from an empty stack
    returns the last element of a non-empty stack
    removes the last element from a non-empty stack

or

Stack
  when empty
    push adds an element
    pop returns nil
  when not empty
    push adds an element
    pop returns last element
    pop removes last element

Once you see this output, it'll be pretty clear to you whether the organization you're using makes sense or not.

David Chelimsky
  • 8,920
  • 2
  • 38
  • 30