[SalesForce] JSON deserialize test

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);

Best Answer

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.

Related Topic