开发者

Suggestions on extending fit.RowFixture and fit.TypeAdapter so that I can bind/invoke on a class that keeps attrs in a map

TLDR: I'd like to know how to extend fit.TypeAdaptor so that I can invoke a method that expects parameters as default implementation of TypeAdaptor invokes the binded (bound ?) method by reflection and assumes it's a no-param method...

Longer version - I'm using fit to build a test harness for my system (a service that returns a sorted list of custom objects). In order to verify the system, I thought I'd use fit.RowFixture to assert attributes of the list items.

Since RowFixture expects the data to be either a public attribute or a public method, I thought of using a wrapper over my custom object (say InstanceWrapper) - I also tried to implement the suggestion given in this previous thread about formatting data in RowFixture.

The trouble is that my custom object has around 41 attributes and I'd like to provide testers with the option of开发者_Python百科 choosing which attributes they want to verify in this RowFixture. Plus, unless I dynamically add fields/methods to my InstanceWrapper class, how will RowFixture invoke either of my getters since both expect the attribute name to be passed as a param (code copied below) ? I extended RowFixture to bind on my method but I'm not sure how to extend TypeAdaptor so that it invokes with the attr name.. Any suggestions ?

public class InstanceWrapper {

    private Instance instance;
    private Map<String, Object> attrs;
    public int index;

    public InstanceWrapper() {
        super();
    }

    public InstanceWrapper(Instance instance) {
        this.instance = instance;
        init(); // initialise map
    }

    private void init() {
        attrs = new HashMap<String, Object>();
        String attrName;
        for (AttrDef attrDef : instance.getModelDef().getAttrDefs()) {
            attrName = attrDef.getAttrName();
            attrs.put(attrName, instance.getChildScalar(attrName));
        }
    }

    public String getAttribute(String attr) {
        return attrs.get(attr).toString();
    }

    public String description(String attribute) {
        return instance.getChildScalar(attribute).toString();
    }
}

public class MyDisplayRules extends fit.RowFixture {

    @Override
    public Object[] query() {
        List<Instance> list = PHEFixture.hierarchyList;
        return convertInstances(list);
    }

    private Object[] convertInstances(List<Instance> instances) {
        Object[] objects = new Object[instances.size()];
        InstanceWrapper wrapper;
        int index = 0;
        for (Instance instance : instances) {
            wrapper = new InstanceWrapper(instance);
            wrapper.index = index;
            objects[index++] = wrapper;
        }
        return objects;
    }

    @Override
    public Class getTargetClass() {
        return InstanceWrapper.class;
    }

    @Override
    public Object parse(String s, Class type) throws Exception {
        return super.parse(s, type);
    }

    @Override
    protected void bind(Parse heads) {

        columnBindings = new TypeAdapter[heads.size()];
        for (int i = 0; heads != null; i++, heads = heads.more) {
            String name = heads.text();
            String suffix = "()";
            try {
                if (name.equals("")) {
                    columnBindings[i] = null;
                } else if (name.endsWith(suffix)) {
                    columnBindings[i] = bindMethod("description", name.substring(0, name.length()
                            - suffix.length()));
                } else {
                    columnBindings[i] = bindField(name);
                }
            } catch (Exception e) {
                exception(heads, e);
            }
        }
    }

    protected TypeAdapter bindMethod(String name, String attribute) throws Exception {
        Class partypes[] = new Class[1];
        partypes[0] = String.class;

        return PHETypeAdaptor.on(this, getTargetClass().getMethod("getAttribute", partypes), attribute);
    }
}


For what it's worth, here's how I eventually worked around the problem:

I created a custom TypeAdapter (extending TypeAdapter) with the additional public attribute (String) attrName. Also:

@Override
public Object invoke() throws IllegalAccessException, InvocationTargetException {

    if ("getAttribute".equals(method.getName())) {
        Object params[] = { attrName };
        return method.invoke(target, params);
    } else {
        return super.invoke();
    }
}

Then I extended fit.RowFixture and made the following overrides:

  • public getTargetClass() - to return my class reference
  • protected TypeAdapter bindField(String name) throws Exception - this is a protected method in ColumnFixture which I modified so that it would use my class's getter method:

    @Override
    protected TypeAdapter bindField(String name) throws Exception {
    
        String fieldName = camel(name);
    
        // for all attributes, use method getAttribute(String)
        Class methodParams[] = new Class[1];
        methodParams[0] = String.class;
    
        TypeAdapter a = TypeAdapter.on(this, getTargetClass().getMethod("getAttribute", methodParams));
    
        PHETypeAdapter pheAdapter = new PHETypeAdapter(fieldName);
        pheAdapter.target = a.target;
        pheAdapter.fixture = a.fixture;
        pheAdapter.field = a.field;
        pheAdapter.method = a.method;
        pheAdapter.type = a.type;
    
        return pheAdapter;
    
    }
    

I know this is not a neat solution, but it was the best I could come up with. Maybe I'll get some better solutions here :-)

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜