0

In normal runtime everything seems to deserialize correctly but when I try to run a test class it throws the following error:

System.JSONException: Unexpected character ('r' (code 114)): was expecting double-quote to start field name at [line:1, column:4]

Additional information: The root cause seems to be pointing to the testmethod as I am using test.setMock. This actually worked perfect until I tried to introduce the dataDef.JSONRESPONSE in my try catch block.

How can I write a test where I won't get this error?

Has anyone run into this and how did you fix it? Also, it would be helpful to understand what is really going on.

I make a put call via HTTP and the response is given back as JSON and I am trying to deserialize the response.getbody(). errorType, message and stackTrace. I am only interested in errorType and message. I created a wrapper DataDef class.

public class JSONRESPONSE 
    {
        public String errorType;
        public String message;
    }   
    public static JSONRESPONSE parse_jsonresponse(String json)
    {
        return (JSONRESPONSE) System.JSON.deserialize(json, JSONRESPONSE.class);
}

The test class looks like this:

static testMethod void parse_JSONRESPONSE()
{
    String json = JSON.serialize(new Map<String,String>{'errorType'=>'javax.ejb.EJBTransactionRolledbackException','message'=>'ORA-00001: unique constraint (ACCOUNT_SHORT_NAME_UNIQUE) violated'});
    DataDef.JSONRESPONSE obj = DataDef.JSONRESPONSE.parse_jsonresponse(json);
    System.assert(obj != null);
}

In my test class I have this test method. The setup just creates an account record. Normally this works fine. When adding for the JSONRESPONSE it seems to be misbehaving. The response.getbody() is a custom JSON return from an external system.

static testMethod void defensiveFuturecall()
{
    setup();
    Account AMA                                 =   [Select Id from Account where Name  =   :account_affiliate];
    Set<ID> ids                                 =   new Set<ID>();
    ids.add(AMA.id);
    test.startTest();
    test.setMock(HttpCalloutMock.class, new mdm_MockResponseGenerator());
    account.defensiveFuturecall(ids);
    test.stopTest();        
}

I believe the issue is stemming from:

        try
        {
            response                                        =   http.send ( request );
        }
        catch (Exception ex)   
        {
             ex.getTypeName() + ' > Stack Trace ' + ex.getStackTraceString();
        }
        finally
        {
            log_record.Status__c                        =   response.getStatus();
            log_record.Status_Code__c               =   response.getStatusCode();
            //system.debug(response.getBody());
            DataDef.JSONResponse JSONResponse           =   (DataDef.JSONResponse)JSON.deserialize(response.getBody(), dataDef.JSONResponse.class);
            log_record.Response_Error_Type__c       =   JSONResponse.errorType;
            log_record.Response_Message__c          =   JSONResponse.message;
        }

In the line seems to be the cause. When it runs as realtime it is no issue but when running as test class it throws the error.

DataDef.JSONResponse JSONResponse           =   (DataDef.JSONResponse)JSON.deserialize(response.getBody(), dataDef.JSONResponse.class);
  • 2
    Why are you escaping the double quotes? You should only escape double quotes that appear in values... – Phil W Apr 6 at 20:13
  • I used the JSON2APEX helper tool and it is what it generated. – Daryn Apr 7 at 13:11
  • You are using a mock callout. Please include its details in the question since I would assume the problem originates there. I'm guessing its body isn't being populated correctly with JSON but rather with some plain text starting with "r"... – Phil W Apr 7 at 14:03
1

Firstly make sure the JSON to be used is correctly formatted. @krigi's approach is good, but you can go one better and change this to:

Map<String, Object> dataToSerialize = new Map<String, Object> {
    'errorType' => 'javax.ejb.EJBTransactionRolledbackException',
    'aBoolean' => true,
    'aNumber' => 10,
    'anObject' => new Map<String, Object> {
        'aProperty' => 'example'
    }
};

Performing a JSON.serialize on the dataToSerialize will then be able to support relevant data types in JSON (string, boolean, number, array, object).

The next thing to do is to ensure that your callout mock is correctly putting just this JSON into the body:

public class ExampleHttpCalloutMock implements HttpCalloutMock {
    private String theJSON;

    public ExampleHttpCalloutMock(String theJSON) {
        this.theJSON = theJSON;
    }

    public HTTPResponse respond(HTTPRequest req) {
        HTTPResponse res = new HTTPResponse();
        res.setHeader('Content-Type', 'application/json');
        res.setBody(theJSON);
        res.setStatusCode(200);

        return res;
    }
}

That should stop this error.

| improve this answer | |
  • I learned a few new things and gained better understanding of others. The definitely helped. I liked the one better as I didn't know you could do it that way. Also, the previous mockclass I was using was a left over from previous vendor and I think this is a much better implementation allowing for greater versatility. Thank you! – Daryn Apr 7 at 16:54
2

You shouldn't be manually creating (and escaping) a JSON Object String in your test method. Best to let the platform handle the serialization for you. Try:

    String json = JSON.serialize(new Map<String,String>{'errorType'=>'javax.ejb.EJBTransactionRolledbackException','message'=>'ORA-00001: unique constraint (ACCOUNT_SHORT_NAME_UNIQUE) violated'});
| improve this answer | |
  • I like your approach and I have incorporated that suggestion. I am still getting the following error when I run the test class that makes the callout. System.JSONException: Unexpected character ('r' (code 114)): was expecting double-quote to start field name at [line:1, column:4]. I will update the main post to include more info. – Daryn Apr 7 at 12:33
  • 1
    Maybe the issue is with my testmethod as I am not sure if or how to mock the response.getbody to use the what is defined in the datadef. – Daryn Apr 7 at 13:17
  • This is only good if all property values are strings. This should use Map<String, Object> instead, which will allow support of strings, numbers, booleans, arrays and nested objects (themselves represented by Map<String, Object> too). – Phil W Apr 7 at 14:17
  • 1
    @Phil W the original post/question examples clearly implied property values of type String, thus I answered along those lines :) – krigi Apr 7 at 16:56
  • You did Krigi so I am giving you a useful. Thank you! – Daryn Apr 7 at 17:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.