Having as few branches of logic as possible is a development fundamental, but forgetting to take advantage of it is easy. Here is an example.
In Rails you can redirect back to the page the user came from like this:
def action
redirect_to :back
end
However, if the user came to the page directly and does not have an HTTP_REFERER header, Rails will raise a RedirectBackError. The easiest way to prevent this exception is to check for a referer before redirecting back.
def action
redirect_to request.env['HTTP_REFERER'] ? :back : some_default_url
end
Although checking the request header adds a little noise to this method, it solves the problem of receiving the exception if the referer header is missing. Now, let's look at testing this method. How many tests do we have to write for this one line? Two. We need a test to make sure the method redirects back if the referer is present, and we need a test to make sure the method redirects to some_default_url is the referer is missing.
def test_action_redirects_back_if_referer_is_present
@request.env['HTTP_REFERER'] = "some url"
get :action
assert_redirected_to :back
end
def test_action_redirects_to_some_default_url_if_referer_is_missing
get :action
assert_redirected_to some_default_url
end
To reduce this to a single test we need to change the method to have a single branch of logic. We can do this by creating a method to handle the referer checking.
def action
redirect_back_or_to some_default_url
end
protected
def redirect_back_to_to(url)
redirect_to request.env['HTTP_REFERER'] ? :back : url
end
Although we still need two tests for the redirect_back_or_to method, we can test the action like this:
def test_action_redirects_back_or_to_some_default_url
@controller.expects(:redirect_back_or_to).with(some_default_url)
get :action
end
In addition to reducing the number of tests we need from two to one, pulling out the redirect_back_or_to method keeps our code DRY for anytime we want to avoid a RedirectBackError.