We are doing a lot of JSON data parsing in our solution by own JSON parser implementation. I wonder how can I write unit tests for JSON parser. Is it good approach to load JSON data from static resources? How can I get JSON as string from static resource? Do I need to use StaticResourceCalloutMock when I only test JSON parser methods that simply get input as string and return SObject?
[SalesForce] recommended way to unit test JSON Parser based on JSON.deserializeUntyped calls
Related Solutions
Correction to Trigger
To make the trigger more robust, don't directly check the key prefix of the ID unless you do so dynamically (e.g. String jobPrefix = AVTRRT__Job_Applicant__c.SObjectType.getDescribe().getKeyPrefix()
). An easier way that saves a describe is to check SObjectType
values. Code illustration follows.
trigger caseAttachment1771 on Attachment (after insert, after delete, after undelete) {
Map<Id, String[]> jobs = new Map<Id, String[]>();
AVTRRT__Job_Applicant__c[] apps = new AVTRRT__Job_Applicant__c[0];
for(Attachment record: Trigger.new!=null?Trigger.new:Trigger.old) {
if(record.ParentId.getSObjectType() == AVTRRT__Job_Applicant__c.SObjectType) {
jobs.put(record.ParentId, new String[0]);
}
}
for(Attachment record: [SELECT Id, ParentId FROM Attachment WHERE ParentId IN :jobs.keySet()]) {
jobs.get(record.ParentId).add(record.Id);
}
for(Id recordId: jobs.keySet()) {
apps.add(new AVTRRT__Job_Applicant__c(Id=recordId, Attach_Ids__c=String.join(jobs.get(recordId),'|')));
}
update apps;
}
Sample Test Method
While it's always best to learn, some people learn by example, so I've included an example here. This code will fully test your trigger and make sure that the logic is working as designed.
// Test method example
@isTest static void test() {
// Create some parents
AVTRRT__Job_Applicant__c jobRecord = new AVTRRT__Job_Applicant__c(Name='Test');
insert jobRecord;
Account accRecord = new Account(Name='Test');
insert accRecord;
// Create some attachments
Attachment[] attRecords = new Attachment[] {
new Attachment(ParentId=jobRecord.Id, Name='Test', Body=Blob.valueOf('Test'), ContentType='text/plain'),
new Attachment(ParentId=accRecord.Id, Name='Test', Body=Blob.valueOf('Test'), ContentType='text/plain')
};
// Test insert trigger logic
insert attRecords;
// Make sure that Attach_Ids__c has value
jobRecord = [SELECT Id, Name, Attach_Ids__c FROM AVTRRT__Job_Applicant__c WHERE Id = :jobRecord.Id];
System.assertEquals(attRecord[0].Id, (Id)jobRecord.Attach_Ids__c);
// Test delete trigger logic
delete attRecords[0];
// Make sure that Attach_Ids__c does not have value
jobRecord = [SELECT Id, Name, Attach_Ids__c FROM AVTRRT__Job_Applicant__c WHERE Id = :jobRecord.Id];
System.assertEquals(true, String.isBlank(jobRecord.Attach_Ids__c));
// Test undelete trigger logic
undelete attRecords[0];
// Make sure that Attach_Ids__c has value
jobRecord = [SELECT Id, Name, Attach_Ids__c FROM AVTRRT__Job_Applicant__c WHERE Id = :jobRecord.Id];
System.assertEquals(attRecord[0].Id, (Id)jobRecord.Attach_Ids__c);
}
Note that some logic may still be incorrect. This is meant to be illustrative only, and it's likely that you'll need to change the logic so that required fields are populated, etc.
Don't bother using JSONParser. It's inefficient, and really only meant for porting Java code, as far as I can tell. Since you're only concerned with a few elements, you can just do the following:
public class JSONParserUtil {
public class AccountList {
public AccountItem[] account;
}
public class AccountItem {
public String CONTAINER, accountName, accountStatus;
public Integer providerAccountId;
}
@future(callout=true)
public static void parseJSONResponse() {
Http httpProtocol = new Http();
// Create HTTP request to send.
HttpRequest request = new HttpRequest();
// Set the endpoint URL.
String endpoint = 'https://developer.api.yodlee.com:443/ysl/restserver/v1/accounts';
request.setEndPoint(endpoint);
// Set the HTTP verb to GET.
request.setMethod('GET');
request.setHeader('Authorization','XXX');
// Send the HTTP request and get the response.
// The response is in JSON format.
HttpResponse response = httpProtocol.send(request);
System.debug(response.getBody());
List<Yodlee_Accounts__c> acclst = new List<Yodlee_Accounts__c>();
// Parse the data according to the AccountList class
AccountList data = (AccountList)JSON.deserialize(response.getBody(), AccountList.class);
// Iterate over the data
for(AccountItem item: data.account) {
// Do stuff with item...
}
}
}
Best Answer
If the JSON is reasonably small then I would build it in the unit tests. That allows you to generate various permutations and always have the values to hand to assert against.
The best way to do that (when you don't have Apex classes generated by e.g. JSON2Apex) is by creating nested
Map<String, Object>
s and then applyingJSON.serialize
. This ensures correct escaping and avoids ugly and error-prone string quoting and concatenation. Here is an example:At first sight, this looks pretty confusing, but it is just using a
List
for any JSON array[...]
and aMap
for any JSON object{...}
. And Apex has good data initialization syntax available such as=>
for setting up map name-value pairs. You don't have to build it all at once either, you can set up pieces and then assemble them.If the JSON is very large and you have examples that you want to use in tests then static resources are one way to go. You can query static resources (in product code or test code) like this: