On Saturday, June 8, 2002, at 07:12  pm, chromatic wrote:

> On Saturday 08 June 2002 11:02, Michael G Schwern wrote:
[snip]
>> In reality, if Bar.pm changes in an incompatible way Foo.pm's own tests
>> should fail and that should cover you.
>
> Not in a strict isolation testing environment, where I don't want to 
> use the
> real Bar.pm when testing Foo.pm.
>
> Maybe I'm being too strict about my isolation, but it's had good 
> results for
> me so far.

[This message has became a bit of a ramble... apologies in advance :-) ]

I played with something a bit like the proposed Test::Depend, but I 
abandoned it after a while since it didn't help me in the way I had 
expected. Don't know if this will help the discussion but what I did was 
roughly as follows...

-       Have all of my mock objects stick their class, the method names 
they were
        called with, and $0 into a database. So you have a bunch of rows like:

                MockFoo open            Bar/t/bar.t
                MockFoo close   Bar/t/bar.t
                ....

-       Manually maintain a mapping from MockFoo to the real class hierarchy
        that implemented Foo (this could probably have been automated if I 
could
        have been bothered to think about it :-)

-       Have a test for each module that compared an MD5 hash of each 
method in the   module (got by extracting 'sub method_name { .... }' 
string from the .pm)
        to the hash from the last run, and throw out some warnings on the tests
        that used the mocked version of the same method if it had changed (or
        vanished).

My hope was that this would help me in refactoring code. I wouldn't have 
to keep track of what modules use what methods, so I could be more 
confidant in making changes.

This fell down in three ways. Two minor, one major.

        #1: Minor problem - Didn't work for AUTOLOAD methods, generated 
methods,
        etc.

        #2: Minor problem - False negatives when changes were not in 
method_name,
        but something other private method of Foo that method_name called.

These were not really issues for me, probably because of my personal 
coding style, so I didn't get a lot of false negative/positives.

Test::Depend will cope better with these problems since it looks at the 
test results of the module as a whole. This should give an excellent 
indication of semantic change (at the expense of it being module, rather 
than method, specific).

Test::Depend also involves less work in implementing the unit tests 
since you don't have to mess with the mocks.

However, it doesn't help with:

        #3: Major problem - You have to check the warnings manually!

I found that, once you have a moderately complex system, it's hard to 
determine whether changes you are being warned about are going to be an 
issue (beyond simple things like method renaming). I spent too much time 
looking at the code, and usually ended up writing a functional test to 
make sure that I wasn't missing something.

I eventually just bit the bullet and started writing more functional 
tests. This (of course)  had the usual affect of writing more tests --- 
it made development faster.

Worry about making a non-compatible change to a class? Not me. I just do

        % make test >& functional1.txt

make the change, then repeat

        % make test >&! functional2.txt ; diff functional1.txt functional2.txt

until the only difference I see is the time the tests took.

While this involves some repetition of the unit tests it does help track 
things like changing APIs. If you change Foo in some non-compatible way 
you get the following little pattern:

        - Tests show that Bar's functional tests fail, but unit tests succeed.
        - Update MockFoo so that Bar's unit tests fail the same way
        - Fix Bar.pm so that all tests pass.

I don't run the functional tests as often as the unit tests so I don't 
lose a lot of speed. It doesn't involve a lot of duplicate code since I 
keep most of my tests in classes that allow me to swap mock/live objects 
by subclassing and/or configuration options.

Once I started writing decent functional tests, my hack to track changed 
methods suddenly ceased being useful. I think you may find the same 
thing occurring with Test::Depend - it will just encourage you to write 
more functional tests ;-)

Cheers,

Adrian
--
Adrian Howard  <[EMAIL PROTECTED]>
phone: 01929 550720  fax: 0870 131 3033  www.quietstars.com

Reply via email to