Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Complex object as param #66

Open
dtamajon opened this issue Aug 22, 2014 · 7 comments
Open

Complex object as param #66

dtamajon opened this issue Aug 22, 2014 · 7 comments

Comments

@dtamajon
Copy link

Hi again Anuj,

I have been testing with easytest on "basic" way, and now I'm trying to test passing a complex object as parameter.

For example:

@dataloader(filePaths = { "myXmlData.xml" })
testSave_withMyEntity(MyEntity myEntity){
myBo.save(myEntity);
}

And myXmlData.xml contains:
<TestMethod name="testSave_withMyEntity">
    <TestRecord id="1">
        <Description></Description>
        <InputData>
            <Entry key="name" value="Administrador" />
            <Entry key="description" value="" />
            <Entry key="state" value="true" />
            <Entry key="value" value="8" />
        </InputData>
    </TestRecord>
</TestMethod>

When I test with JUnit, it shows the method with the given parameters in XML
testSave_withMyEntity{name=Administrador,description=,state=true,value=8}, but it does not make the instance of 'MyEntity', so test is failing.

What am I missing?

Thank you!

@dtamajon
Copy link
Author

I answer to myself. XML should be like:

<Entry key="myEntity" value="{ &quot;name&quot;: &quot;Administrador&quot;, &quot;description&quot;: &quot;&quot;, &quot;state&quot; : &quot;true&quot;, &quot;value&quot; : &quot;8&quot; }" />

The &quot; is correct, you need a double-quote to get JSON correctly parsed or you get an error.

@anujgandharv
Copy link
Member

Hi Daniel,
I am glad you looked into the issue and also solved it. Personally I think " is a workaround. I will try to fix this so that you don't need this. Possibly one way could be to try something like this :

I am not sure whether it will work as I havent tested it myslef. But I have a feeling it will. Basically I have changed the double quotes in value to single quotes so that I can use double quotes for the JSON.

Let me know if that worked.

@anujgandharv
Copy link
Member

      <Entry key="myEntity" value='{"name": "Administrador",  "description": "", "state" : "true", "value" :   "8"}' />

@dtamajon
Copy link
Author

dtamajon commented Sep 1, 2014

I'm back with this question... how would you define a object array with JSON? I have checked two methods, with no success:

Attemp 1

<InputData>
    <Entry key="itemList" value='
        {"name": "Administrador",  "description": "", "state" : "true", "value" :   "8"},
        {"name": "Developer",  "description": "", "state" : "true", "value" :   "90"},
        {"name": "Tester",  "description": "", "state" : "true", "value" :   "3"},
    ' />
</InputData>

with StackTrace:

java.lang.RuntimeException: org.codehaus.jackson.map.JsonMappingException: Can not deserialize instance of java.util.ArrayList out of START_OBJECT token
 at [Source: java.io.StringReader@532c780f; line: 1, column: 1]
    at org.easetech.easytest.util.GeneralUtil.handleJSONData(GeneralUtil.java:659)
    at org.easetech.easytest.util.GeneralUtil.populateJSONData(GeneralUtil.java:636)
    at org.easetech.easytest.converter.JSONDataConverter.convert(JSONDataConverter.java:55)
    at org.easetech.easytest.converter.JSONDataConverter.convert(JSONDataConverter.java:18)
    at org.easetech.easytest.converter.ConversionDelegator.convert(ConversionDelegator.java:127)
    at org.easetech.easytest.annotation.Param$DataSupplier.getValueSources(Param.java:196)
    at org.easetech.easytest.internal.EasyAssignments.potentialsForNextUnassigned(EasyAssignments.java:100)
    at org.easetech.easytest.runner.InternalParameterizedStatement.runWithAssignment(InternalParameterizedStatement.java:109)
    at org.easetech.easytest.runner.InternalParameterizedStatement.evaluate(InternalParameterizedStatement.java:93)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
    at org.easetech.easytest.runner.DataDrivenTestRunner$1.evaluate(DataDrivenTestRunner.java:339)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
    at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
    at java.util.concurrent.FutureTask.run(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)
Caused by: org.codehaus.jackson.map.JsonMappingException: Can not deserialize instance of java.util.ArrayList out of START_OBJECT token
 at [Source: java.io.StringReader@532c780f; line: 1, column: 1]
    at org.codehaus.jackson.map.JsonMappingException.from(JsonMappingException.java:163)
    at org.codehaus.jackson.map.deser.StdDeserializationContext.mappingException(StdDeserializationContext.java:219)
    at org.codehaus.jackson.map.deser.StdDeserializationContext.mappingException(StdDeserializationContext.java:212)
    at org.codehaus.jackson.map.deser.std.CollectionDeserializer.handleNonArray(CollectionDeserializer.java:246)
    at org.codehaus.jackson.map.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:204)
    at org.codehaus.jackson.map.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:194)
    at org.codehaus.jackson.map.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:30)
    at org.codehaus.jackson.map.ObjectMapper._readMapAndClose(ObjectMapper.java:2732)
    at org.codehaus.jackson.map.ObjectMapper.readValue(ObjectMapper.java:1863)
    at org.easetech.easytest.util.GeneralUtil.handleJSONData(GeneralUtil.java:654)
    ... 20 more

Attemp 2
Error on attemp 1 suggested the value is not a collection, so I have tried to define as collection, in the way (I think) JSON expects:

<InputData>
    <Entry key="itemList" value='[
        {"name": "Administrador",  "description": "", "state" : "true", "value" :   "8"},
        {"name": "Developer",  "description": "", "state" : "true", "value" :   "90"},
        {"name": "Tester",  "description": "", "state" : "true", "value" :   "3"},
    ]' />
</InputData>

with StackTrace

java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to myClass
    at MyClassTest.testLoggedGetAll(MyClassTest.java:1461)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
    at org.easetech.easytest.runner.InternalParameterizedStatement.methodCompletesWithParameters(InternalParameterizedStatement.java:193)
    at org.easetech.easytest.runner.InternalParameterizedStatement.runWithCompleteAssignment(InternalParameterizedStatement.java:151)
    at org.easetech.easytest.runner.InternalParameterizedStatement.runWithAssignment(InternalParameterizedStatement.java:134)
    at org.easetech.easytest.runner.InternalParameterizedStatement.evaluate(InternalParameterizedStatement.java:93)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
    at org.easetech.easytest.runner.DataDrivenTestRunner$1.evaluate(DataDrivenTestRunner.java:339)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
    at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
    at java.util.concurrent.FutureTask.run(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)

Data is loaded, but instead of a collection for my type class I get a collection of LinkedHashMap, with all the entries as key/value pairs.

Maybe this data input is not supported by EasyTest?

@dtamajon
Copy link
Author

dtamajon commented Sep 1, 2014

Finally, I have overwritten my XMLDataLoader to parse JSON array of objects. I have had to do it in the DataLoader because Converter is not being executed given to a previous pre-parse, which results in a different type matching, and is not called.

The semantics for JSON in my XML is:

<InputData>
    <Entry key="itemList" value='[
        {"name": "Administrador",  "description": "", "state" : "true", "value" :   "8"},
        {"name": "Developer",  "description": "", "state" : "true", "value" :   "90"},
        {"name": "Tester",  "description": "", "state" : "true", "value" :   "3"},
    ](package.myImplementation)' />
</InputData>

As you see, I have added the implementation class to let Jackson JSON parser which is the type of objects in the list to fulfill.

I leave my code solution here, if anyone else needs.

    /**
     * Returns a Map representation of a Single data set for a given method. This data is used to run the test method
     * once.
     * 
     * @param testEntry a list of {@link Entry} objects
     * @return a Map
     */
    Map<String, Object> convertFromListOfEntry(List<Entry> testEntries, List<Entry> defaultEntries) {
        Map<String, Object> testData = new HashMap<String, Object>();

        // use default test entries as defaults, when present

        if (defaultEntries != null) {
            for (Entry defaultEntry : defaultEntries) {
                convertFromEntry(defaultEntry, testData);
            }
        }

        // use actual test entries on top

        if (testEntries != null) {
            for (Entry entry : testEntries) {
                convertFromEntry(entry, testData);
            }
        }
        return testData;
    }

    /**
     * Adds a entry to the given Map representation.
     * 
     * @param entry {@link Entry} object
     * @param data map whether add entry representation
     */
    void convertFromEntry(Entry entry, Map<String, Object> data) {
        boolean done = false;
        String value = entry.getValue();
        if ( value.startsWith("[") ) {
            List<?> list = this.convertJsonList(value);
            if ( list != null ) {
                data.put(entry.getKey(), list);
                done = true;
            }
        }

        if ( !done ) {
            data.put(entry.getKey(), entry.getValue());
        }
    }

    /**
     * Try to convert given string to a list of objects.
     * 
     * @param json string to parse as json
     * @return list of objects, if given string is a valid json; otherwise, {@code null}
     */
    public List<?> convertJsonList(String json) {
        List<?> perfilList = null;

        Class<?> clazz = null;
        Pattern pattern = Pattern.compile("\\(([^\\[\\]])*\\)$", Pattern.CASE_INSENSITIVE | Pattern.DOTALL | Pattern.MULTILINE);
        Matcher matcher = pattern.matcher(json);

        while (matcher.find()) {
            try {
                String proposedClass = matcher.group().replace("[", "").replace("(", "").replace(")", "");
                clazz = Class.forName(proposedClass);
                json = json.replace(matcher.group(), "");
                break;

            } catch (ClassNotFoundException e) {
            }
        }

        if ( this.isValidJSON(json)) {
            ObjectMapper objectMapper = new ObjectMapper();
            try {
                if ( clazz != null ) {
                    CollectionType collectionType = objectMapper.getTypeFactory().constructCollectionType(List.class, clazz);
                    perfilList = objectMapper.readValue(json, collectionType);
                }
                else {
                    perfilList = objectMapper.readValue(json, List.class);
                }

            } catch (IOException e) {
            }
        }

        return perfilList;
    }

    /**
     * Indicates wether string is a valid JSON string or not.
     * @param json string to check
     * @return {@code true} is string is a valid JSON; otherwise, {@code false}
     */
    public boolean isValidJSON(final String json) {
        boolean valid = false;
        try {
            final JsonParser parser = new ObjectMapper().getJsonFactory().createJsonParser(json);
            while (parser.nextToken() != null) {
            }
            valid = true;

        } catch (JsonParseException jpe) {

        } catch (IOException ioe) {

        }

        return valid;
    }

Exceptions have been muted because it is a conversion tryout, so if it fails it will be parsed using the default conversion.

@anujgandharv
Copy link
Member

Again, sorry for replying late. Is it possible for you to contribute this change in code as a Pull Request to EasyTest Core object. I will then quickly incorporate it and will then plan a new release soon.

Thanks,
Anuj Kumar

@dtamajon
Copy link
Author

dtamajon commented Sep 1, 2014

Hi Anuj,

I will think in a cleaner way to implement in Core, just to be Jackson compatible (my solution is fast but is not compatible).

I need to navigate deeper in EasyTest Core because I think is a collateral situation given for another functionallity.

I will do it in next days.
Daniel Tamajon

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants