Logic Branches Fundamental
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:
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.
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.
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.
Although we still need two tests for the redirect_back_or_to method, we can test the action like this:
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.
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.
Posted on 2007-07-20 | permalink | del.icio.us
Blog Archive
