[rspec-users] Isolating rails model specs from their implementation

David Chelimsky
2007-07-29T21:45:01Z

On 7/29/07, Russell Tracey <russell.tracey at gmail.com>
wrote:
> On 29/07/07, David Chelimsky <dchelimsky at gmail.com> wrote:
> > On 7/29/07, Russell Tracey <russell.tracey at gmail.com> wrote:
> > > I''m currently taking a Rails project management app I
built when
> > > learning Rails and adding specs to it. During the course of
building
> > > the app the requirement that project should be archiveable was
added.
> > > So a project is in one of two states active or archived.
> > >
> > > This led to the creation of the following methods:
> > >
> > > Project.active_projects
> > > Project.archived_projects
> > >
> > > @project.active?
> > > @project.archived?
> > > @project.archive!
> > > @project.unarchive!
> > >
> > > The current implementation of this is using a separate table of
> > > "visibilities" as follows:
> > >
> > > # Implementation 1 (Current)
> > >
> > > Tables:
> > >
> > >   Project
> > >     id name           visibility_status_id
> > >     1  ActiveProject                        1
> > >     1  ArchivedProject                     2
> > >
> > >   VisibilityStatuses
> > >     id name
> > >     1  Live
> > >     2  Archived
> > >
> > > But the same behavior could be implemented using a datetime
column as follows:
> > >
> > > # Implementation 2
> > >
> > > Tables:
> > >
> > >   Project
> > >     id name            archived_at
> > >     1  ActiveProject   null
> > >     1  ArchivedProject 2007-07-29:18:57
> > >
> > > Or in fact numerous other ways e.g.
> > >
> > > # Implementation 3
> > >
> > > Army of cows:
> > >
> > > Each cow represents a project, the cows wear one of two hats
> > > to indicate the active/archived status of the project they
represent.
> > >
> > > ...and so on.
> > >
> > > It''s my understanding that model specs (and specs in
general) should
> > > be shielded from the implementation details, so how do i check
that
> > > Project.active_projects only returns active projects without
looking
> > > at assuming something about the implementation? My initial
thought is
> > > to check each of them using one of the other exposed methods
above, in
> > > this case...
> > >
> > >   Project.active_projects.all? {|p| p.active? }
> > >
> > > but then i can''t work out how to spec all the other
methods without
> > > going round in circles so that each spec would end up assuming
that
> > > the other methods work (in this case that p.active? is working)
or
> > > worse resorting to peeking at implementation details.
> >
> > Keep in mind that back-filling examples to existing code is a very
> > different process from writing the examples first, which is the
> > situation for which RSpec is intended. In that case, you might start
> > with one example like this:
> >
> > describe Project do
> >   it " should not be active by default" do
> >     project = Project.create
> >     project.should_not be_active
> >   end
> > end
> >
> > Then the next example might be that when you activate it should be
active:
> >
> > describe Project do
> >   it " should not be active by default" { ... }
> >
> >   it "should be active after you activate it" do
> >     project = Project.create
> >     project.activate!
> >     project.should be_active
> >   end
> >
> >   it "should show up in the list of active projects when
activated" do
> >     project = Project.create
> >     project.activate!
> >     Project.active_projects.should include(project)
> >   end
> > end
> >
> > etc.
> >
> > In this second pair of examples, we never "test" the
activate! method
> > in terms of looking at its internal effects (i.e. that it changes
> > something in the database), but rather through the difference in the
> > way the object behaves after having called the activate! method.
> >
> > Make sense?
> >
> > David
> > _______________________________________________
> > rspec-users mailing list
> > rspec-users at rubyforge.org
> > http://rubyforge.org/mailman/listinfo/rspec-users
> >
>
> Yes this is making more sense now, the only remaining thing i am
> wondering is, given the above examples, would specing that
> active?/archived? be possible without resorting to implementation
> details that Kyle wrote about. If i''m reading them correctly the
> examples above spec out the behavior of activate!, active_projects and
> the default state of a project after creation, but don''t define
the
> behavior of active?
>
> In this app the projects are actually "active" by default before
being
> archived at some point later. So they are only ever in one of two
> states, active or archived.
>
> I''m thinking something like this
>
> describe Project do
>   ...
>   it " Not sure what this would be called?" do
>    project = Project.create
>    project.should be_active
>
>    project.archive!
>
>    project.should_not be_active
>  end
> end

Changing state between expectations in one example is a TDD no-no. The
reason is that if "project.should be_active" fails, you might do
something to fix it and then find that "project.should_not be_active"
fails. If you have them in separate examples then the fact that only
one fails and not the other gives you better feedback:

describe Project do
  it "should be active when first created" do
    project = Project.create
    project.should be_active
  end

  it "should not be active after it is archived" do
    project = Project.create
    project.archive!
    project.should_not be_active
  end
end

> _______________________________________________
> rspec-users mailing list
> rspec-users at rubyforge.org
> http://rubyforge.org/mailman/listinfo/rspec-users
>