Nerd post today. Most of my MXUnit unit tests look something like:
<cffunction name="setUp" returntype="void" access="public">
<cfscript>
variables.beanFactory = createObject("component", "coldspring.beans.DefaultXmlBeanFactory").init();
variables.beanFactory.loadBeansFromXmlFile("/config/globalbeans.xml", true);
variables.service = variables.beanFactory.getBean("BatchbookService");
</cfscript>
<cfset localMode = true />
</cffunction>
You can read more about my localMode trick for testing remote APIs but I’m simply loading up my Coldspring configuration file for my app which includes dozens and dozens and dozens of beans. I have three problems with this:
- The unit tests depend on the configuration used to run the site which might not always be in my environment
- It slows down my tests by generating beans that my unit test doesn’t need
- My production and test bean config may not be the same (credentials, etc)
I might be able to get around #2 by using lazy init but the principle still holds: the unit test should be as standalone as possible. In the case I’m working on today, I have a client I’ve written (and released) for a third party API from Batchbook. But I need to have some Batchbook-to-My-App conversions of JSON to queries and so forth. I don’t want to bake those into my publicly-released client so what I really want in my production app is some bean config that looks like:
<bean id="BatchbookService" class="model.external.batchbook.BatchbookService">
<constructor-arg name="BatchbookClient">
<bean class="model.external.batchbook.batchbook">
<constructor-arg name="apikey"><value>password</value></constructor-arg>
<constructor-arg name="endpoint"><value>https://somehost.batchbook.com/api/v1</value></constructor-arg>
<constructor-arg name="restconsumer"><ref bean="restconsumer" /></constructor-arg>
<constructor-arg name="JSONUtil"><ref bean="JSONUtil" /></constructor-arg>
</bean>
</constructor-arg>
</bean>
<bean id="RestConsumer" class="model.external.restconsumer" />
<bean id="JSONUtil" class="model.utils.JSONUtil" />
But if my BatchbookClient is a nested constructor-arg, how can I get it from the beanfactory to test it?
Alternative Coldspring Configurations
Turns out there’s more than one way to load beans into Coldspring and passing a path to an XML configuration file is just one. Looking over the 1.0 reference guide, I found two additional methods of interest:
- loadBeansFromXmlRaw(xmlStringConfig, true)
- loadBeansFromXmlObj(parsedXmlConfig, true)
Applying that to my current unit test, my setup method now looks like:
<cffunction name="setup">
<cfset var beanConfigs = "" />
<cfset var localMode = true />
<cfsavecontent variable="beanConfigs">
<beans>
<bean id="BatchbookService" class="model.external.batchbook.BatchbookService">
<constructor-arg name="BatchbookClient"><ref bean="BatchbookClient" /></constructor-arg>
</bean>
<bean id="BatchbookClient" class="model.external.batchbook.batchbook">
<constructor-arg name="apikey"><value>password</value></constructor-arg>
<constructor-arg name="endpoint"><value>https://somehost.batchbook.com/api/v1</value></constructor-arg>
<constructor-arg name="restconsumer"><ref bean="restconsumer" /></constructor-arg>
<constructor-arg name="JSONUtil"><ref bean="JSONUtil" /></constructor-arg>
</bean>
<bean id="RestConsumer" class="model.external.restconsumer" />
<bean id="JSONUtil" class="model.utils.JSONUtil" />
</beans>
</cfsavecontent>
<cfscript>
variables.beanFactory = createObject("component", "coldspring.beans.DefaultXmlBeanFactory").init();
variables.beanFactory.loadBeansFromXmlRaw(beanConfigs, true);
variables.bb = variables.beanFactory.getBean("BatchbookClient");
variables.svc = variables.beanFactory.getBean("BatchbookService");
</cfscript>
</cffunction>
Coldspring now instantiates much quicker and it only loads beans related to my unit test. Plus, I have the flexibility to directly address the BatchbookClient and run it through its paces alongside the BatchbookService.
If you only have a few unit tests, these improvements won’t mean much but as your test suites grow (and you work towards continuous deployment…) you will find that speeding up testing is always a good thing and helps encourage you to do it.
Comments are closed.