0

Ill be very specific about what i am trying to achieve here. Help would be greatly appreciated. I am currently creating a test JSON object like this:

    String testTrainingRecord(int employeeId, String date, String courseTitle) {
    String zeroDate = date.substring(0, 11) + "00:00:00";
    return "{\"id\":0,\"employeeId\":" + employeeId + ",\"dateIdentified\":\"" + zeroDate + "\",\"dateFrom\":\"" + zeroDate + "\"," +
            "\"dateTo\":\"" + zeroDate + "\",\"courseTitle\":\"" + courseTitle + "\",\"courseProvider\":\"APIprovider\",\"category\":\"API\",\"subject\":\"Recruitment\"," +
            "\"location\":\"Bristol\",\"cost\":0,\"duration\":0,\"durationHours\":0,\"cpdhours\":0,\"cpdpoints\":0,\"evaluationCompleted\":true,\"nonAttendance\":true," +
            "\"notes\":\"APInote\",\"reviewDate\":\"" + zeroDate + "\",\"developmentStatus\":\"APIdevelopment\",\"priorityLevel\":\"Level1\"," +
            "\"lastUpdate\":\"" + zeroDate + "\"}";
}

I then do this when in my test which posts it to the API and this work fine:

.body(testTrainingRecord(employeeId, date, courseTitle)).log().all()

What i would like to do is something like this:

    public class testTrainingRecord2 {
    HashMap<String, String> hashMapString = new HashMap<String, String>();
    HashMap<String, Integer> hashMapInt = new HashMap<String, Integer>();
    HashMap<String, Boolean> hashMapIBool = new HashMap<String, Boolean>();

    public testTrainingRecord2(int employeeId, String date, String courseTitle) {
        String zeroDate = date.substring(0, 11) + "00:00:00";

        hashMapInt.put("id", 0);
        hashMapInt.put("employeeId", employeeId);
        hashMapString.put("dateIdentified", zeroDate);
        hashMapString.put("dateFrom", zeroDate);
        hashMapString.put("dateTo", zeroDate);
        hashMapString.put("courseTitle", courseTitle);
        hashMapString.put("courseProvider", "APIprovider");
        hashMapString.put("category", "API");
        hashMapString.put("subject", "Recruitment");
        hashMapString.put("location", "Bristol");
        hashMapInt.put("cost", 0);
        hashMapInt.put("duration", 0);
        hashMapInt.put("durationHours", 0);
        hashMapInt.put("cpdhours", 0);
        hashMapInt.put("cpdpoints", 0);
        hashMapIBool.put("evaluationCompleted", true);
        hashMapIBool.put("nonAttendance", true);
        hashMapString.put("notes", "APInote");
        hashMapString.put("reviewDate", zeroDate);
        hashMapString.put("developmentStatus", "APIdevelopment");
        hashMapString.put("priorityLevel", "Level1");
        hashMapString.put("lastUpdate", zeroDate);

        return;
    }
}

Which i am attempting to post like this:

.body(new testTrainingRecord2(employeeId, date, courseTitle)).log().all()

However when the test runs it errors as follows:

com.fasterxml.jackson.databind.JsonMappingException: No serializer found for class TrainingTests$testTrainingRecord2 and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) )

at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.failForEmpty(UnknownSerializer.java:59)
at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.serialize(UnknownSerializer.java:26)
at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:118)
at com.fasterxml.jackson.databind.ObjectMapper.writeValue(ObjectMapper.java:1819)
at com.fasterxml.jackson.databind.ObjectMapper$writeValue$0.call(Unknown Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:47)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:115)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:135)
at io.restassured.internal.mapping.Jackson2Mapper.serialize(Jackson2Mapper.groovy:53)
at io.restassured.internal.mapping.Jackson2Mapper.serialize(Jackson2Mapper.groovy)
at io.restassured.mapper.ObjectMapper$serialize.call(Unknown Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:47)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:115)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:127)
at io.restassured.internal.mapping.ObjectMapping.serializeWithJackson2(ObjectMapping.groovy:196)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:567)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:101)
at org.codehaus.groovy.runtime.callsite.StaticMetaMethodSite$StaticMetaMethodSiteNoUnwrapNoCoerce.invoke(StaticMetaMethodSite.java:149)
at org.codehaus.groovy.runtime.callsite.StaticMetaMethodSite.callStatic(StaticMetaMethodSite.java:100)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallStatic(CallSiteArray.java:55)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callStatic(AbstractCallSite.java:196)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callStatic(AbstractCallSite.java:216)
at io.restassured.internal.mapping.ObjectMapping.serialize(ObjectMapping.groovy:141)
at io.restassured.internal.mapping.ObjectMapping$serialize.call(Unknown Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:47)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:115)
at io.restassured.internal.RequestSpecificationImpl.body(RequestSpecificationImpl.groovy:750)
at TrainingTests.testPostTrainingRecord(TrainingTests.java:105)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:567)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)

Would anybody be kind enough to help please? :)

JLee101
  • 93
  • 1
  • 1
  • 8
  • jackson is complaining about the fact that `testTrainingRecord2` doesn't have any public accessible property (https://stackoverflow.com/questions/8367312/serializing-with-jackson-json-getting-no-serializer-found) and even if you provide getters and setters for the hashmaps, the object wont reflect the json at all. The properties that you put in the hashmaps should be public fields of the class. – MarcoLucidi May 15 '20 at 18:01

2 Answers2

1

MarcoLucidi already pointed this out, but a more typical approach would be to define the class with public fields, or perhaps even properties which would satisfy the OO principle known as encapsulation. A property is a private field, like:

private int cost;

that is accessible through a getter and a setter method, like:

public int getCost() {
    return cost;
}

public void setCost(int cost) {
    this.cost = cost;
}

The result for your class would be:

public class TestTrainingRecord2 {

    private boolean evaluationCompleted;
    private boolean nonAttendence;

    private int id;
    private int employeeId;
    private int cost;
    private int duration;
    private int durationHours;
    private int cpdhours;
    private int cpdpoints;

    private String dateIdentified;
    private String dateFrom;
    private String dateTo;
    private String courseTitle;
    private String courseProvider;
    private String category;
    private String subject;
    private String location;
    private String notes;
    private String reviewDate;
    private String developmentStatus;
    private String priorityLevel;
    private String lastUpdate;
    private String zeroDate;

    public TestTrainingRecord2(int employeeId, String date, String courseTitle) {

        this.zeroDate = date + "00:00:00";

        this.employeeId = employeeId;
        this.dateIdentified = date;
        this.dateFrom = zeroDate;
        this.dateTo = zeroDate;
        this.courseTitle = courseTitle;
        this.courseProvider = "APIprovider";
        this.category = "API";
        this.subject = "Recruitment";
        this.location = "Bristol";
        this.cost = 0;
        this.duration = 0;
        this.durationHours = 0;
        this.cpdhours = 0;
        this.cpdpoints = 0;
        this.evaluationCompleted = true;
        this.nonAttendence = true;
        this.notes = "APInote";
        this.reviewDate = zeroDate;
        this.developmentStatus = "APIdevelopment";
        this.priorityLevel = "Level1";
        this.lastUpdate = zeroDate;
    }

    public boolean isEvaluationCompleted() {
        return evaluationCompleted;
    }

    public void setEvaluationCompleted(boolean evaluationCompleted) {
        this.evaluationCompleted = evaluationCompleted;
    }

    public boolean isNonAttendence() {
        return nonAttendence;
    }

    public void setNonAttendence(boolean nonAttendence) {
        this.nonAttendence = nonAttendence;
    }

    public int getCost() {
        return cost;
    }

    public void setCost(int cost) {
        this.cost = cost;
    }

    // ... other getters and setters omitted, but I will include them if you prefer
}

Many development environments such as IntelliJ will actually write getters and setters for you with just a few clicks.

Then Jackson will serialize the object for you without complaint. Here is a quick test to see your object as JSON:

TestTrainingRecord2 record = new TestTrainingRecord2(0, "someDate", "someCourse");

ObjectMapper mapper = new ObjectMapper(); // from com.fasterxml.jackson.databind.ObjectMapper

String jsonString = mapper.writeValueAsString(record);

System.out.println(jsonString);

and the result:

{"evaluationCompleted":true,"nonAttendence":true,"id":0,"employeeId":0,"cost":0,"duration":0,"durationHours":0,"cpdhours":0,"cpdpoints":0,"dateIdentified":"someDate","dateFrom":"someDate00:00:00","dateTo":"someDate00:00:00","courseTitle":"someCourse","courseProvider":"APIprovider","category":"API","subject":"Recruitment","location":"Bristol","notes":"APInote","reviewDate":"someDate00:00:00","developmentStatus":"APIdevelopment","priorityLevel":"Level1","lastUpdate":"someDate00:00:00"}
Thomas Portwood
  • 1,031
  • 8
  • 13
  • Perfect. This is exactly what i wanted to do. Really well explained as well. Thank you very much for your help i really appreciate it. – JLee101 May 21 '20 at 07:31
0

Even if you had getters, setters and 'public' access modifier for the 3 HashMap attributes in the TestTrainingRecord2.java class, I think your expected json would not be created. It would be a Java class with three attributes (hashMapString, hashMapInt, hashMapIBool) and JSON object will be as follows;

{
  "hashMapString" : {
    "key_1": "value_1",
    "key_2": "value_2",
    "key_3": "value_3"
  },
  "hashMapInt": {
    "key_1": 1,
    "key_2": 2,
    "key_3": 3"
  },
  "hashMapIBool": {
    "key_1": true,
    "key_2": true,
    "key_3": false
  }
}

Therefore what you have do is create a single Java class and add all the key, valuse pairs as attributes and their values. And give public access modifier to them. And create getters and setters for each of them;

public class TestTrainingRecord2 {

    public String string_1;
    public int int_1;
    public String bool_1;

    public String getString_1() {
        return string_1;
    }

    public void setString_1(String string_1) {
        this.string_1 = string_1;
    }

    public int getInt_1() {
        return int_1;
    }

    public void setInt_1(int int_1) {
        this.int_1 = int_1;
    }

    public String getBool_1() {
        return bool_1;
    }

    public void setBool_1(String bool_1) {
        this.bool_1 = bool_1;
    }
}

Or you can create a HashMap with String as key data type and Object as value data type;

    HashMap<String,Object> map = new HashMap<>();

    map.put("key_1", "value_1");
    map.put("key_2", 1);
    map.put("key_3", true);

And pass it;

.body(map).log().all()
kaweesha
  • 803
  • 6
  • 16