<rdf:RDF
    xmlns:s='http://snipsnap.org/rdf/snip-schema#'
    xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'
    xml:base='http://bliki.brandonburk.com/snipsnap/rdf'>
    <s:Snip rdf:about='http://bliki.brandonburk.com/snipsnap/rdf#Unit+Testing+with+StrutsTestCase+and+EasyMock'
         s:cUser='brandon'
         s:oUser=''
         s:mUser='brandon'>
        <s:name>Unit Testing with StrutsTestCase and EasyMock</s:name>
        <s:content>1 Background&#xD;&#xA;A common problem I have observed with Java web development using the Struts framework is the inability to easily unit test your Struts Actions. Even more difficult is the task of testing the Struts flow control without a running Servlet container (E.g. Tomcat, Jetty, WebSphere, etc.).&#xD;&#xA;&#xD;&#xA;I have recently rolled the sleeves up and decided to dig into this for the Reference Implementation I am building for my organization. This Reference Implementation utilizes Struts and subsequently I am aiming to embody many best practices, including unit testing at every layer of the architecture.&#xD;&#xA;&#xD;&#xA;The primary benefits of unit testing your Struts Actions are:&#xD;&#xA;- ~~Support for Test Driven Development (Red, Green, Refactor!)~~&#xD;&#xA;- ~~Assurance of flow control~~&#xD;&#xA;- ~~Confidence in refactoring for both Actions and ActionForms~~&#xD;&#xA;- ~~The unit test serves as a documented contract of the Action behavior~~&#xD;&#xA;- ~~Lighter weight development environment, no container needed~~&#xD;&#xA;&#xD;&#xA;1 Mocking to the Rescue&#xD;&#xA;The concept of mocking drives this effort. The StrutsTestCase technology provides this via the MockStrutsTestCase class. In addition EasyMock provides a quick and concise way to specify the mock classes that are used from within your Struts Action.&#xD;&#xA;&#xD;&#xA;For each unit test I have taken a two part testing approach:&#xD;&#xA;- Create a mock object using EasyMock to assert the behavior of the service implementation class that is being executed by the Struts Action.&#xD;&#xA;- Create a mock object using MockStrutsTestCase of the Struts environment to assert that the correct ActionForward is returned.&#xD;&#xA;&#xD;&#xA;1.1 The Abstract Factory Design Pattern&#xD;&#xA;In order to enable substitution of the service class I have applied the {link:Abstract Factory pattern|url=http://en.wikipedia.org/wiki/Abstract_factory_pattern}. The TestObjectFactory class is a concrete implementation of an abstract ObjectFactory class. At the time of unit testing I can easily insert a stub or mock service, while at runtime the SpringObjectFactory concrete implementation  will provide the actual services from the Spring application context of the web application.&#xD;&#xA;{code}&#xD;&#xA;...&#xD;&#xA;public abstract class ObjectFactory&#xD;&#xA;{&#xD;&#xA;    private static ObjectFactory instance;&#xD;&#xA;    public abstract Object getBean(String beanName, ServletContext servletContext);&#xD;&#xA;    public static synchronized ObjectFactory getInstance()&#xD;&#xA;    {&#xD;&#xA;        if (instance == null)&#xD;&#xA;        {&#xD;&#xA;            instance = new SpringObjectFactory();&#xD;&#xA;        }&#xD;&#xA;&#xD;&#xA;        return instance;&#xD;&#xA;    }&#xD;&#xA;    public static synchronized void setInstance(ObjectFactory instance)&#xD;&#xA;    {&#xD;&#xA;        ObjectFactory.instance = instance;&#xD;&#xA;    }&#xD;&#xA;}&#xD;&#xA;...&#xD;&#xA;public class TestObjectFactory extends ObjectFactory&#xD;&#xA;{&#xD;&#xA;    private static HashMap beans = new HashMap();&#xD;&#xA;&#xD;&#xA;    public Object getBean(String beanName, ServletContext servletContext)&#xD;&#xA;    {&#xD;&#xA;        return beans.get(beanName);&#xD;&#xA;    }&#xD;&#xA;    public void addBean(String beanName, Object bean)&#xD;&#xA;    {&#xD;&#xA;        beans.put(beanName, bean);&#xD;&#xA;    }&#xD;&#xA;}&#xD;&#xA;...&#xD;&#xA;public class SpringObjectFactory extends ObjectFactory&#xD;&#xA;{&#xD;&#xA;&#xD;&#xA;    public Object getBean(String beanName, ServletContext servletContext)&#xD;&#xA;    {&#xD;&#xA;        WebApplicationContext webContext = WebApplicationContextUtils&#xD;&#xA;                .getWebApplicationContext(servletContext);&#xD;&#xA;&#xD;&#xA;        return webContext.getBean(beanName);&#xD;&#xA;    }&#xD;&#xA;}&#xD;&#xA;{code}&#xD;&#xA;&#xD;&#xA;1.1 StrutsTestCase and MockStrutsTestCase&#xD;&#xA;Example:&#xD;&#xA;{code}&#xD;&#xA;...&#xD;&#xA;public class ReservationPersistActionTest extends MockStrutsTestCase&#xD;&#xA;{&#xD;&#xA;&#9;private TestObjectFactory factory;&#xD;&#xA;&#xD;&#xA;&#9;public void setUp() throws Exception&#xD;&#xA;&#9;{&#xD;&#xA;&#9;&#9;super.setUp();&#xD;&#xA;&#9;&#9;factory = new TestObjectFactory();&#xD;&#xA;&#9;&#9;ObjectFactory.setInstance(factory);&#xD;&#xA;&#9;&#9;setContextDirectory(new File(&quot;WebContent&quot;));&#xD;&#xA;&#9;}&#xD;&#xA;&#xD;&#xA;&#9;public void testExecute_CreateSuccess()&#xD;&#xA;&#9;{&#xD;&#xA;&#9;&#9;// create our mock service&#xD;&#xA;&#9;&#9;MockControl control = &#xD;&#xA;                    MockControl.createControl(IReservationService.class);&#xD;&#xA;&#9;&#9;IReservationService reservationService = &#xD;&#xA;&#9;&#9;&#9;(IReservationService) control.getMock();&#xD;&#xA;                &#xD;&#xA;                // record expected mock state, behavior, and return value&#xD;&#xA;&#9;&#9;ReservationForm reservationForm = new ReservationFormStub();&#xD;&#xA;&#9;&#9;IReservationBO reservation = new ReservationBO();&#xD;&#xA;&#9;        ((ReservationFormStub)reservationForm).setReservationBO(reservation);&#xD;&#xA;&#9;&#9;reservationForm.setOperation(&quot;create&quot;);&#xD;&#xA;&#9;&#9;reservationService.create(reservation);&#xD;&#xA;&#9;&#9;ReservationTR transactionResult = new ReservationTR();&#xD;&#xA;&#9;&#9;transactionResult.setSuccess(true);&#xD;&#xA;&#9;&#9;control.setReturnValue(transactionResult);&#xD;&#xA;&#xD;&#xA;                // replay state, activate mock for use&#xD;&#xA;&#9;&#9;control.replay(); &#xD;&#xA;&#xD;&#xA;&#9;&#9;// put the mock service into the test object factory&#xD;&#xA;                // which is used by my struts Action&#xD;&#xA;&#9;&#9;factory.addBean(&#xD;&#xA;                    &quot;reservationService&quot;, reservationService);&#xD;&#xA;&#xD;&#xA;&#9;&#9;// Call the MockStrutsTestCase methods to specify URL path, form, etc.&#xD;&#xA;&#9;&#9;setRequestPathInfo(&quot;/reservation/persist&quot;);&#xD;&#xA;&#9;&#9;setActionForm(reservationForm);&#xD;&#xA;&#9;&#9;actionPerform();&#xD;&#xA;&#9;&#9;// Assert the action&apos;s forward&#xD;&#xA;&#9;&#9;verifyForward(&quot;success&quot;);&#xD;&#xA;&#xD;&#xA;&#9;&#9;// Assert the mock service behaved as recorded&#xD;&#xA;&#9;&#9;control.verify();&#xD;&#xA;&#9;}&#xD;&#xA;...&#xD;&#xA;{code}&#xD;&#xA;&#xD;&#xA;1 References&#xD;&#xA;- http://strutstestcase.sourceforge.net&#xD;&#xA;- http://javaboutique.internet.com/tutorials/StrutsTestCase&#xD;&#xA;- http://www.onjava.com/pub/a/onjava/2005/10/26/test-driven-development-using-strutstestcase.html&#xD;&#xA;- http://www.devx.com/Java/Article/20198&#xD;&#xA;- http://www-128.ibm.com/developerworks/java/library/j-stc/&#xD;&#xA;- http://www.easymock.org</s:content>
        <s:mTime>2008-02-05 23:24:57.0</s:mTime>
        <s:cTime>2007-02-08 01:07:04.0</s:cTime>
        <s:comments>
            <rdf:Bag>
                <rdf:li>
                    <s:Comment rdf:about='http://bliki.brandonburk.com/snipsnap/rdf#comment-Unit+Testing+with+StrutsTestCase+and+EasyMock-1'
                         s:cUser='anurekha_t'
                         s:oUser=''
                         s:mUser='anurekha_t'>
                        <s:name>comment-Unit Testing with StrutsTestCase and EasyMock-1</s:name>
                        <s:content>Brandon,&#xD;&#xA;&#xD;&#xA; In the example you have given, can you give some more details on the ObjectFactory and the TestObjectFactory?&#xD;&#xA;Basically, I&apos;m not able to understand how you are making your action class use mocked reservationService object. &#xD;&#xA;&#xD;&#xA; If I understand correctly, this is a framework which will not need any mock container for mocking the app server APIs. Correct me if wrong.&#xD;&#xA;&#xD;&#xA; Request your immediate clarification on this.&#xD;&#xA;&#xD;&#xA;thanks,&#xD;&#xA;Anu.</s:content>
                        <s:mTime>2007-08-29 03:22:12.0</s:mTime>
                        <s:cTime>2007-08-29 03:22:11.0</s:cTime>
                        <s:commentedSnip rdf:resource='http://bliki.brandonburk.com/snipsnap/rdf#Unit+Testing+with+StrutsTestCase+and+EasyMock'/>
                    </s:Comment>
                </rdf:li>
                <rdf:li>
                    <s:Comment rdf:about='http://bliki.brandonburk.com/snipsnap/rdf#comment-Unit+Testing+with+StrutsTestCase+and+EasyMock-2'
                         s:cUser='perkar'
                         s:oUser=''
                         s:mUser='perkar'>
                        <s:name>comment-Unit Testing with StrutsTestCase and EasyMock-2</s:name>
                        <s:content>Brandon,&#xD;&#xA;&#xD;&#xA;I agree with Anu. I do not really understand the ObjectFactory thing.&#xD;&#xA;&#xD;&#xA;I have read somewhere else that a good solution could be to use aop to be able to set your service objects as mock.&#xD;&#xA;&#xD;&#xA;I have tried using spring AOP, but Im not very pleased with my solution.&#xD;&#xA;&#xD;&#xA;Do you have any suggestion to be able to set your service mock object in a nice way. I can see you are using ObjectFactory. Is that a class you have created yourself? In that case, is it a static class? How is it possible to use that one in your Action class?&#xD;&#xA;&#xD;&#xA;Lots of questions :)&#xD;&#xA;&#xD;&#xA;thanks,&#xD;&#xA;Pelle</s:content>
                        <s:mTime>2007-10-23 16:41:56.0</s:mTime>
                        <s:cTime>2007-10-23 16:41:56.0</s:cTime>
                        <s:commentedSnip rdf:resource='http://bliki.brandonburk.com/snipsnap/rdf#Unit+Testing+with+StrutsTestCase+and+EasyMock'/>
                    </s:Comment>
                </rdf:li>
                <rdf:li>
                    <s:Comment rdf:about='http://bliki.brandonburk.com/snipsnap/rdf#comment-Unit+Testing+with+StrutsTestCase+and+EasyMock-3'
                         s:cUser='dennilee'
                         s:oUser=''
                         s:mUser='dennilee'>
                        <s:name>comment-Unit Testing with StrutsTestCase and EasyMock-3</s:name>
                        <s:content>Obviously this does&apos;t work. You didn&apos;t cover the most important part. How to inject the mock.</s:content>
                        <s:mTime>2008-01-29 04:11:36.0</s:mTime>
                        <s:cTime>2008-01-29 04:11:36.0</s:cTime>
                        <s:commentedSnip rdf:resource='http://bliki.brandonburk.com/snipsnap/rdf#Unit+Testing+with+StrutsTestCase+and+EasyMock'/>
                    </s:Comment>
                </rdf:li>
                <rdf:li>
                    <s:Comment rdf:about='http://bliki.brandonburk.com/snipsnap/rdf#comment-Unit+Testing+with+StrutsTestCase+and+EasyMock-4'
                         s:cUser='brandon'
                         s:oUser=''
                         s:mUser='brandon'>
                        <s:name>comment-Unit Testing with StrutsTestCase and EasyMock-4</s:name>
                        <s:content>To help clarify how the mock service is substituted for the real service I have added a section dealing with how this is accomplished using the Abstract Factory pattern.&#xD;&#xA;&#xD;&#xA;Thanks for the questions!&#xD;&#xA;&#xD;&#xA;Brandon</s:content>
                        <s:mTime>2008-02-05 23:14:08.0</s:mTime>
                        <s:cTime>2008-02-05 23:14:08.0</s:cTime>
                        <s:commentedSnip rdf:resource='http://bliki.brandonburk.com/snipsnap/rdf#Unit+Testing+with+StrutsTestCase+and+EasyMock'/>
                    </s:Comment>
                </rdf:li>
                <rdf:li>
                    <s:Comment rdf:about='http://bliki.brandonburk.com/snipsnap/rdf#comment-Unit+Testing+with+StrutsTestCase+and+EasyMock-5'
                         s:cUser='nodje'
                         s:oUser=''
                         s:mUser='nodje'>
                        <s:name>comment-Unit Testing with StrutsTestCase and EasyMock-5</s:name>
                        <s:content>I just registered to say brilliant! ... and simple.&#xD;&#xA;&#xD;&#xA;thanks a lot brandon! &#xD;&#xA;&#xD;&#xA;you saved my ass on this one, I was preparing to use the Spring&apos;s Struts Plugin , DelegatingActionProxy (refactoring all my actions, declaring them twice) and use AOP to control the injected mocks... not simple&#xD;&#xA;&#xD;&#xA;I&apos;d just add to the article that you have to replace your getBean(...) implementation with the ObjectFactory.getBean(...) one. &#xD;&#xA;Which is just changing one line if you have a BaseAction that all your Struts 1 action extends...</s:content>
                        <s:mTime>2009-03-03 04:16:10.0</s:mTime>
                        <s:cTime>2009-03-03 04:16:10.0</s:cTime>
                        <s:commentedSnip rdf:resource='http://bliki.brandonburk.com/snipsnap/rdf#Unit+Testing+with+StrutsTestCase+and+EasyMock'/>
                    </s:Comment>
                </rdf:li>
            </rdf:Bag>
        </s:comments>
        <s:snipLinks>
            <rdf:Bag>
                <rdf:li rdf:resource='http://bliki.brandonburk.com/snipsnap/rdf#Regarding Java'/>
                <rdf:li rdf:resource='#snipsnap-index'/>
            </rdf:Bag>
        </s:snipLinks>
        <s:attachments
             rdf:type='http://www.w3.org/1999/02/22-rdf-syntax-ns#Bag'/>
    </s:Snip>
</rdf:RDF>
