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

Russell Tracey
2007-07-29T21:09:20Z

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