How to Test Struts 2 Actions Without a Container
来源:互联网 发布:数据分析师做什么 编辑:程序博客网 时间:2024/04/28 20:50
I recently worked on a project that used Struts 2.1 and the Spring framework integration plug-in. There are sporadic examples of how to test Struts actions in this context but none of them seemed to be up-to-date or they required you to start a servlet container to run the tests. None of the examples I found also showed you how to leverage Spring's transactional JUnit helper classes which enable you to run tests in a transaction and automatically rollback after each test. This is a huge benefit if you're working with a project where lots of test data is needed.
Since none of the existing examples seemed to address my needs, I put together a solution using a combination of mock objects and Spring's test helper classes.
Here's what you need:
- Java 5 or above
- Struts 2.1.8.1 (including the junit and spring integration plug-ins)
- Spring Framework 2.5.6 (including spring)
- JUnit 4.4
The versions outlined above are the ones I used so I know they work. You may be able to use slightly different versions.
The approach I use here will have the following benefits:
- Little to no manual setup and configuration required for tests.
- Since the tests are transactional, there's no need to manually clean up test data. Each time the tests are run, the database returns to the state it was in before running a test.
- Easy overrides. Using a combination of annotations and helper methods provided by JUnit, Struts and Spring superclasses, you can easily modify the default behavior of tests.
Let's get started with some code.
Extending StrutsTestCase
I started with a base class called StrutsSpringTransactionalTests which extends the StrutsSpringTestCase. The StrutsSpringTestCase is bundled with the struts2-junit-plugin.jar and provides functionality for adding the Spring context to the servlet context (normally achieved via the web descriptor).
...package imports omitted for brevity...@RunWith(SpringJUnit4ClassRunner.class)@TestExecutionListeners({TransactionalTestExecutionListener.class,DependencyInjectionTestExecutionListener.class,DirtiesContextTestExecutionListener.class})@Transactionalpublic abstract class StrutsSpringTransactionalTests extends StrutsTestCase implements ApplicationContextAware {protected final Log logger = LogFactory.getLog(getClass());protected DataSource dataSource;/*** The {@link org.springframework.context.ApplicationContext} that was injected into this test instance* via {@link #setApplicationContext(org.springframework.context.ApplicationContext)}.*/protected ApplicationContext applicationContext;public abstract void setDataSource(DataSource dataSource);/*** Set the {@link ApplicationContext} to be used by this test instance,* provided via {@link ApplicationContextAware} semantics.*/public final void setApplicationContext(final ApplicationContext applicationContext) {this.applicationContext = applicationContext;}/*** The SimpleJdbcTemplate that this base class manages, available to subclasses.*/protected SimpleJdbcTemplate simpleJdbcTemplate;private String sqlScriptEncoding;/*** Specify the encoding for SQL scripts, if different from the platform encoding.** @see #executeSqlScript*/public void setSqlScriptEncoding(String sqlScriptEncoding) {this.sqlScriptEncoding = sqlScriptEncoding;}/*** Count the rows in the given table.** @param tableName table name to count rows in* @return the number of rows in the table*/protected int countRowsInTable(String tableName) {return SimpleJdbcTestUtils.countRowsInTable(this.simpleJdbcTemplate, tableName);}/*** Convenience method for deleting all rows from the specified tables.* Use with caution outside of a transaction!** @param names the names of the tables from which to delete* @return the total number of rows deleted from all specified tables*/protected int deleteFromTables(String... names) {return SimpleJdbcTestUtils.deleteFromTables(this.simpleJdbcTemplate, names);}/*** Execute the given SQL script. Use with caution outside of a transaction!* <p>The script will normally be loaded by classpath. There should be one statement* per line. Any semicolons will be removed. <b>Do not use this method to execute* DDL if you expect rollback.</b>** @param sqlResourcePath the Spring resource path for the SQL script* @param continueOnError whether or not to continue without throwing an* exception in the event of an error* @throws org.springframework.dao.DataAccessException* if there is an error executing a statement* and continueOnError was <code>false</code>*/protected void executeSqlScript(String sqlResourcePath, boolean continueOnError)throws DataAccessException {Resource resource = this.applicationContext.getResource(sqlResourcePath);SimpleJdbcTestUtils.executeSqlScript(this.simpleJdbcTemplate, new EncodedResource(resource, this.sqlScriptEncoding), continueOnError);}}
Wiring in Spring Configuration and Setting DataSource
From here, extend the StrutsSpringTransactionalTests class:
*** package and imports omitted for brevity ***@ContextConfiguration(locations = {"file:src/main/webapp/WEB-INF/appContext.xml","classpath:appTestContext.xml"})public abstract class AppTransactionalStrutsTestCase extends StrutsSpringTransactionalTests {@Beforepublic void onSetUp() throws Exception {super.setUp();setupAction();}protected abstract void setupAction() throws DataAccessException;@Overrideprotected void setupBeforeInitDispatcher() throws Exception {servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, applicationContext);}protected ActionProxy initActionProxy(String uri) {ActionProxy proxy = getActionProxy(uri);ActionContext actionContext = proxy.getInvocation().getInvocationContext();actionContext.setSession(new HashMap<String, Object>());return proxy;}@Autowiredpublic void setDataSource(@Qualifier(value = "appDataSource") DataSource appDataSource) {this.dataSource = appDataSource;this.simpleJdbcTemplate = new SimpleJdbcTemplate(appDataSource);}}
You can optionally combine the above two classes. I separated them in order to reuse StrutsSpringTransactionalTests across different Struts applications.
Struts Action Test Implementation
Finally, here's an example of a test class which leverages AppTransactionalStrutsTestCase:
public class SearchTest extends AppTransactionalStrutsTestCase {private static final String BOND_NAME = "ACE 2006-ASP6 A2C";@Overrideprotected void setupAction() throws DataAccessException {}@Testpublic void testSearchByBondName() throws Exception {request.addParameter("searchRequest.bondName", BOND_NAME);ActionProxy proxy = initActionProxy("/trader/searchPostBack!postBack.action");Assert.assertEquals(Action.INPUT, proxy.execute());}}
When the above test runs, any database operations are automagically rolled back.
- How to Test Struts 2 Actions Without a Container
- How to Interactively Create a Docker Container
- how to test a file 's existence
- How to create a test plan?
- How to compile a APR test script
- How To Create A Struts 2 Web Application
- How To Create A Struts 2 Web Application
- How To Login To Windows Without A Password
- Struts 2 Actions
- How to build a struts application
- How to Get Code into a Docker Container
- How to Lead a Happier and Successful Test Team – Test Leadership Part 2
- How to Run a Python Script Without Python
- How to Create a SharePoint 2010 Project Without SharePoint Server
- sencha touch出现错误“[ERROR][Ext.Container#onFirstItemAdd] Adding a card to a tab container without spe”
- How to add a test plan, package to Android CTS?
- How to add a test plan, package to Android CTS?
- How to add a test plan, package to Android CTS?
- [ASP.NET MVC 小牛之路]15 - Model Binding
- iPhone开发中的国际化
- NSLocalizedString 实现国际化
- 利用ssh传输文件
- 直接拿来用!最火的Android开源项目(一)
- How to Test Struts 2 Actions Without a Container
- Unit Testing A Struts 2 Action Class - Struts 2 JUnit Plugin
- Unit Testing Struts 2 Action Classes Posted At : September 4, 2009 10:12 AM | Posted By : Bruce Phil
- sql主表查询,附带显示子表记录数量。
- We Are the Champion---The Audacity of Hope
- 深入浅出图解C#堆与栈 C# Heap(ing) VS Stack(ing) 第五节 引用类型复制问题及用克隆接口ICloneable修复
- 传多个值到其它页面的方法
- Mix of blocking and non-blocking assignments to variable <en> is not a recommended coding practice.
- C标准库 memset 的一个问题