title: RSpec for validating generated object or testing functions?
body: <p>Below is one of the first RSpec tests I've written, and after implementing it, a few questions were left open to me. </p>
  
  <p>The tests run the same method <code>calculate</code> with different inputs, and validate the generated object. However, the individual small methods, like <code>day_off?</code> are not explicitly tested. </p>
  
  <p>Should I write tests for the individual methods instead of validating the generated object? Or is it enough to just validate the output? Or should actually both the individual methods and the output be tested?</p>
  
  <p>The typical answer I suppose is "it depends", but depends on what?</p>
  
  <p>I really hope that this won't be closed as a subjective question, because I think there could actually be a useful answer to this. </p>
  
  <p>The complete code can be found at: <a href="https://gist.github.com/asmand/d1ccbcd01789353c01c3" rel="nofollow">https://gist.github.com/asmand/d1ccbcd01789353c01c3</a></p>
  
  <p>Here is the class to be tested:</p>
  
  <pre><code>class WeeklyFlexCalculator
  
    attr_reader :params
  
    def initialize(params)
      @params = params
    end
  
    def calculate
      group_efforts(
        (params.start_date..params.end_date).map do |date|
          daily_effort(date)
        end.compact
      )
    end
  
    def group_efforts(result)
      weekly = result.group_by { |e| get_week_key(e[:date]) }
      weekly.map do |key,w|
        {
          year: get_year_from_week_key(key),
          week: get_week_from_week_key(key),
          weekTarget: get_target_sum(w),
          weekEffort: get_effort_sum(w),
          efforts: w
        }
      end.sort { |a, b| b.efforts[0].date &lt;=&gt; a.efforts[0].date }
    end
  
    def daily_effort(date)
        target = get_target(date)
        effort = get_effort(date)
        return if target == 0 &amp;&amp; effort == 0
        {
          date: date,
          effort: effort,
          target: target,
          diff: effort - target
        }
    end
  
    def get_target_sum(efforts)
      efforts.inject(0){|sum,e| sum + e[:target]}
    end
  
    def get_effort_sum(efforts)
      efforts.inject(0){|sum,e| sum + e[:effort]}
    end
  
    def get_week_key(date)
      date.cwyear.to_s + "|" + date.cweek.to_s
    end
  
    def get_year_from_week_key(key)
      key.split('|')[0]
    end
  
    def get_week_from_week_key(key)
      key.split('|')[1]
    end
  
    def get_target(date)
      day_off?(date) ? 0 : params.user.hours_per_day
    end
  
    def get_effort(date)
      ts = get_timesheet(date)
      ts.nil? ? 0.0 : ts.TimeInHours
    end
  
    def day_off?(date)
      date.wday == 0 or date.wday == 6 or params.holidays.include? date.to_s
    end
  
    def get_timesheet(date)
      params.timesheets.select {|ts| ts.Date == date.to_s}.first
    end
  end
  </code></pre>
  
  <p>And here is the test: </p>
  
  <pre><code>require './WeeklyFlexCalculator'
  
  describe WeeklyFlexCalculator, "during Christmas week" do
  
    subject(:calculation) { WeeklyFlexCalculator.new(params).calculate }
  
    let(:params) do
      messages = {
        :start_date =&gt; Date.new(2013,12,23),
        :end_date =&gt; Date.new(2013,12,29),
        :holidays =&gt; ["2013-12-24", "2013-12-25", "2013-12-26"],
        :timesheets =&gt; [],
        :user =&gt; user
      }
      double(:params,messages)
    end
  
    let(:user) do
      messages = {
        :hours_per_day =&gt; 7.5
      }
      double(:user, messages)
    end
  
    let(:timesheet) do
      messages = {
        :Date =&gt; Date.new(2013,12,23).to_s,
        :TimeInHours =&gt; 5.0
      }
      double(:timesheet, messages)
    end
  
    context "with no work performed" do
      it { should have(1).item }
  
      context "the week calculated" do
        subject(:workweek) {calculation[0]}
  
        its([:year]) { should eq "2013" }
        its([:week]) { should eq "52" }
        its([:weekTarget]) { should eq 15.0 }
        its([:weekEffort]) { should eq 0.0 }
  
        context "the work efforts" do
          subject(:efforts) {workweek[:efforts]}
  
          it { should have(2).items }
  
          context "the first work effort" do
            subject(:effort) {efforts[0]}
  
            its([:target]) {should eq 7.5}
            its([:diff]) {should eq -7.5}
            its([:effort]) {should eq 0.0}
          end
        end
      end
    end
  
    context "with work effort on normal day" do
      before do
        params.stub(:timesheets =&gt; [timesheet])
      end
  
      it { should have(1).item }
  
      context "the week calculated" do
        subject(:workweek) {calculation[0]}
  
        its([:year]) { should eq "2013" }
        its([:week]) { should eq "52" }
        its([:weekTarget]) { should eq 15.0 }
        its([:weekEffort]) { should eq 5.0 }
  
        context "the work efforts" do
          subject(:efforts) {workweek[:efforts]}
  
          it { should have(2).items }
  
          context "the first work effort" do
            subject(:effort) {efforts[0]}
  
            its ([:effort]) { should eq 5.0 }
            its ([:diff]) { should eq -2.5 }
          end
        end
      end
    end
  
    context "with work effort on a holiday" do
      before do
        timesheet.stub(:Date =&gt; Date.new(2013,12,24).to_s)
        params.stub(:timesheets =&gt; [timesheet])
      end
  
      it { should have(1).item }
  
      context "the week calculated" do
        subject(:workweek) {calculation[0]}
  
        its([:year]) { should eq "2013" }
        its([:week]) { should eq "52" }
        its([:weekTarget]) { should eq 15.0 }
        its([:weekEffort]) { should eq 5.0 }
  
        context "the work efforts" do
          subject(:efforts) {workweek[:efforts]}
  
          it { should have(3).items }
  
          context "the first work effort" do
            subject(:effort) {efforts[0]}
  
            its ([:effort]) { should eq 0.0 }
            its ([:diff]) { should eq -7.5 }
          end
  
          context "the second work effort" do
            subject(:effort) {efforts[1]}
  
            its ([:effort]) {should eq 5.0}
            its ([:diff]) { should eq 5.0}
          end
        end
      end
    end
  end
  </code></pre>