[rspec-users] spec-ing private methods?

41 views
Skip to first unread message

Joaquin Rivera Padron

unread,
Oct 14, 2009, 3:36:00 PM10/14/09
to rspec-users
hello there,
how do you tipically spec private methods? The thing is Ï have something like this:
 
def some_method
   complex_method + other_complex_methods
end
 
private
def complex_method...
def other_complex_methods ...
 
and the two complex methods can get really tricky to get right, I would like to be able to write specs for them, how do you do that? I mean I cannot do:
 
object.some_private_method
 
am I?
 
thanks in advance,
joaquin

--
www.least-significant-bit.com

Scott Taylor

unread,
Oct 14, 2009, 3:49:25 PM10/14/09
to rspec-users
On Oct 14, 2009, at 3:36 PM, Joaquin Rivera Padron wrote:

hello there,
how do you tipically spec private methods? The thing is Ï have something like this:
 
def some_method
   complex_method + other_complex_methods
end
 
private
def complex_method...
def other_complex_methods ...
 
and the two complex methods can get really tricky to get right, I would like to be able to write specs for them, how do you do that? I mean I cannot do:
 
object.some_private_method

You have a few options:

1. Make the method public in the object you are testing
2. Make the method public in the test case
3. Don't test the method
4. Use __send__ or (send) to call it.
5. Refactor private methods to a new object, and make the methods public in that object.

Most of those options suck (esp. 1, 2, 3, & 4) - usually it represents a design flaw (you are doing too much in your class).

Scott

 
am I?
 
thanks in advance,
joaquin

--
www.least-significant-bit.com
_______________________________________________
rspec-users mailing list
rspec...@rubyforge.org
http://rubyforge.org/mailman/listinfo/rspec-users

Nicolás Sanguinetti

unread,
Oct 14, 2009, 4:24:45 PM10/14/09
to rspec-users
On Wed, Oct 14, 2009 at 5:49 PM, Scott Taylor <sc...@railsnewbie.com> wrote:
>
> On Oct 14, 2009, at 3:36 PM, Joaquin Rivera Padron wrote:
>
> hello there,
> how do you tipically spec private methods? The thing is Ï have something
> like this:
>
> def some_method
>    complex_method + other_complex_methods
> end
>
> private
> def complex_method...
> def other_complex_methods ...
>
> and the two complex methods can get really tricky to get right, I would like
> to be able to write specs for them, how do you do that? I mean I cannot do:
>
> object.some_private_method
>
> You have a few options:
> 1. Make the method public in the object you are testing
> 2. Make the method public in the test case
> 3. Don't test the method
> 4. Use __send__ or (send) to call it.
> 5. Refactor private methods to a new object, and make the methods public in
> that object.
> Most of those options suck (esp. 1, 2, 3, & 4) - usually it represents a
> design flaw (you are doing too much in your class).
> Scott

3 is ok.

A "private" method just means it's an implementation detail that is
outside of your API. If you are aiming to test the behavior of your
API, then you don't care about implementation details.

-foca

Stephen Eley

unread,
Oct 14, 2009, 4:31:00 PM10/14/09
to rspec-users
On Wed, Oct 14, 2009 at 3:49 PM, Scott Taylor <sc...@railsnewbie.com> wrote:
>
> Most of those options suck (esp. 1, 2, 3, & 4) - usually it represents a
> design flaw (you are doing too much in your class).

I disagree that the simple existence of private methods is a sign of a
design flaw. There are plenty of use cases for which private methods
are the simplest and most practical approach. I use them all the time
to help me deconstruct complicated multi-step actions into shorter,
more atomic chunks of logic. They're also how callbacks and filters
are usually implemented in Rails.

That said, though: I usually don't bother testing them. I use RSpec
for unit testing, and when I'm doing that I care about the _external
behavior_ of the unit. I want to know what the object will do when I
poke it from another object. Private methods are an implementation
detail, not a "What does it do?" but rather "How does it do that?" --
and that's not any other class's business.

They're also not usually that complex or brittle. I'll know they work
because the public or protected methods that call them work; and I
don't need tests to understand them. In the exceptional cases where
they _might_ be complex (e.g., some ActiveRecord callbacks or
authentication filters) you're right -- putting them into a module and
testing that module makes sense.

--
Have Fun,
Steve Eley (sfe...@gmail.com)
ESCAPE POD - The Science Fiction Podcast Magazine
http://www.escapepod.org

Scott Taylor

unread,
Oct 14, 2009, 4:34:40 PM10/14/09
to rspec-users

Yeah, 3 is OK, although ideally you *are* testing the method, albeit
indirectly through the public interface.

Scott

Ashley Moran

unread,
Oct 14, 2009, 4:32:43 PM10/14/09
to rspec-users
On 14 Oct 2009, at 20:49, Scott Taylor wrote:
> On Oct 14, 2009, at 3:36 PM, Joaquin Rivera Padron wrote:
>
>> private
>> def complex_method...
>> def other_complex_methods ...
>>
>> and the two complex methods can get really tricky to get right, I
>> would like to be able to write specs for them, how do you do that?
>> I mean I cannot do:
>>
>> object.some_private_method
>
> You have a few options:
>
> 1. Make the method public in the object you are testing
> 2. Make the method public in the test case
> 3. Don't test the method
> 4. Use __send__ or (send) to call it.
> 5. Refactor private methods to a new object, and make the methods
> public in that object.
>
> Most of those options suck (esp. 1, 2, 3, & 4) - usually it
> represents a design flaw (you are doing too much in your class).

I'm with Scott, this usually indicates a design flaw, and 5 is usually
the solution. The clue is in the names you gave them - you shouldn't
have complex methods, especially private ones.

Can you post any of the code so we can see where the complexity/
problem is?

Ashley

--
http://www.patchspace.co.uk/
http://www.linkedin.com/in/ashleymoran
http://aviewfromafar.net/

Ashley Moran

unread,
Oct 14, 2009, 4:47:10 PM10/14/09
to rspec-users

On 14 Oct 2009, at 21:31, Stephen Eley wrote:

> I disagree that the simple existence of private methods is a sign of a
> design flaw. There are plenty of use cases for which private methods
> are the simplest and most practical approach. I use them all the time
> to help me deconstruct complicated multi-step actions into shorter,
> more atomic chunks of logic.
>

> <snip>


>
> They're also not usually that complex or brittle.

I don't think Scott meant to imply that private methods are bad, only
that complex ones private methods are. I use private methods for the
same reason you describe above, to break complex actions down into
more understandable chunks. When it takes 30 private methods before I
can understand the code, that usually indicates there's a problem, and
that a new class needs to emerge. (There are usually other signs of
this too, though.)

Ashley

_______________________________________________

Joaquin Rivera Padron

unread,
Oct 14, 2009, 5:22:04 PM10/14/09
to rspec-users
hello

2009/10/14 Ashley Moran <ashley...@patchspace.co.uk>

On 14 Oct 2009, at 20:49, Scott Taylor wrote:
On Oct 14, 2009, at 3:36 PM, Joaquin Rivera Padron wrote:

private
def complex_method...
def other_complex_methods ...

and the two complex methods can get really tricky to get right, I would like to be able to write specs for them, how do you do that? I mean I cannot do:

object.some_private_method

You have a few options:

1. Make the method public in the object you are testing
2. Make the method public in the test case
3. Don't test the method
4. Use __send__ or (send) to call it.
5. Refactor private methods to a new object, and make the methods public in that object.

Most of those options suck (esp. 1, 2, 3, & 4) - usually it represents a design flaw (you are doing too much in your class).

I'm with Scott, this usually indicates a design flaw, and 5 is usually the solution.  The clue is in the names you gave them - you shouldn't have complex methods, especially private ones.

Can you post any of the code so we can see where the complexity/problem is?

hey ashley,
the code itself is not very interesting, it's some fast hacking I'm doing dumping chess positions into a string, and then the methods given the character index on that string should translate it to two-dimensional vectors in the board, boring: mainly math calculations multiplying columns by some number and adding row numbers and such, and during the spec-ing of the public method the question arose...

and yeah, I think 5 it my favorite at the moment, they don't have to be private anyway, also IMHO testing private methods through the public API it's not in general applicable (only thinking here, no code sample)...

but anyway I wanted to hear what you guys think about it

thanks,
joaquin





--
www.least-significant-bit.com

Matt Wynne

unread,
Oct 14, 2009, 5:33:44 PM10/14/09
to rspec-users

On 14 Oct 2009, at 20:49, Scott Taylor wrote:

> On Oct 14, 2009, at 3:36 PM, Joaquin Rivera Padron wrote:
>
>> hello there,
>> how do you tipically spec private methods? The thing is Ï have
>> something like this:
>

> You have a few options:
>
> 1. Make the method public in the object you are testing
> 2. Make the method public in the test case
> 3. Don't test the method
> 4. Use __send__ or (send) to call it.
> 5. Refactor private methods to a new object, and make the methods
> public in that object.
>
> Most of those options suck (esp. 1, 2, 3, & 4) - usually it
> represents a design flaw (you are doing too much in your class).

Yup. Sprout class[1] works for me, every single time.

[1]http://xunitpatterns.com/Sprout%20Class.html

cheers,
Matt

+447974 430184
ma...@mattwynne.net
http://mattwynne.net

Matt Wynne

unread,
Oct 14, 2009, 5:48:35 PM10/14/09
to Matt Wynne, rspec-users

On 14 Oct 2009, at 22:33, Matt Wynne wrote:

>
> On 14 Oct 2009, at 20:49, Scott Taylor wrote:
>
>> On Oct 14, 2009, at 3:36 PM, Joaquin Rivera Padron wrote:
>>
>>> hello there,
>>> how do you tipically spec private methods? The thing is Ï have
>>> something like this:
>>
>> You have a few options:
>>
>> 1. Make the method public in the object you are testing
>> 2. Make the method public in the test case
>> 3. Don't test the method
>> 4. Use __send__ or (send) to call it.
>> 5. Refactor private methods to a new object, and make the methods
>> public in that object.
>>
>> Most of those options suck (esp. 1, 2, 3, & 4) - usually it
>> represents a design flaw (you are doing too much in your class).
>
> Yup. Sprout class[1] works for me, every single time.
>
> [1]http://xunitpatterns.com/Sprout%20Class.html

By the way, Sprout Class comes from 'Working Effectively with Legacy
Code' (Feathers), which is probably the best book I've read on TDD
(Admittedly I've yet to read The RSpec Book). Highly recommended.

Ashley Moran

unread,
Oct 14, 2009, 7:07:25 PM10/14/09
to rspec-users

On 14 Oct 2009, at 22:48, Matt Wynne wrote:

> By the way, Sprout Class comes from 'Working Effectively with Legacy
> Code' (Feathers), which is probably the best book I've read on TDD
> (Admittedly I've yet to read The RSpec Book). Highly recommended.


I've heard good things about this book too. Do you also recommend
xUnit Patterns?

Bizarrely, I'm including xUnit patterns as a reference in a
presentation on mocks I'm giving tomorrow[1], despite never having
read it. (Only because I know it contains some mocking definitions I
refer to...)

Incidentally, the mocking section of the RSpec Book is very good.
Which is to say I agree with what it says :)

Ashley

[1] http://nwrug.org/events/october09/

_______________________________________________

Zach Dennis

unread,
Oct 14, 2009, 8:06:58 PM10/14/09
to rspec-users
On Wed, Oct 14, 2009 at 7:07 PM, Ashley Moran
<ashley...@patchspace.co.uk> wrote:
>
> On 14 Oct 2009, at 22:48, Matt Wynne wrote:
>
>> By the way, Sprout Class comes from 'Working Effectively with Legacy Code'
>> (Feathers), which is probably the best book I've read on TDD (Admittedly
>> I've yet to read The RSpec Book). Highly recommended.
>
>
> I've heard good things about this book too.  Do you also recommend xUnit
> Patterns?

It's been a while since I've read that book. From what I remember it
had a lot of great information, but there were many sections I don't
find applicable for my coding practices. I think a lot of the typical
xUnit styles of testing are heavily influenced from heavier weight
languages (like Java for instance). I think that is reflected in much
of the material in the book.

I did enjoy reading it over a few month period because of the wealth
of information, but I would consider many of the techniques dated
given where the current state of tools (RSpec and Cucumber), the
flexibility of our ruby, and the philosophy of BDD have put us.

So if you're looking to read it for immediately applicable techniques
I would say don't bother, but if you're an information-whore and want
to gain a wealth of knowledge, and want to see a lot of thinking and
techniques that have influenced a lot of today's tools and thinking,
then read it, for sure, but don't expect to read it in a weekened or
even a week... that would be information overload -- your brain would
explode.

Zach


>
> Bizarrely, I'm including xUnit patterns as a reference in a presentation on
> mocks I'm giving tomorrow[1], despite never having read it.  (Only because I
> know it contains some mocking definitions I refer to...)
>
> Incidentally, the mocking section of the RSpec Book is very good.  Which is
> to say I agree with what it says :)
>
> Ashley
>
> [1] http://nwrug.org/events/october09/
>
> --
> http://www.patchspace.co.uk/
> http://www.linkedin.com/in/ashleymoran
> http://aviewfromafar.net/
>
>
>
>
>
>
>
> _______________________________________________
> rspec-users mailing list
> rspec...@rubyforge.org
> http://rubyforge.org/mailman/listinfo/rspec-users
>

--
Zach Dennis
http://www.continuousthinking.com (personal)
http://www.mutuallyhuman.com (hire me)
http://ideafoundry.info/behavior-driven-development (first rate BDD training)
@zachdennis (twitter)

Andrew Premdas

unread,
Oct 15, 2009, 3:59:52 AM10/15/09
to rspec-users
2009/10/14 Joaquin Rivera Padron <joah...@gmail.com>
_______________________________________________
rspec-users mailing list
rspec...@rubyforge.org
http://rubyforge.org/mailman/listinfo/rspec-users

An interesting standard to apply to your complex methods (well I think its interesting and valuable) is this

    A method can either do one thing or call other methods but not both.
    A method cannot be more than 5 lines long (insert a bigger number if you must ...)
    If a method consists of calls to other methods then the names of these methods should document the method.

If you apply this to refactor your existing code you should end up with a hierarchy of small private methods with leaves that do tiny little tasks and nodes that describe how tasks are done. I find this beneficial in itself (the code is much easier to read) as well as being a good first step to extracting new objects. Nodes in the old object become public methods and their leaves come along as private methods in the new object.

You can take this further and state that no public method should ever do anything, it should just contain calls to private methods which document how the public method works. I expect most would consider that going to far ... sigh

Andrew

Stephen Eley

unread,
Oct 15, 2009, 11:03:59 AM10/15/09
to rspec-users
On Wed, Oct 14, 2009 at 4:47 PM, Ashley Moran
<ashley...@patchspace.co.uk> wrote:
>
> I don't think Scott meant to imply that private methods are bad, only that
> complex ones private methods are.

This is a good point. I was overgeneralizing the response, despite
the question being somewhat more specific. My bad.

(It's a worthwhile discussion, though.)


--
Have Fun,
Steve Eley (sfe...@gmail.com)
ESCAPE POD - The Science Fiction Podcast Magazine
http://www.escapepod.org

Stephen Eley

unread,
Oct 15, 2009, 11:06:55 AM10/15/09
to rspec-users
On Thu, Oct 15, 2009 at 3:59 AM, Andrew Premdas <apre...@gmail.com> wrote:
>
> You can take this further and state that no public method should ever do
> anything, it should just contain calls to private methods which document how
> the public method works. I expect most would consider that going to far ...
> sigh

Once complexity has been managed, what's the value of that strategy?


--
Have Fun,
Steve Eley (sfe...@gmail.com)
ESCAPE POD - The Science Fiction Podcast Magazine
http://www.escapepod.org

Andrew Premdas

unread,
Oct 15, 2009, 3:59:40 PM10/15/09
to rspec-users
2009/10/15 Stephen Eley <sfe...@gmail.com>

On Thu, Oct 15, 2009 at 3:59 AM, Andrew Premdas <apre...@gmail.com> wrote:
>
> You can take this further and state that no public method should ever do
> anything, it should just contain calls to private methods which document how
> the public method works. I expect most would consider that going to far ...
> sigh

Once complexity has been managed, what's the value of that strategy?

I wrote a bit more about this in a blog article some time ago (http://blog.andrew.premdas.org/articles/2009/04/07/writing-classes-why-its-so-easy-to-do-it-wrong-in-ruby). I'm afraid my blog is fundamentally anti-social, and can be a bit flakey on up time so apologies if article is hard to access.

Fundamentally the reason for following this approach is to have code you can come back to and work on easily. If the public methods of a class document essentially the algorithm of how the class fulfils it function, and hide the detail then its much easier to come back to the code and work on it.

    class Car
       def start
         turn_the_key
         pump_fuel_into_engine
         spark
      end

of course that was all well and good in the eighties, but in the late oughties we need to update

    class Car
       def start
         check_the_key_security_code
         boot_the_engine_management
         pump_fuel_into_engine
         spark

Not the greatest example I'm afraid (especially as I don't drive and now nothing about cars), but hopefully you get the idea

All best

Andrew

J. B. Rainsberger

unread,
Oct 15, 2009, 5:30:37 PM10/15/09
to rspec-users
On Wed, Oct 14, 2009 at 15:36, Joaquin Rivera Padron <joah...@gmail.com> wrote:
> hello there,
> how do you tipically spec private methods? The thing is Ï have something
> like this:
>
> def some_method
>    complex_method + other_complex_methods
> end
>
> private
> def complex_method...
> def other_complex_methods ...
>
> and the two complex methods can get really tricky to get right, I would like
> to be able to write specs for them, how do you do that? I mean I cannot do:
>
> object.some_private_method
>
> am I?

Hello, Joaquin.

I typically make these methods public, then test them. I use the
guideline "if it's important enough to test, then it's useful enough
to use somewhere else". I usually end up using the method -- or
something like it -- somewhere else in my code base, then make it
public at that point, anyway.

I have also trusted, for a long time, the idea that if I want to test
it, but it's private, then I really have a small object trying to grow
out of a larger object. I find this especially true when I have three
private methods, all related to each other, in the same class. In that
case I see the clear signal that a smaller object is getting out, so I
let that happen.

Of course, not everyone feels comfortable with so many small objects,
and not everyone feels comfortable making those methods public. If you
don't feel comfortable to do that, then you should look for tricks in
Ruby to let you invoke that private method another way. I think
someone else suggested using __send__() for that. I strongly prefer
not to do that, and when I do, I generally only do it as a first step
towards refactoring the code I'm testing.

Only you can decide what to do, but if you can't decide, then I highly
recommend making the method public, then testing it. If that makes you
dislike the design, then create a new class for the method and move it
there, making it public.

I hope this helps you.
--
J. B. (Joe) Rainsberger :: http://www.jbrains.ca ::
http://blog.thecodewhisperer.com
Diaspar Software Services :: http://www.diasparsoftware.com
Author, JUnit Recipes
2005 Gordon Pask Award for contribution to Agile practice :: Agile
2010: Learn. Practice. Explore.

Zach Dennis

unread,
Oct 15, 2009, 10:42:20 PM10/15/09
to rspec-users
Hello Joaquin,

I might shift my focus from whether or not these methods should be
made public or moved to another class and first make sure I had
written examples that focused on the behaviour I was interested in. I
have found that having those tend to be a good help when thinking
about making private methods public. Even if you find you don't need
these methods to be public you will find the examples afford you a
great deal of freedom to refactor that code in a number of ways or by
simply leaving them as private methods all while leaving the examples
intact.

>
> and yeah, I think 5 it my favorite at the moment,
> they don't have to be
> private anyway, also IMHO testing private methods through the public API
> it's not in general applicable (only thinking here, no code sample)...
>
> but anyway I wanted to hear what you guys think about it

Behaviour first. That will help you identify if you're dealing with
different responsibilities which might push you to extract a new
object, or if you're dealing with logic that goes together (in which
you might keep well-named private methods), or if you want to pull out
some of the dry and boring math calculations out into a method on some
utility class.

If you put good examples in place you'll be able to change your mind
later without having to maintain *brittle* specs while having a great
deal of freedom for re-organizing the implementation in a number of
imaginable ways.

--
Zach Dennis
http://www.continuousthinking.com (personal)
http://www.mutuallyhuman.com (hire me)
http://ideafoundry.info/behavior-driven-development (first rate BDD training)
@zachdennis (twitter)

Ashley Moran

unread,
Oct 17, 2009, 3:55:39 PM10/17/09
to zach....@gmail.com, rspec-users

On 15 Oct 2009, at 01:06, Zach Dennis wrote:

> So if you're looking to read it for immediately applicable techniques
> I would say don't bother, but if you're an information-whore and want
> to gain a wealth of knowledge, and want to see a lot of thinking and
> techniques that have influenced a lot of today's tools and thinking,
> then read it, for sure, but don't expect to read it in a weekened or
> even a week... that would be information overload -- your brain would
> explode.

Thanks for the info. I think I'll look at some other books on my list
first then, and come back to this when I run out.

Cheers
Ashley

Pat Maddox

unread,
Oct 21, 2009, 3:38:23 AM10/21/09
to rspec-users
Break your object up. It's too big.

Reply all
Reply to author
Forward
0 new messages