So I have been trying to get a blog kicked-off this year and this was the lead article, but I haven't found a hosting solution I am happy with. I have my $5 Confluence/Jira licenses and so I think I will deploy them somewhere (cheap I hope). Why is that important? Well the post is all about Struts2 unit testing and the pattern I have developed. I have been working on this for a while and I have combined the best elements from the approaches I have come across. So since its not hosted anywhere I will attach the Post and the relevant files to this mailing list. I am sure the classes can improve so I am hoping that with the pattern out there, people will show how it can be further refactored and optimized.

Quick Start
*Classes to Use*
- BaseStrutsTestCaseSpring
- StrutsContextLoader

*Example Usage*
- BusinessEntityControllerTest

*Blog Post*
- struts2-testing.txt

-D

Struts2 Unit Testing

http://itefforts.blogspot.com/2007/08/struts2-spring-junit.html
http://depressedprogrammer.wordpress.com/2007/06/18/unit-testing-struts-2-actions-spring-junit/
http://www.appfuse.org

If you are going to work with Struts2, and you haven't been under an 
engineering rock over the past 5 years, you are going to want to unit test your 
actions.  Unit testing in Struts2 comes in several flavors, and I will give you 
a tour of resources available to you as well a recommendation for what works 
for me.

Most developers using Struts2 are using Spring to wire their actions to various 
Services.  We will assume that is true for you and ignore simple unit testing 
of an Action with no Services or using an alternative like Guice to inject 
dependent services.  There are a few things we are looking for when we unit 
test:

1. We want our tests to auto wire services like they will "in the wild"
2. We don't want our calls to Services to change the state of the underlying 
data such that unit tests start depending on each other
3. We want to be able to test our Struts actions in a few different 
configurations:
        * Test actions with the interceptor stack
        * Test action methods in isolation
        * Test Result views (JWebUnit) - a different post all together.

Testing Fixtures
When you are testing you will likely want to have some pre-set data "fixtures" 
that your action unit tests can use.  For example, you want a record in the 
database with the id of 1, so you can test your action.get() method with the 
parameter id=1.  I use maven as my build tool and the db-unit plugin to setup 
my database with the default test fixture data.  The best example of this at 
work is in the Appfuse framework (http://www.appfuse.org) or you can take a 
look at the plugin itself (http://mojo.codehaus.org/dbunit-maven-plugin/).

Appfuse (Matt Raible) http://www.appfuse.org
The unit testing model in Appfuse for Struts2 action focuses on unit testing 
the action methods in isolation of the surrounding Struts2 container 
environment.  Actions are created and then wired to their services via Spring, 
parameters are then set using setters on the Action (mimicking the Parameters 
interceptor).  Then the test will call the action methods directly, and then 
inspect the action using the action's getters.
This method of testing works well.  It is simple and easy to understand.  It 
extends from a mouthful of a Spring JUnit utility class, 
AbstractTransactionalDataSourceSpringContextTests, that automatically database 
transactions triggered from action methods.  This is important so your test 
fixtures are stable for every test.  

http://fisheye4.atlassian.com/browse/appfuse/trunk/web/struts/src/main/java/org/appfuse/webapp/action/BaseActionTestCase.java


Depressed Programmer - 
http://depressedprogrammer.wordpress.com/2007/06/18/unit-testing-struts-2-actions-spring-junit/
My favorite name for a blog, this was the first base unit test I saw that 
allowed you to do the type of tests from Appfuse, or also run your actions with 
the full stack of interceptors.  The bonus to this method is that you can test 
your Struts configuration and the interaction of your actions with interceptors 
based on the request parameters you expect to be sent.  It is definitely more 
complicated, but it gives you some flexibility.  You can test your action with 
mock Request parameters and the interceptor stack, or you can test the action 
methods directly, like Appfuse.

The bad news about this test case is that it is complicated to setup your 
request parameters and its Struts initialization is a bit dated. It also does 
not provide the auto-rollback feature the Appfuse test does.  The good news is 
that I have adapted these two testing models to create a mix of the two that I 
have used successfully for a while.

*BaseStrutsTestCaseSpring*
This base class uses Junit 4 annotations, Spring 2 testing context annotations 
and non-deprecated methods for initializing Struts for tests.  It comes with a 
custom StrutsContextLoader class to fire up a WebApplicationContext from the 
file locations in the @ContextConfiguration annotation.  You can setup your 
request by using the protected MockHttpServletRequest request member variable 
which is used when setting up the Struts2 environment.  Similar to Appfuse it 
uses a Spring test utility, AbstractTransactionalJUnit4SpringContextTests, to 
automatically rollback database transactions.  One thing I needed to get used 
to is using the Assert.assertXXX() static methods rather than just using 
assertXXX().  

Since this is a Spring test context setup you can inject MockServices when you 
don't want the underlying service code to execute.  I have also injected 
Resources so I can do things like test an attachment system from the classpath 
during unit tests and from the filesystem in production.  I use Spring Security 
and so in my Struts2 actions they call out to get the "currentUser".  I inject 
a mock SecurityManager that gives Struts2 the User they are looking for and 
everyone is happy!

The basic setup is:

* Extend the base class
* Create a test method with an @Test annotation
* setup your request by calling methods like request.setParameter("id","1");
* call createAction with the namespace and action name and get back an instance 
of your Action object you can manipulate
* Do anything else to the action you want before the interceptors and action 
method run
* execute proxy.execute() and capture the Struts result as a String  (proxy is 
another protected member variable setup in createAction())
* Inspect your result string and action getters with Assert calls to test how 
it behaved.

If you really want something not to rollback, you can add a @Rollback(false) 
annotation.  I cannot remember the occasion but there have been times as I was 
developing tests that I turned off Rollback to see the records that got 
created.  More likely you need some unit tests at the service level if you 
looking at things like that, but I am assuming I was being lazy.  

If you really don't care about setting Request parameters or the interceptor 
stack, and want to test like in Appfuse, you can get your action back and just 
use the action.setters, call the action.method() and then use the 
action.getters to check its state after.

*StrutsContextLoader*
This is there to setup the ServletContext to locate the webroot.  If you are 
using Maven2, you should have no problems.  If not you may have to edit the 
path String....  This also takes the Spring configuration files and sets them 
up for the WebApplicationContext.

Struts Junit Plugin
This includes the StrutsTestCase base class that extends XWorkTestCase.  This 
is of course the "native" struts testing class, but I found it to be difficult 
to use unless you have some rather intimate knowledge of some of the more 
abstract Struts2 parts like ValueStack or ConfigurationManager.  I never really 
saw a clear pattern for its use in the Struts2 code base.  In fact, I see a lot 
of JWebUnit testing which I really only use for integration or view (JSP) 
testing.  I am sure that the StrutsTestCase class can be updated to include the 
features I have described in the previous setups, and perhaps it will.  But 
until then I would reccommend using my hybrid base TestCase.


JWebUnit
There is nothing special about JWebUnit for testing struts.  You have to start 
your application up in your servlet container and then point your JWebUnit 
tests to the webapp.  This is nice for integration testing, but its not 
something you run all the time.  These tests are something in the "I should do 
those but..." category.  I find my JSP/Freemarker templates are very simple and 
I don't make many mistakes.  When I do they are very obvious so the unit tests 
don't help me.  That said, I think they add another dimension of coverage and 
maintainability for your application 6 months from now, when you are deploying 
some patch.  

Attachment: BusinessEntityControllerTest.java
Description: Binary data

Attachment: BaseStrutsTestCaseSpring.java
Description: Binary data

Attachment: StrutsContextLoader.java
Description: Binary data




On May 15, 2009, at 2:49 PM, DUSTIN PEARCE wrote:

I can help you. When I get home tonight can send you some base test classes that will do what you want.

On May 15, 2009, at 2:01 PM, wkbutler <kent.but...@gmail.com> wrote:


Right, we're using Struts2. I'll investigate further, it would be nice to have the option. I'll post back if I find something useful. Thanks Matt -




mraible wrote:

I'm guessing you're using Struts 2? If so, this won't work because in a
normal (web.xml) environment, the filters handle wiring up the
dependencies.
Hopefully setting the dependencies manually isn't too much of a pain. If
it
is, I'd suggest talking to the Struts 2 developers and see if there's a
way
to enable autowiring (or the struts2-spring-plugin) during unit testing.

Matt

On Fri, May 15, 2009 at 2:36 PM, wkbutler <kent.but...@gmail.com> wrote:


Thanks dusty. Yes, you are correct -- test/resources contains a couple Spring configs that came default with AF2 (thanks Matt & group!) and I
don't
believe I have changed them at all.

Sorry, I should have said - I'm testing our Actions, and the beans are
declared in standard beans xml files.  I verified that the
BaseActionTestCase is pulling in all of our expected appContexts, and
indeed
it is setting AUTOWIRE_BY_NAME explicitly.  Hmmmmm......

Here's an example action bean, FWIW:

<bean id="addressAction" class="com.e3.webapp.action.AddressAction"
scope="prototype"/>

This bean should be picking up an 'addressManager' and a
'localSecurityContext' reference during testing, but it's not. Both collaborators are defined as beans, and the names line up. The same
config
files work when executing jetty:run.

I have not tried used annotations for testing, BTW - ours is still an old
school configuration.

I don't see any bit of the Spring configs under test/resources that look
at
all relevant, either.  It's all specific to User management and
datasource
configuration.

Thanks again -



dusty wrote:

Hey kent,

Autowiring should work for your unit tests. You likely have a spring configuration file in your test/resources directory. If you are using Spring annotations, you can make sure that they are configured to be
scanned in your test applicationContext.xml.

There is a base test class, BaseDaoTestCase that gets things setup and
fires up Spring for your tests.  If you changed your Spring config
file names or locations you need to update this test class since it has the Spring config file locations as a static array of Strings if I
remember correctly.

Other than that what are you trying to wire? What are you trying to
test, DAO, Manager or Controller?

-D

On May 14, 2009, at 8:58 PM, wkbutler wrote:


Hi all -
We're using Spring bean autowiring during the regular execution of
our app.
I believe this is a feature of the ContextLoaderListener which is
specified
in the web.xml.  That works great.

In unit testing however, autowiring is not automatically enabled. Our
Spring contexts are getting picked up from the classpath however,
same as
always.

Does anyone know how I can enable autowiring for unit testing?
Seems like a
configuration to the context currently being used by the unit
tester, but to
be honest I have not yet figured out how the unit testing mojo is
configured.  Thanks for any pointers.

Kent


--
View this message in context:

http://www.nabble.com/Spring-Autowiring-during-Unit-testing-tp23553006s2369p23553006.html
Sent from the AppFuse - User mailing list archive at Nabble.com.


---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscr...@appfuse.dev.java.net
For additional commands, e-mail: users-h...@appfuse.dev.java.net



---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscr...@appfuse.dev.java.net
For additional commands, e-mail: users-h...@appfuse.dev.java.net




--
View this message in context:
http://www.nabble.com/Spring-Autowiring-during-Unit-testing-tp23553006s2369p23566907.html
Sent from the AppFuse - User mailing list archive at Nabble.com.


---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscr...@appfuse.dev.java.net
For additional commands, e-mail: users-h...@appfuse.dev.java.net





--
View this message in context: 
http://www.nabble.com/Spring-Autowiring-during-Unit-testing-tp23553006s2369p23567226.html
Sent from the AppFuse - User mailing list archive at Nabble.com.


---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscr...@appfuse.dev.java.net
For additional commands, e-mail: users-h...@appfuse.dev.java.net


---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscr...@appfuse.dev.java.net
For additional commands, e-mail: users-h...@appfuse.dev.java.net



---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscr...@appfuse.dev.java.net
For additional commands, e-mail: users-h...@appfuse.dev.java.net

Reply via email to