Advice on designing for IoC

Chad Woolley <lists-+3axmRx+iL57nQ4KK8CQzgC/[email protected]> Sun, 11 Jan 2004 07:28:44 -0700
Newsgroups gmane.comp.java.jcontainer.interest
Message-ID <[email protected]>
Hello,

I was referred here with this question by the kind folks on the picocontainer 
list, so I hope this isn't off-topic.  It's also a very long post, so feel free 
to ignore it - it's partially to help me organize my thoughts.

I am developing a mock-object framework (http://www.virtualmock.org), and I am 
looking for advice on how to have a better design according to IoC principles. 
I want to design VirtualMock internally to follow IoC principles, as well as 
allowing it to play well with IoC-based containers and applications.

I'm relatively new to IoC, so I'm trying to understand how to apply it. 
However, many of the tools and examples are based around Server or GUI 
applications, and I'm not sure how to apply some of the concepts to a testing 
framework.

To try to structure this post, I'll divide it into BACKGROUND, GOALS and QUESTIONS.

BACKGROUND:

- VirtualMock uses aspect-oriented programming to allow you to create "virtual" 
mock objects, which are on the class level, not actual objects.  In other words, 
picking out and intercepting any method call, regardless of the actual object 
reference.  It can also mock static or final methods.  For more details, see 
http://www.virtualmock.org/features.html.

- While this approach might be anathema on a list like this, it can have many 
powerful applications, such as unit testing EJBs, or unit/functional testing 
legacy applications which may not be as well-designed as you wish.

- Simple example of current API:

   // Create VM main control
   VM vm = VMFactory.createVM();

   // Create class under test
   sample = new Sample();

   // Create return value
   String expected = new String("This is the mock return value");

   // "record" the expected invocation.  Note it is a static method.
   SampleCollaborator.staticPrint();
   // "record" the value the virtual mock will return
   vm.recordReturnValue(expected);

   // Put mock in "playback" mode
   vm.playbackCallsOrdered();

   // Invoke the Class Under Test.  Note the collaborator is not passed in
   String actual = Sample.getStaticCollaboratorOutput();

   // verify the mock returned the recorded value
   assertEquals(expected, actual);
   // verify the mock was invoked
   vm.verify();


GOALS:

- Make the framework as flexible as possible.  In practice, this would mean 
allowing the user as much freedom as possible to override the default behaviors 
of the framework, at various levels of granularity.

- Make the framework as extensible as possible.  Developers should be able to 
easily override any portion of the framework and provide their own implementation.

- Make the framework simple to use.  Since a mock object framework is something 
that will be used over and over, it should have a very simple, concise API.  I 
am suspecting, although I'm hope I'm wrong, that there will be some trade off 
between flexibility/extensibility and simplicity/usability.


QUESTIONS:

- How should I approach the entry point and configuration of the framework? 
Currently VMFactory.createVM() is the is a static method the user calls, and 
this automatically triggers instantiation and initialization of internal 
components.  However, this doesn't seem good (static methods and static 
factories are bad, right?)  An IoC type-3 approach would seem to suggest I 
should pass in a VMConfiguration object that contains all configuration data. 
An IoC type-2 approach would suggest that I provide setters for all 
configuration options on the VMFactory.  However, I don't want to force the user 
to go through a lengthy initialization process every time they want to use a 
mock object, because this will make the framework a pain to use.

- How should I approach 'global' defaults for configuration?  This is probably 
related to the first question.  I currently was going down the path of an XML 
configuration which would allow the user to configure certain global behaviors, 
or to configure them for certain scopes (application wide, wildcard 
package/class/method name matching, etc).  However, this seems to conflict with 
the IoC approach of passing in all objects and configuration data.  Are XML 
config files bad?

- How can I avoid using a static method or factory as my entry point?  Again, 
probably related to the first question.  Is passing in or setting the 
configuration the only option?  What if the users just wants a default 
configuration?

- How would the concept of "lifecycle" (http://picocontainer.org/lifecycle.html) 
    fit into my framework - as the pico site says: "Lifecycle is at least half 
of Inversion of Control."?  The use of VirtualMock does have a clearly defined 
lifecycle, which I call "phases" 
(http://www.virtualmock.org/documentation.html).  It seems like there's some 
important similarity here, but I'm not sure what the significance is.

- Do you see any benefit in trying to structure VirtualMock to run inside of a 
container, such as picocontainer?

-  Are there any other suggestions, especially those that will make the 
framework more flexible, extensible, or easy to use.


Many Thanks,
Chad