You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@tapestry.apache.org by ca...@ocean7.com on 2004/10/26 22:09:57 UTC

Re: Unit testing w/ Tapestry

Howard provided the
org/apache/tapestry/test/AbstractInstantiator
(it didn't compile, so i made a small change - see attached file)

I use the EasyMock package.

Code snips looks like this

instor = new AbstractInstantiator();
try {
//	instantiate page to test
    page = (OrgEdit) instor.getInstance(Class.forName("OrgEdit"));
} catch (ClassNotFoundException e) {
    logger.error("ClassNotFoundException:" + e.toString());
}
  

MockControl control = MockControl.createControl(IRequestCycle.class);

//	get mock IRequestCycle
IRequestCycle mockcycle = (IRequestCycle) control.getMock();

//	prepare the IEngine
AppEngine ae = new AppEngine();

//	prepare the Visit
Visit v = new Visit();
ae.setVisit(v);

//	expect 1	
mockcycle.getEngine();
control.setReturnValue(ae);
	
//	expect 2
Home mhome = null;
mhome = new Home();
try {
    mhome = (Home) instor.getInstance(Class.forName("org.mnn.member.pages.Home"));
} catch (ClassNotFoundException e) {
    logger.error("ClassNotFoundException:" + e.toString());
}
mockcycle.getPage("MemberHome");
control.setReturnValue(mhome);
	
//	expect 3
mockcycle.activate(mhome);

control.replay();

Organization org = new Organization();
org.setName("New Org");
org.setOrgId(1);

page.setOrg(org);

page.setOrgTypeMap(new ListEditMap());
page.setOrgCatMap(new ListEditMap());

page.submitForm(mockcycle);

//	do your asserts:
assertEquals("Organization successfully inserted", mhome.getMsg());
...

Re: Unit testing w/ Tapestry

Posted by Howard Lewis Ship <hl...@gmail.com>.
An improved version of AbstractInstantiator is part of Tapestry 3.1.


On Thu, 28 Oct 2004 12:49:24 -0600, Dave Smith <di...@gmail.com> wrote:
> Carsten,
> 
> Thanks for the example and guidance. It works great. :)
> 
> Howard,
> 
> Are there any plans to integrate the AbstractInstantiator into
> Tapestry? I don't mind having the code in my project directly, but it
> seems like Tapestry could use (from my naive standpoint) more
> infrastructure for doing unit testing of Tapestry-based apps. Do you
> have any ideas for this that you would like to see implemented but
> haven't had time to?  Regardless, thanks for the great framework! :)
> 
> D.
> 
> 
> 
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: tapestry-user-unsubscribe@jakarta.apache.org
> For additional commands, e-mail: tapestry-user-help@jakarta.apache.org
> 
> 


-- 
Howard M. Lewis Ship
Independent J2EE / Open-Source Java Consultant
Creator, Jakarta Tapestry
Creator, Jakarta HiveMind
http://howardlewisship.com

---------------------------------------------------------------------
To unsubscribe, e-mail: tapestry-user-unsubscribe@jakarta.apache.org
For additional commands, e-mail: tapestry-user-help@jakarta.apache.org


Re: Unit testing w/ Tapestry

Posted by Dave Smith <di...@gmail.com>.
Carsten,

Thanks for the example and guidance. It works great. :)

Howard,

Are there any plans to integrate the AbstractInstantiator into
Tapestry? I don't mind having the code in my project directly, but it
seems like Tapestry could use (from my naive standpoint) more
infrastructure for doing unit testing of Tapestry-based apps. Do you
have any ideas for this that you would like to see implemented but
haven't had time to?  Regardless, thanks for the great framework! :)

D.

---------------------------------------------------------------------
To unsubscribe, e-mail: tapestry-user-unsubscribe@jakarta.apache.org
For additional commands, e-mail: tapestry-user-help@jakarta.apache.org


Re: submit button position

Posted by Jamie Orchard-Hays <ja...@dang.com>.
And not only that...

If you have two buttons, the one that is placed first is the default one 
called when pressing enter from a field to submit the form--though I believe 
this is a browser issue rather than a tapestry issue.

Components are called in order when a form is submitted. The Form Listener 
always gets called last. So, you can put your submit listener there instead 
of in the Submit component and guaranty that it will be called last.

Jamie

----- Original Message ----- 
From: "Markus Wiederkehr" <ma...@gmail.com>
To: "Tapestry users" <ta...@jakarta.apache.org>
Sent: Wednesday, October 27, 2004 10:06 AM
Subject: Re: submit button position


>I assume you use the Submit component and its listener parameter... If
> this is the case the position of the component in the form matters,
> because the listener gets invoked earlier if you place it at the top.
>
> Excerpt from 
> http://jakarta.apache.org/tapestry/doc/ComponentReference/Submit.html:
> "[...] This notification occurs as the component is rewinded, i.e.,
> prior to the Form's listener. [...]"
>
> Markus
>
> On Wed, 27 Oct 2004 15:59:03 +0200, Andreas Vombach
> <an...@psi.ch> wrote:
>> Maybe this sounds strange, but if I move the position of my
>> submit-buttons in a form from the bottom to the top of the form in the
>> html page I get stale links. I restarted sessions etc. but it seems to
>> shift some expected parameter positions. Has anybody experience how to
>> prevent it?
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: tapestry-user-unsubscribe@jakarta.apache.org
> For additional commands, e-mail: tapestry-user-help@jakarta.apache.org
> 


---------------------------------------------------------------------
To unsubscribe, e-mail: tapestry-user-unsubscribe@jakarta.apache.org
For additional commands, e-mail: tapestry-user-help@jakarta.apache.org


Re: submit button position

Posted by Markus Wiederkehr <ma...@gmail.com>.
I assume you use the Submit component and its listener parameter... If
this is the case the position of the component in the form matters,
because the listener gets invoked earlier if you place it at the top.

Excerpt from http://jakarta.apache.org/tapestry/doc/ComponentReference/Submit.html:
"[...] This notification occurs as the component is rewinded, i.e.,
prior to the Form's listener. [...]"

Markus

On Wed, 27 Oct 2004 15:59:03 +0200, Andreas Vombach
<an...@psi.ch> wrote:
> Maybe this sounds strange, but if I move the position of my
> submit-buttons in a form from the bottom to the top of the form in the
> html page I get stale links. I restarted sessions etc. but it seems to
> shift some expected parameter positions. Has anybody experience how to
> prevent it?

---------------------------------------------------------------------
To unsubscribe, e-mail: tapestry-user-unsubscribe@jakarta.apache.org
For additional commands, e-mail: tapestry-user-help@jakarta.apache.org


submit button position

Posted by Andreas Vombach <an...@psi.ch>.
Maybe this sounds strange, but if I move the position of my 
submit-buttons in a form from the bottom to the top of the form in the 
html page I get stale links. I restarted sessions etc. but it seems to 
shift some expected parameter positions. Has anybody experience how to 
prevent it?

Thanks Andreas

---------------------------------------------------------------------
To unsubscribe, e-mail: tapestry-user-unsubscribe@jakarta.apache.org
For additional commands, e-mail: tapestry-user-help@jakarta.apache.org


Re: Unit testing w/ Tapestry

Posted by ca...@ocean7.com.
Sorry, the AbstractInstantiator didn't get through as attachment:


//Copyright 2004 The Apache Software Foundation
//
//Licensed under the Apache License, Version 2.0 (the "License");
//you may not use this file except in compliance with the License.
//You may obtain a copy of the License at
//
//  http://www.apache.org/licenses/LICENSE-2.0
//
//Unless required by applicable law or agreed to in writing, software
//distributed under the License is distributed on an "AS IS" BASIS,
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//See the License for the specific language governing permissions and
//limitations under the License.

package org.apache.tapestry.test;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;

import javassist.CannotCompileException;
import javassist.ClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
import javassist.CtMethod;
import javassist.LoaderClassPath;
import javassist.NotFoundException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.tapestry.ApplicationRuntimeException;
import org.apache.tapestry.IResourceResolver;
import org.apache.tapestry.util.DefaultResourceResolver;

/**
 * Used to instantiate an instance of an otherwise abstract class.
 * Analyzes the class and builds a sub-class where all abstract properties
 * have real implementations.
 */
public class AbstractInstantiator
{
    private static final Log LOG =
        LogFactory.getLog(AbstractInstantiator.class);

    private static class InstantiatorClassLoader extends ClassLoader
    {
        public InstantiatorClassLoader(ClassLoader parent)
        {
            super(parent);
        }
    
        public Class loadClass(String name, byte[] bytecodes)
        {
            Class result = defineClass(name, bytecodes, 0, bytecodes.length);

            resolveClass(result);

            return result;
        }

    }
    private ClassPool _classPool;

    /**
     * Map keyed on abstract Class, value is an enhanced Class.
     */
    private Map _enhancedClasses = new HashMap();
    private InstantiatorClassLoader _loader;

    private Map _primitives = new HashMap();

    private IResourceResolver _resolver;
    private int _uid = 0;

    {
        _primitives.put(boolean.class, "boolean");
        _primitives.put(short.class, "short");
        _primitives.put(byte.class, "byte");
        _primitives.put(char.class, "char");
        _primitives.put(int.class, "int");
        _primitives.put(long.class, "long");
        _primitives.put(float.class, "float");
        _primitives.put(double.class, "double");
    }

    public AbstractInstantiator()
    {
        this(new DefaultResourceResolver());
    }
    
    public AbstractInstantiator(IResourceResolver resolver)
    {
        _resolver = resolver;

        ClassLoader parentLoader = _resolver.getClassLoader();

        _loader = new InstantiatorClassLoader(parentLoader);

        _classPool = new ClassPool(null);

        ClassPath path = new LoaderClassPath(parentLoader);

        _classPool.appendClassPath(path);
    }

    private void addAccessorMethod(
        CtClass ctClass,
        PropertyDescriptor pd,
        String attributeName)
    {
        String methodName =
            getMethodName(pd.getReadMethod(), "get", pd.getName());

        CtClass returnType = getCtClass(pd.getPropertyType());

        CtMethod method =
            new CtMethod(returnType, methodName, new CtClass[0], ctClass);

        try
        {
            method.setBody("{ return " + attributeName + "; }");
            method.setModifiers(Modifier.PUBLIC);

            ctClass.addMethod(method);
        }
        catch (CannotCompileException ex)
        {
            throw new ApplicationRuntimeException(ex);
        }
    }

    private void addDefaultConstructor(CtClass ctClass)
    {
    }

    private void addField(CtClass ctClass, String fieldName, Class fieldType)
    {
        CtClass ctType = getCtClass(fieldType);

        try
        {
            CtField field = new CtField(ctType, fieldName, ctClass);

            field.setModifiers(Modifier.PRIVATE);

            ctClass.addField(field);
        }
        catch (CannotCompileException ex)
        {
            throw new ApplicationRuntimeException(ex);
        }
    }

    private void addMissingProperties(CtClass ctClass, BeanInfo info)
    {
        PropertyDescriptor[] pd = info.getPropertyDescriptors();

        for (int i = 0; i < pd.length; i++)
            addMissingProperty(ctClass, pd[i]);

    }

    private void addMissingProperty(CtClass ctClass, PropertyDescriptor pd)
    {
        Method readMethod = pd.getReadMethod();
        Method writeMethod = pd.getWriteMethod();

        boolean abstractRead = isAbstract(readMethod);
        boolean abstractWrite = isAbstract(writeMethod);

        if (!(abstractRead || abstractWrite))
            return;

        String attributeName = "_$" + pd.getName();
        Class propertyType = pd.getPropertyType();

        addField(ctClass, attributeName, propertyType);

        if (abstractRead)
            addAccessorMethod(ctClass, pd, attributeName);

        if (abstractWrite)
            addMutatorMethod(ctClass, pd, attributeName);

    }

    private void addMutatorMethod(
        CtClass ctClass,
        PropertyDescriptor pd,
        String attributeName)
    {
        String methodName =
            getMethodName(pd.getWriteMethod(), "set", pd.getName());

        CtClass parameterType = getCtClass(pd.getPropertyType());

        CtMethod method =
            new CtMethod(
                CtClass.voidType,
                methodName,
                new CtClass[] { parameterType },
                ctClass);

        try
        {
            method.setBody("{ " + attributeName + " = $1; }");
            method.setModifiers(Modifier.PUBLIC);

            ctClass.addMethod(method);
        }
        catch (CannotCompileException ex)
        {
            throw new ApplicationRuntimeException(ex);
        }
    }

    private String constructNewClassName(Class inputClass)
    {
        return inputClass.getName() + "$enhance_" + _uid++;
    }

    private Class createEnhancedClass(Class inputClass)
    {
        if (inputClass.isInterface())
            throw new IllegalArgumentException(
                "Can not create instance of interface "
                    + inputClass.getName()
                    + ".");

        if (!Modifier.isAbstract(inputClass.getModifiers()))
        {
            LOG.error("Class " + inputClass.getName() + " is not abstract.");
            return inputClass;
        }

        BeanInfo info = null;

        try
        {
            info = Introspector.getBeanInfo(inputClass, Object.class);
        }
        catch (IntrospectionException ex)
        {
            throw new ApplicationRuntimeException(
                "Unable to introspect class "
                    + inputClass.getName()
                    + ": "
                    + ex.getMessage(),
                ex);
        }

        String newName = constructNewClassName(inputClass);

        CtClass newClass =
            _classPool.makeClass(newName, getCtClass(inputClass));

        addMissingProperties(newClass, info);

        addDefaultConstructor(newClass);

        try
        {
            // newClass.writeFile();

	    // changed by Carst 20040611
//             byte[] bytecode = _classPool.write(newName);
//             return _loader.loadClass(newName, bytecode);
	    return _classPool.get(newName).toClass();
        }
        catch (Exception ex)
        {
            throw new ApplicationRuntimeException(
                "Unable to instantiate enhanced subclass "
                    + newClass.getName()
                    + ": "
                    + ex.getMessage(),
                ex);
        }

    }

    private CtClass getCtClass(Class inputClass)
    {
        String name = getCtName(inputClass);

        try
        {
            return _classPool.get(name);
        }
        catch (NotFoundException ex)
        {
            throw new ApplicationRuntimeException(ex);
        }
    }

    private String getCtName(Class inputClass)
    {
        if (inputClass.isArray())
            return getCtName(inputClass.getComponentType()) + "[]";

        if (inputClass.isPrimitive())
            return (String) _primitives.get(inputClass);

        return inputClass.getName();
    }

    public Class getEnhancedClass(Class inputClass)
    {
        Class result = (Class) _enhancedClasses.get(inputClass);

        if (result == null)
        {
            result = createEnhancedClass(inputClass);
            _enhancedClasses.put(inputClass, result);
        }

        return result;
    }

    /**
     * Given a particular abstract class; will create an instance of that class. A subclass
     * is created with all abstract properties filled in with ordinary implementations.
     */
    public Object getInstance(Class abstractClass) {
        Class enhancedClass = getEnhancedClass(abstractClass);

        try {
            return enhancedClass.newInstance();
        } catch (Exception ex) {
            throw new ApplicationRuntimeException(
						  "Unable to instantiate instance of "
						  + enhancedClass.getName()
						  + ": "
						  + ex.getMessage(),
						  ex);
        }
    }

    private String getMethodName(Method m, String prefix, String propertyName) {
        if (m != null)
            return m.getName();

        StringBuffer buffer = new StringBuffer(prefix);

        buffer.append(propertyName.substring(0, 1).toUpperCase());
        buffer.append(propertyName.substring(1));

        return buffer.toString();
    }

    private boolean isAbstract(Method m)
    {
        return m == null || Modifier.isAbstract(m.getModifiers());
    }
}

-- 
Carsten Heinrigs
Ocean-7 Development
Tel: 212 533-8460



---------------------------------------------------------------------
To unsubscribe, e-mail: tapestry-user-unsubscribe@jakarta.apache.org
For additional commands, e-mail: tapestry-user-help@jakarta.apache.org


Re: Unit testing w/ Tapestry

Posted by Dave Smith <di...@gmail.com>.
Was there supposed to be an attachment with your first email?

D.


On Tue, 26 Oct 2004 16:18:50 -0600, Dave Smith <di...@gmail.com> wrote:
> On Tue, 26 Oct 2004 16:09:57 -0400, carst@ocean7.com <ca...@ocean7.com> wrote:
> 
> 
> > Howard provided the
> > org/apache/tapestry/test/AbstractInstantiator
> > (it didn't compile, so i made a small change - see attached file)
> >
> > I use the EasyMock package.
> >
> > Code snips looks like this
> >
> > instor = new AbstractInstantiator();
> > try {
> > //      instantiate page to test
> >     page = (OrgEdit) instor.getInstance(Class.forName("OrgEdit"));
> > } catch (ClassNotFoundException e) {
> >     logger.error("ClassNotFoundException:" + e.toString());
> > }
> >
> > MockControl control = MockControl.createControl(IRequestCycle.class);
> >
> > //      get mock IRequestCycle
> > IRequestCycle mockcycle = (IRequestCycle) control.getMock();
> >
> > //      prepare the IEngine
> > AppEngine ae = new AppEngine();
> >
> > //      prepare the Visit
> > Visit v = new Visit();
> > ae.setVisit(v);
> >
> > //      expect 1
> > mockcycle.getEngine();
> > control.setReturnValue(ae);
> >
> > //      expect 2
> > Home mhome = null;
> > mhome = new Home();
> > try {
> >     mhome = (Home) instor.getInstance(Class.forName("org.mnn.member.pages.Home"));
> > } catch (ClassNotFoundException e) {
> >     logger.error("ClassNotFoundException:" + e.toString());
> > }
> > mockcycle.getPage("MemberHome");
> > control.setReturnValue(mhome);
> >
> > //      expect 3
> > mockcycle.activate(mhome);
> >
> > control.replay();
> >
> > Organization org = new Organization();
> > org.setName("New Org");
> > org.setOrgId(1);
> >
> > page.setOrg(org);
> >
> > page.setOrgTypeMap(new ListEditMap());
> > page.setOrgCatMap(new ListEditMap());
> >
> > page.submitForm(mockcycle);
> >
> > //      do your asserts:
> > assertEquals("Organization successfully inserted", mhome.getMsg());
> > ...
> >
> > 
> > Carsten Heinrigs
> > Ocean-7 Development
> > Tel: 212 533-8460
> >
> >
> > ---------------------------------------------------------------------
> > To unsubscribe, e-mail: tapestry-user-unsubscribe@jakarta.apache.org
> > For additional commands, e-mail: tapestry-user-help@jakarta.apache.org
> >
> >
>

---------------------------------------------------------------------
To unsubscribe, e-mail: tapestry-user-unsubscribe@jakarta.apache.org
For additional commands, e-mail: tapestry-user-help@jakarta.apache.org


Re: Unit testing w/ Tapestry

Posted by Dave Smith <di...@gmail.com>.
On Tue, 26 Oct 2004 16:09:57 -0400, carst@ocean7.com <ca...@ocean7.com> wrote:
> Howard provided the
> org/apache/tapestry/test/AbstractInstantiator
> (it didn't compile, so i made a small change - see attached file)
> 
> I use the EasyMock package.
> 
> Code snips looks like this
> 
> instor = new AbstractInstantiator();
> try {
> //      instantiate page to test
>     page = (OrgEdit) instor.getInstance(Class.forName("OrgEdit"));
> } catch (ClassNotFoundException e) {
>     logger.error("ClassNotFoundException:" + e.toString());
> }
> 
> MockControl control = MockControl.createControl(IRequestCycle.class);
> 
> //      get mock IRequestCycle
> IRequestCycle mockcycle = (IRequestCycle) control.getMock();
> 
> //      prepare the IEngine
> AppEngine ae = new AppEngine();
> 
> //      prepare the Visit
> Visit v = new Visit();
> ae.setVisit(v);
> 
> //      expect 1
> mockcycle.getEngine();
> control.setReturnValue(ae);
> 
> //      expect 2
> Home mhome = null;
> mhome = new Home();
> try {
>     mhome = (Home) instor.getInstance(Class.forName("org.mnn.member.pages.Home"));
> } catch (ClassNotFoundException e) {
>     logger.error("ClassNotFoundException:" + e.toString());
> }
> mockcycle.getPage("MemberHome");
> control.setReturnValue(mhome);
> 
> //      expect 3
> mockcycle.activate(mhome);
> 
> control.replay();
> 
> Organization org = new Organization();
> org.setName("New Org");
> org.setOrgId(1);
> 
> page.setOrg(org);
> 
> page.setOrgTypeMap(new ListEditMap());
> page.setOrgCatMap(new ListEditMap());
> 
> page.submitForm(mockcycle);
> 
> //      do your asserts:
> assertEquals("Organization successfully inserted", mhome.getMsg());
> ...
> 
> 
> Carsten Heinrigs
> Ocean-7 Development
> Tel: 212 533-8460
> 
> 
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: tapestry-user-unsubscribe@jakarta.apache.org
> For additional commands, e-mail: tapestry-user-help@jakarta.apache.org
> 
>

---------------------------------------------------------------------
To unsubscribe, e-mail: tapestry-user-unsubscribe@jakarta.apache.org
For additional commands, e-mail: tapestry-user-help@jakarta.apache.org