且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

Java反射机制在UI自动化测试中的一个应用

更新时间:2022-09-11 10:09:01

源代码如下:

package com.sap.crm.ui.core.pages;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.sap.crm.ui.core.tags.ContainerElement;
import com.sap.crm.ui.core.tags.Element;
import com.sap.crm.ui.core.tags.ElementFactory;
import com.sap.crm.ui.core.tags.ParentElement;
import com.sap.crm.ui.core.tags.SubFactory;
import com.sap.crm.ui.core.tags.UISession;

public class AbstractPage {

    protected final UISession session;
    protected ParentElement container;

    public AbstractPage(final UISession session) {
        super();
        this.session = session;
        init();
    }

    protected void init() {
        final List<Field> fields = new ArrayList<Field>();
        final List<Field> subFields = new ArrayList<Field>();
        Map<Class<? extends SubObject>, Map<String, SubFactory<? extends SubObject>>> subFactories = new HashMap<>();

        Page pageAnnotation = this.getClass().getAnnotation(Page.class);
        Location defaultLocation = null;
        if (pageAnnotation != null) {
            defaultLocation = pageAnnotation.defaultLocation();
        }

        // Retrieve all fields/members that are declared as
        // public/protected/private.
        for (final Field field : this.getClass().getDeclaredFields()) {

            if (!field.isAccessible())
                field.setAccessible(true);

            final Class<?> type = field.getType();
            if (Element.class.isAssignableFrom(type) || type == String.class) {
                if (field.getAnnotation(Container.class) != null) {
                    fields.add(0, field);
                } else {
                    fields.add(field);
                }

            } else if (AbstractPage.class.isAssignableFrom(type)) {
                // element is inheriting from AbstractPage
                createSubPage(field, type);

            } else if (SubObject.class.isAssignableFrom(type)) {
                // subFactory e.g. for table columns
                subFields.add(field);
            }
        }

        // Now consider all fields that have a PageElement annotation and create
        // the concrete element instances via factory
        for (final Field field : fields) {
            PageElement pageElement = resolveAnnotations(field);

            if (pageElement != null) {
                final Class<?> type = field.getType();
                if (type == String.class) {
                    // Just a versioned ID
                    setField(field, pageElement.id());
                    continue;
                }

                // Determine element type
                @SuppressWarnings("unchecked")
                Class<? extends Element> fieldType = (Class<? extends Element>) type;
                if (pageElement.type() != Element.class) {
                    if (!fieldType.isAssignableFrom(pageElement.type())) {
                        throw new IllegalArgumentException();
                    }

                    fieldType = pageElement.type();
                }

                // Get factory dependent from location
                Element element;
                ElementFactory factory = null;

                factory = resolveLocation(factory, pageElement, defaultLocation);

                // Check if proxy needed
                if (ElementFactory.isConcreteType(fieldType)) {
                    // Concrete type is known, no proxy needed
                    element = factory.element(fieldType, pageElement.id());
                } else {
                    // Final Type is not known, need a proxy
                    element = factory.proxy(pageElement.id());
                }

                if (field.getAnnotation(Container.class) != null) {
                    container = (ContainerElement) element;
                }

                setField(field, element);

                // Remember subFactories
                if (SubFactory.class.isAssignableFrom(fieldType)) {
                    registerSubFactory(subFactories, field, fieldType, element);
                }

            }
        }

        // Instantiate subObject fields from factories
        for (Field subField : subFields) {
            PageElement pageElement = resolveAnnotations(subField);

            // Get factory
            Map<String, SubFactory<? extends SubObject>> subFactoriesForType = subFactories
                    .get(subField.getType());
            if (subFactoriesForType == null) {
                throw new IllegalArgumentException("Subfactory for type "
                        + subField.getType() + " could not be found on page");
            }
            SubFactory<? extends SubObject> subFactory = subFactoriesForType
                    .get(pageElement.subFactoryName());
            if (subFactory == null) {
                throw new IllegalArgumentException("Subfactory for type "
                        + subField.getType() + " with name "
                        + pageElement.subFactoryName()
                        + " could not be found on page");
            }

            // Get parametrization for generic types
            ParameterizedType genericType = (ParameterizedType) subField
                    .getGenericType();
            Type[] actualTypeArguments = genericType.getActualTypeArguments();

            // Produce and assign
            Object subObject = subFactory.produce(pageElement.id(),
                    actualTypeArguments);
            try {
                subField.set(this, subObject);
            } catch (final IllegalAccessException e) {
                throw new IllegalStateException(e);
            }
        }
    }

    private void setField(final Field field, Object value) {
        try {
            field.set(this, value);
        } catch (final IllegalAccessException e) {
            throw new IllegalStateException(e);
        }
    }

    private void createSubPage(final Field field, final Class<?> type) {
        @SuppressWarnings("unchecked")
        final Class<? extends AbstractPage> pageType = (Class<AbstractPage>) type;
        // Create sub pages
        try {
            final Constructor<? extends AbstractPage> constructor = pageType
                    .getConstructor(UISession.class);
            try {
                final AbstractPage page = constructor.newInstance(session);
                field.set(this, page);
            } catch (final IllegalArgumentException e) {
                e.printStackTrace();
            } catch (final InstantiationException e) {
                e.printStackTrace();
            } catch (final IllegalAccessException e) {
                e.printStackTrace();
            } catch (final InvocationTargetException e) {
                e.printStackTrace();
            }
        } catch (final NoSuchMethodException e) {
            // Ignore it, has to be coded manually
        }
    }

    private ElementFactory resolveLocation(ElementFactory factory,
            PageElement pageElement, Location defaultLocation) {
        Location location = pageElement.location();
        if (location == Location.DEFAULT && defaultLocation != null) {
            location = defaultLocation;
        }

        switch (location) {
        case ROOT:
            factory = session.ui();
            break;
        case CONTAINER:
            factory = container.contained();
            break;
        case DEFAULT:
            if (container != null) {
                factory = container.contained();
            } else {
                factory = session.workArea();
            }
            break;
        case TOOLBAR:
            factory = session.getToolbarFactory();
            break;
        case WORK_AREA:
            factory = session.workArea();
            break;
        }
        return factory;
    }

    private void registerSubFactory(
            Map<Class<? extends SubObject>, Map<String, SubFactory<? extends SubObject>>> subFactories,
            final Field field, Class<? extends Element> fieldType,
            Element element) {

        @SuppressWarnings("unchecked")
        SubFactory<? extends SubObject> subFactory = (SubFactory<? extends SubObject>) element;

        // Determine Name
        com.sap.crm.ui.core.pages.SubFactory annotation = field
                .getAnnotation(com.sap.crm.ui.core.pages.SubFactory.class);
        String subFactoryName = (annotation != null) ? annotation.name() : "";

        Class<? extends SubObject> targetType = findSubObjectFactoryTargetType(fieldType);
        Map<String, SubFactory<? extends SubObject>> subFactoriesForType = subFactories
                .get(targetType);
        if (subFactoriesForType == null) {
            subFactoriesForType = new HashMap<>();
            subFactories.put(targetType, subFactoriesForType);
        }

        if (subFactoriesForType.put(subFactoryName, subFactory) != null) {
            throw new IllegalStateException(
                    "Ambiguous subObjectFactories on page for type "
                            + targetType + " with name " + subFactoryName);
        }
    }

    @SuppressWarnings("unchecked")
    private Class<? extends SubObject> findSubObjectFactoryTargetType(
            Class<? extends Element> fieldType) {
        Type[] genericInterfaces = fieldType.getGenericInterfaces();
        for (Type intf : genericInterfaces) {
            if (intf instanceof ParameterizedType
                    && ((ParameterizedType) intf).getRawType() == SubFactory.class) {
                Type[] actualTypeArguments = ((ParameterizedType) intf)
                        .getActualTypeArguments();
                Type type = actualTypeArguments[0];
                Class<? extends SubObject> targetType;
                if (type instanceof ParameterizedType) {
                    targetType = (Class<? extends SubObject>) ((ParameterizedType) type)
                            .getRawType();

                } else {
                    targetType = (Class<? extends SubObject>) type;
                }
                return targetType;
            }
        }
        throw new IllegalArgumentException();
    }

    private PageElement resolveAnnotations(final Field field) {
        // Fetch annotations
        final Versioned versionedAnnotation = field
                .getAnnotation(Versioned.class);
        PageElement pageElement = field.getAnnotation(PageElement.class);

        // Process versions
        if (versionedAnnotation != null) {
            int highestVersion = 0;
            for (final Version version : versionedAnnotation.versions()) {
                if (version.majorVersion() >= session.getMajorVersion()
                        && version.majorVersion() > highestVersion) {
                    pageElement = version.element();
                    highestVersion = version.majorVersion();
                }
            }
        }
        return pageElement;
    }
}