Skip to content

Instantly share code, notes, and snippets.

@justinko
Created October 9, 2011 07:42
Show Gist options
  • Save justinko/1273411 to your computer and use it in GitHub Desktop.
Save justinko/1273411 to your computer and use it in GitHub Desktop.
# In request/integration/acceptance specs, the more examples you have, the slower the spec.
# This obviously applies to any other kind of spec, but the performance trade off
# is not worth it for high level tests - it can potentially cost you seconds.
# Hard to read/process but fast
describe 'viewing a user profile' do
before { login }
it 'displays the name, birthdate, active, address, phone number, last login'
end
# Descriptive but much, much slower
describe 'viewing a user profile' do
before { login }
it 'displays the name'
it 'displays the birthdate'
it 'displays the active status'
it 'displays the address'
it 'displays the phone number'
it 'displays the last login'
end
# (this is the proposal)
# Balanced - descriptive and fast (this would also reflect doc formatting)
# Downside: each description is not directly tied to an expectation
describe 'viewing a user profile' do
before { login }
it 'displays the name',
'displays the birthdate',
'displays the active status',
'displays the address',
'displays the phone number',
'displays the last login'
end
end
# Highest level (too high level IMO)
describe 'viewing a user profile' do
before { login }
it "displays the user's info"
end
@coreyhaines
Copy link

I would prefer to see this split into more isolated examples.
controller spec -> it "adds a user" # this calls something like ManagesUsers.create_new_user(params)
then, something like

ManagesUsers spec "creating a new user" ->
it "creates the user"
it "sends an email to the user"
it "notifies the admin"

This is always going to be faster, as you can test ManagesUsers outside of loading rails.

If you are looking at these are larger, full-stack example (a la scenarios), then I wouldn't have something like "sends an email to the user" instead relying on a scenario that does something with that email. Why does the user get an email? Is there something they do with it?

@justinko
Copy link
Author

justinko commented Oct 9, 2011

@coreyhaines - You're right, examples are bad. Updated (with some other things).

@coreyhaines
Copy link

Is the proposal to support multiple example description?

I actually like the version you have that is labeled "too high level"
I'm not sure I see the point of having all the "shows name, shows email, etc." Is there a value from having that in the description in this case? What would you use that information for in your docs?

@justinko
Copy link
Author

justinko commented Oct 9, 2011

Is the proposal to support multiple example description?

Yes.

I'm not sure I see the point of having all the "shows name, shows email, etc." Is there a value from having that in the description in this case?

First, I don't call these "acceptance" specs. There's no stakeholder involved "accepting" them. These are used, in my case, to ensure everything works together properly.

Second, without "shows name, shows email" in the description, the viewer must look at the expectations within the example to see exactly what "shows the user's info" means. What exactly is shown? I believe using the example description as the communication tool, rather than the expectations, is much better. We do this for unit level specs, because the performance cost isn't great (in most cases).

@coreyhaines
Copy link

Also, worth noting that I don't write these kind of specs in rspec (I use rspec for controller/model/other object specs), so my opinion might not be the most useful for you.

However, these seem close to cucumber scenarios, so I'm using those as a guideline for my comments.

@coreyhaines
Copy link

Should the viewer (could you define who the viewer is and in what cases?) need to know the details? If you are looking at them as regression tests (ensuring everything works properly), then the failed exception should say what is missing "Expected to see 'Corey Haines' but didn't" So, the user wouldn't need to look at the example description, the failure contains it.

When you say unit, are you referring to your tests for the model a la rails naming conventions? Are the performance costs that much higher for full-stack (a la request specs) vs model tests?

@justinko
Copy link
Author

justinko commented Oct 9, 2011

The "Cucumber Way" is the "highest level". At least this is what is pushed by Aslak and Matt Wynne. It is also the primary reason web_steps.rb was removed from cucumber-rails. Anything lower can potentially be brittle and unnecessary.

@coreyhaines
Copy link

So, what value do you get from having the individual details in the example description, especially from a verification point of view. If you keep the "shows user info" you should get a test failure like

"Shows user info" failed
Expected to see "Corey Haines" on page

or something like that. As opposed to:

'displays the name', 'displays the birthdate', 'displays the active status', 'displays the address', 'displays the phone number', 'displays the last login' Failed
Expected to see "Corey Haines"

When I'm looking at my example descriptions form a regression point of view, I to think how the failure message will look and the load required to parse and figure out what failed.

My thought in this case with multiple descriptions would be to look at the goals and point of the example description. If I find that you need to have a bunch listed there, I ask if my example makes sense.

Personally, while I wouldn't write it that way, I don't see too big of a different between line 9 and line 30. In fact, I think I like line 9 a bit better.

@justinko
Copy link
Author

justinko commented Oct 9, 2011

Should the viewer (could you define who the viewer is and in what cases?) need to know the details? If you are looking at them as regression tests (ensuring everything works properly), then the failed exception should say what is missing "Expected to see 'Corey Haines' but didn't" So, the user wouldn't need to look at the example description, the failure contains it.

The viewer is myself and the other developers. Relying on the expectations to let me know what should be displayed has more mental overhead than viewing an example description. I don't care if the name to be shown should be "Corey Haines", I just want to know that a name expects to be shown.

When you say unit, are you referring to your tests for the model a la rails naming conventions?

Yes, I'm referring to the models. I know the Rails definition of unit is not correct :)

Are the performance costs that much higher for full-stack (a la request specs) vs model tests?

Absolutely. Using capybara with capybara-webkit/selenium......

@coreyhaines
Copy link

It could be argued that your choice of description is a matter of style. I prefer regression uses to highlight based on a failure, not on the example description.

Also, as I mentioned, I don't work this way, so my opinion is based on a pretty different style of development.

In the end, though, I'd put a thumbs-down for multiple description strings. It feels like a workaround to feel like you aren't writing too much in your description (line 9 should indicate perhaps this isn't a great description). When I find my example strings too long with lots of commas, I start to question why I'm doing this. In your case, it is for performance, so worth noting you are doing that.

What about something like

it %q| it displays
name
birthdate
active status
| do
...
end

@justinko
Copy link
Author

justinko commented Oct 9, 2011

What about something like
it %q| it displays

That will be displayed as one line when the spec is run with the doc formatter option. But that is basically what I'm doing now.

My entire point really has nothing to do with high level specs. It has to do with the fact that there is a disconnect between great example descriptions and performance. The project at work uses Solr, so I'm running into the same situation with some of my model specs as well (that use Solr). I'm very hesitant, or hold back, to make an example per expectation for the sake of bad performance. I cannot be the only one experiencing this.

@solnic
Copy link

solnic commented Oct 9, 2011

@justinko from a user pov I'd love to be able to visit a page in a before(:all) and then be able to use multiple examples to describe expected behavior of the visited page. I'm not sure how difficult it would be to make it possible (I'm not familiar with rspec internals). Since you came up with this idea I presume what I suggest is not trivial...It is an overkill in case of request or feature specs to call visit before every example :(

I recently stopped writing request specs in favor of feature/scenario where I put all of the expectations in one scenario block. This works fast but the downside is that I'm forced to write long feature descriptions since all the expectations inside scenario block don't have any description. Also failure messages are often not so meaningful (but that's probably more related to capybara, not rspec).

@dchelimsky
Copy link

@solnic - every time somebody puts the When (in Given/When/Then) in a before hook, a kitten dies. I appreciate the desire to do a When once and expect different Thens, but I think before hooks are the wrong solution. I think that jasmine handles this nicely by supporting multiple failing Whens per-example, and it's something I've thought of adding to rspec. I don't think, however, that this solves the issue @justinko is raising here.

@dchelimsky
Copy link

That will be displayed as one line when the spec is run with the doc formatter option.

@justinko - that's not what I see.

describe "user profile" do 
  it %q|displays
    name
    birthdate
    active status| do
  end 
end 

Output:

user profile
  displays
    name
    birthdate
    active status

@dchelimsky
Copy link

@justinko - actually I think multiple failures per example would solve the issue, combined with the example in my last comment. WDYT?

@solnic
Copy link

solnic commented Oct 9, 2011

@dchelimsky I was talking about being able to do this:

describe 'home page' do
  before(:all) { visit home_page_path }

  it 'has menu'
  it' has header'
  it 'has footer'
end

Currently it doesn't work because I need to call visit in before(:each) which will trigger 3 requests per each it block. This is a performance killer and in case of a request spec I don't see any value in being forced to use before(:each). I do understand why it's a great practice in unit specs but we're talking about higher-level full-stack acceptance specs here.

So yeah, I put my 'When' in a before hook and I'm surprised that I'm killing kittens by doing so. Where should I put visit call if not in a before block?

@justinko
Copy link
Author

justinko commented Oct 9, 2011

@dchelimsky - Shit, okay I was wrong about it being on one line. However, in order for it to be nested correctly (as in your example), you need to have the correct amount of white space. So basically you'd have to keep that hard-to-read it block, which doesn't reflect the formatting output :(

@justinko
Copy link
Author

justinko commented Oct 9, 2011

@dchelimsky - Okay, this is kind of crazy...

describe 'the user profile' do
  before { expensive_operation }

  group 'displays the user info' do
    specify 'name'
    specify 'birthdate'
  end
end

group holds the examples, and runs them in its context (which has the before block). Its description is also used.

@dchelimsky
Copy link

@solnik - right now it's your best option. It's rspec's lack of a better option that is forcing the kitten-killing, which is why I think either multiple-failed-expectations-per-example would be a better solution. If you care about documenting the outcomes, but don't mind multiple expectations per example, then formatting the docstring as in my example above would work as well.

@justinko - the HERE doc formatting problem solvable. Ideally, we'd be able to do something like:

describe "user profile" do 
  it %A{displays
          birthdate
          active status} do
    user = User.new(
      :birthdate => Date.new(1963, 11, 26),
      :active => true
    )
    login_as user
    visit my_profile_path
    page.should contain("Active")
    page.should contain("Nov 26, 1963")
  end 
end 

... where A means HERE doc aligned to the first column after the delimiter. But in absence of such in Ruby, I wonder if we could just handle that in rspec without tripling the time it takes to present the doc format?

re: the group idea, one thing I definitely don't want to do is add more DSL that abstracts things further than they are already.

@justinko
Copy link
Author

justinko commented Oct 9, 2011

@dchelimsky - Comma separated example descriptions would give us complete control of formatting. And plays more nice with editors. Metadata would have to be symbols.

However, do you have this problem that I'm talking about? I would only want to add something to RSpec if others are experiencing this pain.

@dchelimsky
Copy link

@justinko - I've seen a lot of complaints about this issue, though generally related to AR fixtures and/or factory-created models (i.e. create the model instance in before(:all) and have it not roll back until after(:all)). So I believe the pain is widely experienced.

The comma separated descriptions is interesting, but adding it could do odd things to existing suites as multiple args are currently concatenated so you can do things like describe Foo, "#bar" do. That's legacy, and I'd have no problem breaking that in a 3.0 release, but no sooner.

@justinko
Copy link
Author

justinko commented Oct 9, 2011

@dchelimsky - Yeah, the comma separated strings don't really solve anything or offer much to be honest. They need to be tied to an individual expectation or it's just not worth it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment