Im using decorator objects in a Rails app so that I dont have to do nil? checks in views. Everything is working nicely and its easy to test, but Im suspicious that my test:code ratio is a little high. Am I being too verbose? Is there a better way this could be done?Test suite:# spec/decorators/client_decorator_spec.rbrequire_relative ../../app/decorators/client_decoratordescribe ClientDecorator do  describe .position do    context with a recorded client position do      it returns the name of the position do        occupation = double(Occupation, name: CEO)        client = double(Client, occupation: occupation)        expect(ClientDecorator.new(client).position).to eq CEO      end    end    context with no recorded position do      it returns an empty string do        client = double(Client, occupation: nil)        expect(ClientDecorator.new(client).position).to eq       end    end  endendProduction code:# app/decorators/client_decorator.rbrequire simple_decoratorclass ClientDecorator < SimpleDecorator  def position    return  unless occupation    occupation.name  endend
Testing a simple guard clause in isolation
ruby;ruby on rails;unit testing
Yeah, the code/test ratio is probably a little high - especially if you have to do this for many attributes.May I direct your attention to the #try method? Seems to me you that you might be able to skip a lot of code (and tests) by doing things like @client.try(:occupation).try(:name).#try simply returns nil if the receiver doesn't respond to the whatever message you're sending. And since #try is also implemented on the nil object, you can chain tries together.In you views, writing something like <%= @client.try(:occupation).try(:name) %> will just print nothing if the try-chain ends up returning nil - same as your current setup. And if you want a different fallback, you can do this:<%= @client.try(:occupation).try(:name) || N/A %>In other words, having an entire decorator seems a bit much just to absorb nils. But granted, the view ends up having to dig a bit further into the models.You can of course also keep the decorator (at least so you current code doesn't break elsewhere), but simply rewrite it using #try to something like# app/decorators/client_decorator.rbrequire simple_decoratorclass ClientDecorator < SimpleDecorator  def position    occupation.try(:name) ||   endendwhich I honestly don't know if I'd bother to test very rigorously (lest it breaks horribly).