Funnily enough I spent Tuesday working out a solution for this kind of
issue for a Cocoon Action I'm working on at the moment.
I went for a 'mock' object approach [see
<http://mockobjects.sourceforge.net/endotesting.html>], creating mock
versions of (for my case) Redirector, Request, Session, Source and
SourceResolver. These all represent the simplest possible implementations
of the interfaces, which pass back results that I pre-set with additional
set methods. I like this approach because it's very simple, very flexible
and transparent, and the mock objects can potentially grow in the source
base alongside the test cases.
Here's an example of a bit of one of the resulting testcases:
> public class SessionHandlerTest extends TestCase {
>
> private SessionHandler handler;
> private SessionMock session;
> private Redirector redirector;
> private SourceResolver sourceResolver;
> private RequestMock request;
> private Parameters parameters;
> private Map objectModel;
>
> public SessionHandlerTest(String name) {
> super(name);
> }
>
> public void setUp() {
> this.handler = new SessionHandler();
> this.session = new SessionMock();
> this.redirector = new RedirectorMock();
> this.sourceResolver = new SourceResolverMock();
> this.request = new RequestMock();
> this.parameters = new Parameters();
> this.objectModel = new HashMap();
> this.objectModel.put(Constants.REQUEST_OBJECT, this.request);
> }
>
> /**
> * Check that a form field of "login=sessionHandler" is required for
> successful
> * login.
> */
> public void testActLoginSessionHandlerField() throws Exception {
> setupValidLoginAttempt();
> this.request.mockRemoveParameter("login");
> Map map = handler.act(this.redirector, this.sourceResolver, this.
> objectModel, "src", this.parameters);
> assertNull("Action should return null to indicate rejected login"
> , map);
> }
>
>
> private void setupValidLoginAttempt() {
> this.request.mockSetParameter("login","sessionHandler");
> this.request.mockSetParameter("username","valid");
> this.request.mockSetParameter("password","user");
> this.parameters.setParameter("accept-login","true");
> this.parameters.setParameter("require-secure-login","true");
> this.parameters.setParameter("required-login-
> parameters","username,password");
> this.session.setAttribute("sessionhandler/original_uri","testpage"
> );
> this.request.mockSetSecure(true);
> this.request.mockSetSession(this.session);
> }
My personal feeling is that setting up specialized TestCases for testing,
whilst it may simplify the code of tests for people familiar with the code,
removes transparency and adds a level of complexity into the test cases
that could make them harder to maintain. e.g. you need to look at the
definition of the new TestCase in order to determine what the pre-set
state is and then learn the new methods for altering it. With plain Mock
classes you have to explicitly setup everything in the TestCase which may
require more lines of code (but then you can easily do a copy and paste
job from a previous test case), however, you end up with a test case that
is self contained and self documenting. The only new methods you need to
learn are additional methods in the mock classes that provide setup or
validation functionality.
Stuart.
PS. I'm intending feeding back the mock objects to Cocoon if folk are
interested.
On Thursday, August 9, 2001, at 06:31 am, giacomo wrote:
>
> I've followed the discussion about dropping Testlet in favor of JUnit
> with great interest.
>
> What I think is missing in this area of unit tests of Avalon
> components are specialized TestCases.
>
> The idea came when I was looking at the DataSourceTestCase (and wanted
> to adapt it for my own components). When one needs to test Components
> you'll mostly need to create a logger (Loggable), maybe a Context
> (Contextualizable), a Configuration object (Configurable) and sometimes
> also a ComponentManager (Composer). And at least here where you have to
> test a Composer you probably wish to have a CM setup easily. This is
> what the aforementioned TestCases should set up for you.
>
> If there is interest I'd like to contribute specialized JUnit
> TestCases for Avalon. I initially thought of two TestCases:
>
> FrameworkTestCase (uses framework.component.DefaultComponentManager)
> ExcaliburTestCase (uses excalibur.component.ExcaliburComponentManager)
>
> The discussion I'd like to make is about how things like Context,
> Configuration (be it an already created Configuration object, a String
> or a File containing the configuration) and the roles got passed into
> the TestCase for setup of the CM. The possibilities are:
>
> Constructor:
> This would set it up once for every test in the class but
> gives no possibility to change it (ie. Configuration,
> Context) for different tests.
>
> separate method:
> This would allow to use it as a test case itself and it
> could be called every time you need to change something.
>
> setup/tearDown method:
> This would set up the CM for every test (including disposing
> of the component itself in the tearDown method) but also no
> way to use different configs for different tests in the same
> TestCase.
>
> Thoughts?
>
> Giacomo
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: [EMAIL PROTECTED]
> For additional commands, e-mail: [EMAIL PROTECTED]
>
-------------------------------------------------------------------------
Stuart Roebuck [EMAIL PROTECTED]
Lead Developer Java, XML, MacOS X, XP, etc.
ADOLOS <http://www.adolos.com/>
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]