The DRY principle states that "each piece of knowledge must have one, unambiguous, authoritative representation in the system." What you do is much more about saving a few characters here and there than keeping things DRY, and the result is a tangled network of dependencies up and down the hierarchy, which, as you can see, is a bitch to do what you want it and therefore fragile and fragile.
To begin with, you wrote out in such a way that verbose and works:
describe MyController do describe "GET #show" do context "published item" do it "redirects to the success url" do item = Factory(:item, published: true) get :show, :id => item.id response.should redirect_to success_url end end context "unpublished item" do it "redirects to the error url" do item = Factory(:item, published: false) get :show, :id => item.id response.should redirect_to error_url end end end end
Now, the only "pieces of knowledge" that are duplicated are the names of examples that can be generated by helpers at the end of each example. This can be resolved in a readable way using the example
method, which is an alias of it
:
describe MyController do describe "GET #show" do context "published item" do example do item = Factory(:item, published: true) get :show, :id => item.id response.should redirect_to success_url end end context "unpublished item" do example do item = Factory(:item, published: false) get :show, :id => item.id response.should redirect_to error_url end end end end
There. DRY. And quite readable and easy to change. Now that you add more examples for any of the contexts, you can add let
:
describe MyController do describe "GET #show" do context "published item" do let(:item) { Factory(:item, published: true) } example do get :show, :id => item.id response.should redirect_to success_url end example do
Now the only duplicated code (not the same as the DRY principle) is get
. If you are really determined to do this, you can delegate these calls to a method, such as get_show(id)
or some such method, but in reality it doesn’t buy a lot. It's not like the API for get
will change from under you, and the only argument to get
is the item
identifier, which you really like in this example (so there is no unnecessary information).
As for using subject
to capture the response and exit one transaction from the same line, it just makes things very difficult to read and does not save you. In fact, I decided to use subject
in this way as a smell .
Hope this helps.
Cheers, David