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