Read the first blog at http://stevefouracre.blogspot.co.uk/2015/05/unit-test-data-creation-framework.html
In my last blog I introduced my Unit Test Data Creation Framework, now we will start building the classes of the framework.
In my last blog I introduced my Unit Test Data Creation Framework, now we will start building the classes of the framework.
First create the Constants class
public class Constants {
public static final String CONST_Account = 'ACCOUNT';
public static final String CONST_Contact = 'CONTACT';
}
|
Now, in the ITestData class add
public interface ITestData {
List<sObject> returnAnyObject(String jsonStr, KeyValue[]
kVals);
}
|
Next, in the TestDataJsonLibrary class add
public class TestDataJsonLibrary {
public static String referenceKey = 'ReferenceID';
public class Standard{
public final Map<String, String> libraryMap = new
Map<String, String>{
Constant.CONST_Account =>
'{"attributes":{"type":"Account"},"Field1__c":"Value
1","Field2__c":"Value 2"}',
Constant.CONST_Contact =>
'[{"attributes":{"type":"Contact"},"'+referenceKey+'":"Reference
Value","Field1__c":"Value 1"}
};
}
}
|
In the Return Data class add
public abstract class TestDataReturnData implements
ITestData{
public Boolean bulkModeOn = false;
public Map<System.Type, String> overrideJson = new
Map<System.Type, String>();
public
List<sObject> returnAnyObject(String jsonStr, KeyValue[] kVals){
List<sObject> sobj;
if(jsonStr.contains(TestDataJsonLibrary.referenceKey))
jsonStr
= getFilteredJsonString(jsonStr, kVals);
if(jsonStr.startsWith('['))
sobj =
(List<sObject>) System.Json.deserialize(jsonStr,
List<sObject>.class);
else
sobj = (List<sObject>)
System.Json.deserialize('['+jsonStr+']', List<sObject>.class);
if(kVals !=
null){
for(sObject obj : sobj)
obj = UtilDML.setObjData(obj,
kVals);
}
return sobj;
}
private String
deserialJson(String jsonStr, KeyValue[] kVals){
List<Object> deserialLst =
(List<Object>) JSON.deserializeUntyped(jsonStr.unescapeEcmaScript());
String aReferenceKey;
//when
setting the fields from the KeyValues if 1 is the lookup field set to aReferenceKey
for(KeyValue kv : kVals){
if(kv.key ==
TestDataJsonLibrary.referenceKey){
aReferenceKey
= kv.value;
break;
}
}
List<sObject> serialLst = new
List<sObject>();
for(Object obj : deserialLst){
Map<String, Object>
objMap = (Map<String, Object>) obj;
if(aReferenceKey ==
objMap.get(TestDataJsonLibrary.referenceKey)){
objMap.remove(TestDataJsonLibrary.referenceKey);
serialLst.add(UtilDML.convertToSobject(objMap));
}
}
return JSON.serialize(serialLst);
}
}
|
In the Insert Data class add
public virtual class TestDataInsertData extends
TestDataReturnData{
private sObject
insertAnyObject(String jsonStr, KeyValue[] kVals, System.Type objType){
if(overrideJson != null && overrideJson.containsKey(objType))
jsonStr
= overrideJson.get(objType);
sObject sobj
= super.returnAnyObject(jsonStr, kVals)[0];
// set to
true if inserting multiple records
if(bulkModeOn == false)
insert
sobj;
return sobj;
}
public Contact
insertContact(String jsonstr, KeyValue[] kVals){
return
(Contact) insertAnyObject((jsonstr != null && jsonstr != '') ?
jsonstr : new
TestDataFramework_JsonLibrary.Standard().M.get(Constants.CONST_Contact),
kVals, Contact.class);
}
}
|
In the Update Data class add
public virtual class TestDataUpdateData extends
TestDataInsertData{
public Account
updateAccount(KeyValue[] insertkVals, KeyValue[] updatekVals){
Account acc
= super.insertAccount(insertkVals);
if(updatekVals != null)
acc = (Account) UtilDML.setObjData(acc,
updatekVals);
update acc;
return acc;
}
}
|
In the ComplexData class add
public virtual class TestDataComplexData extends
TestDataInsertData{
public Account
acc{get;set;}
public Contact
cont{get;set;}
public
public Account
insertContactAndAccount(Map<System.Type, List<KeyValue>> keyMap){
//inserts
just 1 Account and 1 Contact and links them together, using the map in the argument
means you only need to use 1 argument
//stops a
null exception occurring later in the code
if(keyMap ==
null){
keyMap =
new Map<System.Type, List<KeyValue>>();
kMaps.put(Contact.class, new List<KeyValue>());
}else
if(keyMap.containsKey(Contact.class) == false)
keyMap.put(Contact.class, new
List<KeyValue>());
this.acc =
super.insertAccount(keyMap.get(Account.class));
// now
provide the Id into the KeyValues to link the Objects together
keyMap.get(Contact.class).add(new KeyValue('AccountId', this.acc.id,
'ID'));
this.cont =
super.insertContact(keyMap.get(Contact.class));
return
this.acc;
}
public Account insertContactOpportunityAndAccount(Map<System.Type,
List<KeyValue>> keyMap){
insertContactAndAccount(keyMap);
if(keyMap.containsKey(Opportunity.class)
== false)
keyMap.put(Opportunity.class,
new List<KeyValue>());
keyMap.get(Opportunity.class).add(new KeyValue('AccountId',
this.acc.id, 'ID'));
this.opp =
super.insert
return
this.acc;
}
}
|
In the BulkData class add
public virtual class TestDataBulkData extends
TestDataInsertData{
public Account
insertAccountAndContacts(Map<System.Type, KeyValueBulk> keyMap){
//This
inserts 1 Account and a number of Contacts
//stops a
null exception occurring later in the code
if(keyMap ==
null){
keyMap =
new Map<System.Type, KeyValueBulk>();
keyMap.put(Contact.class, new KeyValueBulk());
}else
if(kMaps.containsKey(Contact.class) == false)
kMaps.put(Contact.class, new
KeyValueBulk());
this.conts = new List<Case>();
Account acc
= super.insertAccount(keyMap.get(Account.class) .keyValueBulkLst);
bulkModeOn =
true; // stops records being inserted
//link records together
(keyMap.get(Contact.class)).keyValueBulkLst.add(new
KeyValue('AccountId', acc.id, 'ID'));
List<KeyValueBulk> kVals =
(keyMap.get(Contact.class)).keyValueBulkLst;
for(Integer i = 0; i <
(keyMap.get(Contact.class)).insertRecs; i++)
this.conts.add(super.insertContact(kVals));
insert this.conts;
bulkModeOn =
false; //resets flag
return
this.acc;
}
}
|
KeyValue class looks like this
public class KeyValue{
public
String key{get; set;}
public
String value{get; set;}
public
String fieldType{get; set;}
public
KeyValue(String key, String value, String fieldType){
this.key
= key;
this.value
= value;
this.fieldType
= fieldType.toUpperCase();
}
public
KeyValue(String key, String value){
this.key
= key;
this.value
= value;
}
public
KeyValue(){
}
}
|
KeyValueBulk class looks like this
public class KeyValueBulk{
public integer insertRecs;
public KeyValue[] keyValueBulkLst;
public KeyValueBulk(integer insRecs, KeyValue[] kys){
insertRecs = (insertRecs != null && insertRecs
> 0) ? insertRecs : 1;
keyValueBulkLst
= kys;
}
}
|
Also if you have any triggers, for rapid transactional
processing create the TriggerController class
To bypass code in triggers which often fire workflows and
process builders, which in turn fire triggers again; all of which takes extra
processing time. But when you are simply creating test data you are implicitly
telling the system to create an exact data set and if you are not actually
testing the trigger there is no need when you are creating the test data to run
through the code in the trigger.
In the TriggerController class add
public class TriggerController {
//used
specifically in unit test data framework
public
static Map<System.Type, Boolean> rapidProcessing;
//Account
- Disable / Enable parts of trigger
public
static boolean Account_DisableAllTypes = false;
public
static boolean Account_DisableInsert = false;
public
static boolean Account_DisableUpdate = false;
public
static boolean Account_DisableDelete = false;
public
static boolean Account_DisableUnDelete = false;
//used to
unit test the trigger control to ensure the correct parts of the trigger were
triggered
public
static boolean Account_Insert_Succeeded = false;
public
static boolean Account_Update_Succeeded = false;
public
static boolean Account_Delete_Succeeded = false;
public
static boolean Account_UnDelete_Succeeded = false;
}
|
In the UtilDML class add
public class UtilDML {
public
static Sobject setObjData(Sobject aobj, KeyValue[] kVals){
Sobject
thisobj;
if (kVals != null){
for (KeyValue eachval : kVals){
try{
thisobj =
setFieldVal(aobj, eachval);
}
catch(Exception
ex){system.debug('## ex ' + ex); }
}
}
return
thisobj;
}
public
static Sobject setFieldVal(Sobject obj, KeyValue thisKeyVal){
system.debug('##
thisKeyVal ' + thisKeyVal);
if
(thisKeyVal.fieldtype == 'DATE'){
String
tmpDt = thisKeyVal.value;
Date
dt = Date.valueOf(tmpDt.substring(0,9).trim());
system.debug('##
dt ' + dt);
obj.put(thisKeyVal.key,
dt);
}else
if (thisKeyVal.fieldtype == 'DATETIME'){
system.debug('##
Datetime.valueOf(tmpDt) ' + Datetime.valueOf(thisKeyVal.value));
obj.put(thisKeyVal.key,
Datetime.valueOf(thisKeyVal.value));
system.debug('##
obj ' + obj);
}else
if (thisKeyVal.fieldtype == 'DECIMAL')
obj.put(thisKeyVal.key,
decimal.valueof(thisKeyVal.value));
else
if (thisKeyVal.fieldtype == 'INTEGER')
obj.put(thisKeyVal.key,
integer.valueof(thisKeyVal.value));
else
if (thisKeyVal.fieldtype == 'LONG')
obj.put(thisKeyVal.key,
long.valueof(thisKeyVal.value));
else
if (thisKeyVal.fieldtype == 'DOUBLE')
obj.put(thisKeyVal.key,
Double.valueof(thisKeyVal.value));
else
if (thisKeyVal.fieldtype == 'BOOLEAN')
obj.put(thisKeyVal.key,
((thisKeyVal.value).toUpperCase() == 'TRUE') );
else
if (thisKeyVal.fieldtype == 'BLOB')
obj.put(thisKeyVal.key,
Blob.valueof(thisKeyVal.value));
else
if (thisKeyVal.fieldtype == 'ID'){
obj.put(thisKeyVal.key,
((ID)thisKeyVal.value));
}else
obj.put(thisKeyVal.key,
thisKeyVal.value);//String
system.debug('##
obj ' + obj);
return
obj;
}
public static sObject convertToSobject(Map<String, Object> objMap){
sObject sObj = Schema.getGlobalDescribe().get((String)((Map<String, Object>)objMap.get('attributes')).get('type')).newSObject();
Map<String, Schema.SObjectField> sObjMap = sObj.getSObjectType().getDescribe().fields.getMap();
for(String key : objMap.keySet()){
if(sObjMap.containskey(key)){
Schema.DescribeFieldResult field = sObjMap.get(key).getDescribe();
String fieldType = field.getType().Name();
String value = (String) objMap.get(key);
if(fieldType == 'DATE'){
sObj.put(key, Date.valueOf(value));
}else if(fieldType == 'DATETIME'){
sObj.put(key, Datetime.valueOf(value));
}else if(fieldType == 'DECIMAL'){
//sObj.put(key, Decimal.valueof(value));
sObj.put(key, Decimal.valueOf(value));
}else if(fieldType == 'INTEGER'){
sObj.put(key, Integer.valueOf(value));
}else if(fieldType == 'LONG'){
sObj.put(key, Long.valueOf(value));
}else if(fieldType == 'DOUBLE'){
sObj.put(key, Double.valueOf(value));
}else if(fieldType == 'BOOLEAN'){
sObj.put(key, (value.toUpperCase() == 'TRUE'));
}else{// String
sObj.put(key, value);
}
}
}
return sObj;
}
}
|