Explicit intent
Tell me you are used to explicit your intent every time you write code, and probably I’m talking to a very skilled programmer.
Explicit intent is a quite simple concept.
Great news. Despite the simplicity, there are huge advantages on bringing it to our daily work.
When the intent of code is explicit, future maintenance of this code is kept at a very healthy level.
You know, things can be bad when we are working with poor code.
Lack of explicitness is probably one of the biggest clues indicating your code is in trouble.
Sadly I’m not sure this is something every good programmer is aware.
So I’ve decided to rant about it.
Let’s see how easy can be identifying lack of explicit intent.
Here is some testing code extracted from spree, an open-source e-commerce solution that seems to have a solid code base.
Hopefully you are used to write testing code and you know it should receive the same care as your production code.
require File.dirname(__FILE__) + '/../spec_helper.rb'
describe Payment do
before(:each) do
@order = Order.new
@order.checkout_complete = true
@order.stub!(:total).and_return(100)
@payment = CreditcardPayment.new(:order => @order)
end
describe "save hook" do
it "should mark order as paid if payment_total = total" do
@order.stub!(:payment_total).and_return(100)
@order.should_receive(:pay!)
@payment.save
end
it "should mark order as paid if payment_total > total" do
@order.stub!(:payment_total).and_return(101)
@order.should_receive(:pay!)
@payment.save
end
it "should not mark order as paid if payment_total < total" do
@order.stub!(:payment_total).and_return(99)
@order.should_not_receive(:pay!)
@payment.save
end
end
end
Testing code wrote on top of rspec tends to be very expressive, being the intents of what is happening pretty explicit.
You can see it at this code.
But I have an issue with the setup code.
It failed in telling us what is happening at a first moment:
before(:each) do
@order = Order.new
@order.checkout_complete = true
@order.stub!(:total).and_return(100)
@payment = CreditcardPayment.new(:order => @order)
end
When looking for a good understanding of what is going on, we are enforced to follow the code at a low level.
And if this kind of issue propagate at the code base, programming productivity and focus may be affected.
I told you explicit intent is a quite simple concept.
It is also pretty easy to be applied:
require File.dirname(__FILE__) + '/../spec_helper.rb'
describe Payment do
before(:each) do
create_and_checkout_a_given_order
end
def create_and_checkout_a_given_order
@order = Order.new
@order.checkout_complete = true
@order.stub!(:total).and_return(100)
@payment = CreditcardPayment.new(:order => @order)
end
describe "save hook" do
it "should mark order as paid if payment_total = total" do
@order.stub!(:payment_total).and_return(100)
@order.should_receive(:pay!)
@payment.save
end
it "should mark order as paid if payment_total > total" do
@order.stub!(:payment_total).and_return(101)
@order.should_receive(:pay!)
@payment.save
end
it "should not mark order as paid if payment_total < total" do
@order.stub!(:payment_total).and_return(99)
@order.should_not_receive(:pay!)
@payment.save
end
end
end
What the heck? Giving a name to that piece of code is all we were required to do?
Yes, and now we can read this code without worrying about what is happening at some anonymous and mysterious piece of code.
Giving meaningful names to your code is a very powerful technique.
Having deserved a whole chapter at Clean Code, by Robert Martin.
So, when you understand exactly what is happening at some code you just get in touch, a very skilled programmer should be behind it.
Related posts:







Rafael –
I don’t think that your suggested refactoring provides the clarity that you were looking for in the original sample you provided. After the refactoring, we’re still left with the original issue – by encapsulating the order’s total inside the helper method it is hidden from each of the specs. I think a better approach would be to just create a more meaningful describe block and embed the setup code there instead of bothering with a separate method as a label for your setup state. After all, that’s what before blocks are for.
In keeping with the theme of explicit intent, I tweaked your example so that I could provide a meaningful setup state in each of the specs:
describe CreditcardPayment do
def initialize_payment_for_completed_order(order_options)
total = options[:total]
payment_total = options[:payment_total]
order = Order.new
order.checkout_complete = true
order.stub!(:total).and_return(total)
order.stub!(:payment_total).and_return(payment_total)
payment = CreditcardPayment.new(:order => order)
payment, order
end
describe "when saving" do
it "should mark the order as paid if payment_total = total" do
payment, order = initialize_payment_for_completed_order(:total => 100, :payment_total => 100)
order.should_receive(:pay!)
payment.save
end
it "should mark order as paid if payment_total > total" do
payment, order = initialize_payment_for_completed_order(:total => 100, :payment_total => 101)
order.should_recieve(:pay!)
payment.save
end
it "should not mark order as paid if payment_total 100, :payment_total => 99)
order.should_not_receive(:pay!)
payment.save
end
end
end
I’ts the difference in :total and :payment_total that are important in each of these specs, so they should remain where they present the most clarity. I still don’t really like this, but I think the awkwardness of the return values from my helper method hint at a design problem (which is what specs are for!). I would refactor the implementation code to have the Order delegate to the CreditcardPayment class, and then have Order interrogate its associated payments and manage its own state.
Patrick Reagan
21 Sep 09 at 16:03
Patrick,
Thx for your reply.
Your code seems pretty good, you hid the mockering noise in a nice way.
Despite the provided sample, my purpose was to talk about the importance of clarifying intent while we are writing code.
And it seems that, like me, you take this technique seriously.
rafanoronha
21 Sep 09 at 16:37