Slides and Code from My Code Generation Presentation

As promised, here are the slides and code from my cf.Objective() 2008 presentation, "Leveraging Code Generation to Jumpstart Your Development". Check the bottom of this entry for the enclosure.

So far the conference has been great. I've learned a lot at several sessions, including Andrew Powell's integration presentation on using Spring and Hibernate as the Java model for an app using CF for Controller and View. Chris Scott's ColdSpring presentation got my wheels turning with regard to custom Proxy Factories. And the announcement of the ColdFusion Open Initiative was most welcome, with a public bug base and more organized enhancement request system.

I'll post more after the conference is over, but so far it's been as good as I expected it to be!

Unit Testing Transfer Decorators with CFCUnit, BeanInjector, and ColdMock

Paul Marcotte asked me to post about how one might unit test a Transfer Decorator. So here you go Paul (and anyone else who might be interested) :-) .

To start with, basic testing of a Transfer Decorator is really no different than testing any other component. First, I set up a ColdSpring file to use for the test. Note that, depending on how many things you need to test and what you're testing, you can use the same ColdSpring file to test numerous things. In other words, you don't necessarily have to have a separate ColdSpring XML file for every test.

<beans>
   
   <bean id="transferFactory" class="transfer.transferFactory">
    <constructor-arg name="datasourcePath"><value>/tests/transfer/testing/datasource.xml</value></constructor-arg>
    <constructor-arg name="configPath"><value>/tests/transfer/testing/transfer.xml</value></constructor-arg>
    <constructor-arg name="definitionPath"><value>/tests/transfer/testing/definitions</value></constructor-arg>
   </bean>
   
   <bean id="datasource" factory-bean="transferFactory" factory-method="getDatasource" />

   <bean id="transfer" factory-bean="transferFactory" factory-method="getTransfer" />
   
</beans>

From here, the unit test might look like this (I'm using CFCUnit but the approach in CFUnit would be similar):

<cfcomponent name="TestRegisteredUser" extends="org.cfcunit.framework.TestCase">
   <cffunction name="setUp" access="public" output="false" hint="">
      <cfset var local = StructNew() />
      <cfset local.config = '/tests/transfer/testing/coldspring.xml.cfm' />
      <cfset beanFactory = CreateObject('component', 'coldspring.beans.DefaultXmlBeanFactory').init() />
      <cfset beanFactory.loadBeansFromXmlFile(beanDefinitionFile=local.config) />
      <cfset setRegisteredUser(beanFactory.getBean('transfer').get('user', 2)) />
   </cffunction>
   
   <cffunction name="test_customDecoratorMethod" returntype="void" access="public" output="false" hint="">
      <cfset var local = StructNew() />
      <cfset assertEqualsString("This is a custom method in the Decorator.", getRegisteredUser().customDecoratorMethod(), 'Custom decorator response does not match expected value.') />
   </cffunction>
   
   <cffunction name="getRegisteredUser" access="private" output="false" hint="I return the RegisteredUser.">
      <cfreturn variables.instance.RegisteredUser />
   </cffunction>
      
   <cffunction name="setRegisteredUser" access="private" output="false" hint="I set the RegisteredUser.">
      <cfargument name="RegisteredUser" required="true" hint="RegisteredUser" />
      <cfset variables.instance.RegisteredUser = arguments.RegisteredUser />
   </cffunction>
   
</cfcomponent>

As you can see, you grab the Decorator from Transfer, and then are free to test the methods in the Decorator the same way you would test any other CFC method.

To take this further, you can actually mix a number of the techniques that I've talked about to handle more complex tests. In this example, I'm using the TDOBeanInjectorObserver to wire in a ValidatorFactory to my Decorator. However, to keep the test isolated to the Decorator, I'm using ColdMock to create a mock of the ValidatorFactory and injecting that into the Decorator:

<beans>
   
   <bean id="transferFactory" class="transfer.transferFactory">
    <constructor-arg name="datasourcePath"><value>/tests/transfer/testing/datasource.xml</value></constructor-arg>
    <constructor-arg name="configPath"><value>/tests/transfer/testing/transfer.xml</value></constructor-arg>
    <constructor-arg name="definitionPath"><value>/tests/transfer/testing/definitions</value></constructor-arg>
   </bean>
   
   <bean id="datasource" factory-bean="transferFactory" factory-method="getDatasource" />

   <bean id="transfer" factory-bean="transferFactory" factory-method="getTransfer" />
   
   <bean id="validatorFactory" factory-bean="MockFactory" factory-method="createMock">
      <constructor-arg name="objectToMock">
         <value>tests.transfer.testing.ValidatorFactory</value>
      </constructor-arg>
   </bean>
   <bean id="MockFactory" class="coldmock.MockFactory" />
   
   <bean id="beanInjector" class="common.components.utility.BeanInjector">
      <constructor-arg name="debugMode"><value>false</value></constructor-arg>
      <constructor-arg name="suffixList"><value></value></constructor-arg>
   </bean>
   
   <bean id="TDOBeanInjectorObserver" class="common.components.transfer.TDOBeanInjectorObserver" lazy-init="false">
      <constructor-arg name="transfer">
         <ref bean="transfer" />
      </constructor-arg>
      <property name="beanInjector">
         <ref bean="beanInjector" />
      </property>
   </bean>
   
</beans>

This way, I can test any methods in the Decorator that actually rely on the ValidatorFactory by mocking the results from the ValidatorFactory method calls:

<cfcomponent name="TestRegisteredUser" extends="org.cfcunit.framework.TestCase">
   <cffunction name="setUp" access="public" output="false" hint="">
      <cfset var local = StructNew() />
      <cfset local.config = '/tests/transfer/testing/coldspring.xml.cfm' />
      <cfset beanFactory = CreateObject('component', 'coldspring.beans.DefaultXmlBeanFactory').init() />
      <cfset beanFactory.loadBeansFromXmlFile(beanDefinitionFile=local.config) />
      <cfset setRegisteredUser(beanFactory.getBean('transfer').get('user', 2)) />
   </cffunction>
   
   <cffunction name="test_customDecoratorMethodUsingValidator" returntype="void" access="public" output="false" hint="">
      <cfset var local = StructNew() />
      <cfset local.validator = beanFactory.getBean('validatorFactory') />
      <cfset local.validator.returns("This is the MOCKED ValidatorFactory method.") />
      <cfset assertEqualsString("This is the MOCKED ValidatorFactory method.", getRegisteredUser().customDecoratorMethodUsingValidator(), 'Custom decorator using validator response does not match expected value.') />
   </cffunction>
   
   <cffunction name="getRegisteredUser" access="private" output="false" hint="I return the RegisteredUser.">
      <cfreturn variables.instance.RegisteredUser />
   </cffunction>
      
   <cffunction name="setRegisteredUser" access="private" output="false" hint="I set the RegisteredUser.">
      <cfargument name="RegisteredUser" required="true" hint="RegisteredUser" />
      <cfset variables.instance.RegisteredUser = arguments.RegisteredUser />
   </cffunction>
   
</cfcomponent>

Just for reference, the Decorator I'm using in these test examples looks like this:

<cfcomponent hint="Registered User" extends="transfer.com.TransferDecorator" output="false">
   
   <cffunction name="customDecoratorMethod" access="public" returntype="string" output="false" hint="">
      <cfreturn "This is a custom method in the Decorator." />      
   </cffunction>
   
   <cffunction name="customDecoratorMethodUsingValidator" access="public" returntype="string" output="false" hint="">
      <cfreturn getValidatorFactory().validatorFactoryMethod() />      
   </cffunction>
   
   <cffunction name="getValidatorFactory" access="public" returntype="any" output="false" hint="I return the ValidatorFactory.">
      <cfreturn variables.instance.validatorFactory />
   </cffunction>
      
   <cffunction name="setValidatorFactory" access="public" returntype="void" output="false" hint="I set the ValidatorFactory.">
      <cfargument name="validatorFactory" type="any" required="true" hint="ValidatorFactory" />
      <cfset variables.instance.validatorFactory = arguments.validatorFactory />
   </cffunction>
   
</cfcomponent>

Hopefully that helps clear up how one might go about testing a Transfer Decorator. It also shows how you can use unit testing, ColdMock, and the Transfer Bean Injector together. Please let me know what you think or if you have any questions or comments about how I'm doing this.

More ColdSpring Goodness: Updated TDOBeanInjectorObserver and Release of BeanInjector

I've had some good feedback about the TDOBeanInjectorObserver that I released earlier this week. However, I realized that with a small bit of further effort, I could make the idea even more useful. I had put quite a bit of work and thought into handling the dependency injection and the caching of the dependencies for maximum performance. With this all in place and working, it was only a small step to create a generic BeanInjector component that could be used to autowire any component at all, not just Transfer Decorators! So we now have TDOBeanInjectorObserver v1.1, and BeanInjector v1.0.

From the outside, the TDOBeanInjectorObserver has only changed slightly. The only difference is that you now must pass a BeanInjector property to the TDOBeanInjectorObserver in your ColdSpring XML, like this:

<bean id="transferFactory" class="transfer.transferFactory">
       <constructor-arg name="datasourcePath"><value>/project/config/datasource.xml</value></constructor-arg>
       <constructor-arg name="configPath"><value>/project/config/transfer.xml</value></constructor-arg>
       <constructor-arg name="definitionPath"><value>/project/transfer/definitions</value></constructor-arg>
      </bean>
      
      <bean id="transfer" factory-bean="transferFactory" factory-method="getTransfer" />
      
      <bean id="TDOBeanInjectorObserver" class="project.components.TDOBeanInjectorObserver" lazy-init="false">
         <constructor-arg name="transfer">
            <ref bean="transfer" />
         </constructor-arg>
         <property name="beanInjector">
            <ref bean="beanInjector" />
         </property>
      </bean>
      
      <bean id="beanInjector" class="project.components.BeanInjector" />

The main difference from the 1.0 version is that you must define a BeanInjector bean, and that you must pass this in as a property to the Observer. The only other change is that if you were using the optional constructor arguments for "suffixList" and "debugMode", these are now specified for the BeanInjector instead of the TDOBeanInjectorObserver.

The real benefit of this doesn't have much effect on the Observer. It is that anyone can now use the BeanInjector to autowire any component. Why is this useful? I'm glad you asked! ;-)

There has always been a performance penalty for managing non-Singleton (aka "transient" or "per-request") beans with ColdSpring. This is because ColdSpring goes through several phases of lookups and dependency resolution when it creates beans. With Singletons, this is never an issue because it only happens once, at application startup. However, the benefits of being able to manage non-Singleton beans with ColdSpring, such as autowiring of dependencies, were still great enough to outweigh the overhead.

Well now we can have the best of both worlds. Using the BeanInjector, you can still have any dependent beans that are managed by ColdSpring autowired into your transient object. But because of the way the BeanInjector caches the dependencies for a given component type, the overhead is virtually eliminated.

A second advantage of using the BeanInjector to autowire your transient objects is that you can maintain control over how your transient object is constructed. When you call getBean() on the ColdSpring BeanFactory, you get back a fully initialized and constructed object. This means that you have very minimal control over how the init() method of your component is called (via constructor arguments in the ColdSpring XML configuration). Using the BeanInjector, you are free to create and call the init() method on your transient objects however you want to, and then autowire them at whatever point in the process you want to.

So for example, consider the following ColdSpring XML:

<bean id="userService" class="components.userService">
         <property name="beanInjector">
            <ref bean="beanInjector" />
         </property>
      </bean>
      
      <bean id="beanInjector" class="components.BeanInjector" />
      
      <bean id="validatorFactory" class="components.ValidatorFactory" />

My UserService may want to create and use a transient User object. That User object might have the following function defined in it:

<cffunction name="setValidatorFactory" access="public" returntype="void" output="false" hint="I set the ValidatorFactory.">
         <cfargument name="validatorFactory" type="any" required="true" hint="ValidatorFactory" />
         <cfset variables.instance.validatorFactory = arguments.validatorFactory />
      </cffunction>

So now, in my UserService, I can do something like this:

<cfset var user = CreateObject('component', 'components.User').init() />
      <cfset variables.beanInjector.autowire(user) />

Which will create the User and then autowire it, so that the ValidatorFactory would be injected into it. So this means you could pass dynamic values to the init() method of user if you want to:

<cfset var user = CreateObject('component', 'components.User').init(someDynamicValues) />
      <cfset variables.beanInjector.autowire(user) />

Or even that I could create the object, autowire it, and then call init() in cases where my constructor needs to perform logic that requires the bean to be autowired already:

<cfset var user = CreateObject('component', 'components.User') />
      <cfset variables.beanInjector.autowire(user) />   
      <cfset user.init() />

Obviously you have to be careful with this approach since you are not creating and initializing the component in one chained method call, but in many cases where the transient object is local to a function, there is no real chance of the uninitialized object being used by something else before you call init() on it. The point is, the option is there.

In any event, I thought having a BeanInjector like this might be useful to people. Given that most of the work had already been done in the creation of the Transfer Observer, I decided to take the next step and just move that logic into a component that could autowire anything. I'm interested to hear any thoughts on this, so please comment and give your feedback. Thanks.

My Transfer Decorator Bean Injector Observer Is Now Available

I'm finally releasing my Bean Injector Observer, which will allow you to automatically inject ColdSpring-managed beans into your Transfer Decorator objects. I've found that this makes it much easier to create decorators that act as rich business objects.

I had been holding off on releasing this because it will probably be included in the near future in either the ColdSpring or Transfer bleeding edge releases. However, a lot of people have contacted me privately or on the Transfer list about wanting to use this, so I'm going to make it available on my site until we see it appear in one of the framework downloads.

The content of the release notes follows, which should explain what it is and how it works. I hope people find this useful! Thanks to Sean Corfield and Mark Mandel for their input and review of the code to help me make sure it works as well as possible.

File Name:

TDOBeanInjectorObserver.cfc (Transfer Decorator Object Bean Injector Observer)

Description:

This is a Transfer Observer that will autowire your Transfer Decorators with matching beans from ColdSpring when the Decorator is created. This makes it much easier to create "rich" Decorators that can handle much more business logic than standard Transfer Objects. The dependencies are cached by this Observer for performance. After the first instance of a Decorator is created, all subsequent Decorators of that type will have their dependencies injected using cached information. The component is thread-safe.

Usage:

Usage of the Observer is fairly straightforward. The ColdSpring XML file might look like this:

<bean id="transferFactory" class="transfer.transferFactory">
       <constructor-arg name="datasourcePath"><value>/project/config/datasource.xml</value></constructor-arg>
       <constructor-arg name="configPath"><value>/project/config/transfer.xml</value></constructor-arg>
       <constructor-arg name="definitionPath"><value>/project/transfer/definitions</value></constructor-arg>
      </bean>
      
      <bean id="transfer" factory-bean="transferFactory" factory-method="getTransfer" />
      
      <bean id="TDOBeanInjectorObserver" class="tests.transfer.TDOBeanInjectorObserver" lazy-init="false">
         <constructor-arg name="transfer"><ref bean="transfer" /></constructor-arg>
      </bean>
      
      <bean id="validatorFactory" class="components.ValidatorFactory" />

Your ColdSpring configuration must be set to inject the Transfer object into the Observer as a constructor argument.

The Observer will register itself with Transfer using the transfer.addAfterNewObserver() method. To ensure that this happens at application startup, you have two options:

1. Use the latest version of ColdSpring that supports lazy-init. What this means is that ColdSpring will automatically construct all beans that have lazy-init="false" defined in the ColdSpring XML (as the TDOBeanInjectorObserver bean is in the above config snippet). You tell ColdSpring to construct all non-lazy beans when you create the BeanFactory:

<cfset beanFactory.loadBeans(beanDefinitionFile=configFileLocation, constructNonLazyBeans=true) />

Using this approach, the TDOBeanInjectorObserver will be registerd with Transfer without you have to do anything else.

2. On older versions of ColdSpring, or if you do not wish to use the lazy-init capability, the only additional step required is to create an instance of the Observer after you initialize ColdSpring, like this:

<cfset beanFactory.loadBeans(beanDefinitionFile=configFileLocation) />
<cfset beanFactory.getBean('TDOBeanInjectorObserver') />

This ensures that the Observer is constructed and registers itself with Transfer.

Your Transfer Decorator would have public setter method(s) for the bean(s) you want to inject, for example:

<cffunction name="setValidatorFactory" access="public" returntype="void" output="false" hint="I set the ValidatorFactory.">
<cfargument name="validatorFactory" type="any" required="true" hint="ValidatorFactory" />
<cfset variables.instance.validatorFactory = arguments.validatorFactory />
</cffunction>

Once the Observer is registered with Transfer, any time you create a Transfer Decorator, the Observer will automatically inject any dependent beans into it at creation time. So in the above example, as soon as the Decorator is created, it will automatically have the ValidatorFactory injected into it via the setValidatorFactory() method. The end result is that any setters in your Decorators that have matching bean IDs in ColdSpring will have those beans injected automatically. As an additional example, a bean with an ID of "productService" would be autowired into a Decorator that had a public setter method named setProductService(), and so on.

There is an optional constructor argument called "suffixList" that can be supplied. This is a comma-delimited list of propery name suffixes that will be allowed. If you specify a suffixList, the Observer will only inject beans which end in one of the suffixes in the list. For example, if you specify a suffixList of "service", setter methods for setUserService() and setLoginService() would be called, but setter methods for setValidatorFactory() or setContext() would NOT be called. This can be useful in rare situations where your Transfer Object may have database-driven properties that conflict with the names of ColdSpring beans. Most people probably won't need to worry about this, but the option is here in case the issue arises.

In case you have problems determining whether beans are being properly injected into your Decorators, there is an optional init() method argument called "debugMode". By default, this is false. If you set it to true via the ColdSpring XML config file, the component will trace successful dependency injections to the debugging output. It will also rethrow any errors that occur while trying to inject beans into your Decorators. Obviously, ensure that this is remains off in production.

BlogCFC was created by Raymond Camden. This blog is running version 5.9. Contact Blog Owner