Skip to content

Instantly share code, notes, and snippets.

@alan-morey
Last active August 29, 2015 13:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alan-morey/b869102440198f1d36f1 to your computer and use it in GitHub Desktop.
Save alan-morey/b869102440198f1d36f1 to your computer and use it in GitHub Desktop.
/**
* Salesforce does not allow the ability to write to certain SObject fields
* such as LastModifiedDate, CreatedDate etc. Even if those objects are
* not going to be inserted into the DB. This makes testing around these
* fields difficult as we always need to hit the database to load these values
* and it's not possible to inject the values you require.
*
* This method gets around that limitation by using JSON. We first create
* a representation of the data as a map of fields/values pairs. Then we
* transform the map into a JSON representation. This respresentation includes
* some metadata automatically to help the deserialization. Then we
* pass the JSON string back into the Json.deserializer and it writes the values
* to the fields for us as expected.
*
* Note: If we were to try to insert this object we would encounter an excecption
* as the field would be non-writable at the DB level.
*/
public class TestSobjectBuilder {
public static final String ID_FIELD = 'Id';
Map<String, Object> data = new Map<String, Object>();
Type sObjectClassType;
SObjectType sObjectType;
public TestSobjectBuilderException extends Exception {}
public TestSobjectBuilder(Type sObjectClassType) {
this.sObjectClassType = sObjectClassType;
sObjectType = ((SObject) sObjectClassType.newInstance()).getSObjectType();
}
public TestSObjectBuilder addMockId() {
return add(ID_FIELD, mockId(sObjectType));
}
public TestSObjectBuilder add(SObjectField field, String value) {
return add(String.valueOf(field), value);
}
public TestSobjectBuilder add(String field, String value) {
data.put(field, value);
return this;
}
public TestSobjectBuilder addAll(Map<SObjectField, String> valuesByFieldName) {
for (SObjectField f : valuesByFieldName.keySet()) {
add(f, valuesByFieldName.get(f));
}
return this;
}
public TestSobjectBuilder addAll(Map<String, String> valuesByFieldName) {
data.putAll((Map<String, Object>) valuesByFieldName);
return this;
}
public Object build() {
data.put('attributes', buildAttributes(sObjectType, (Id) data.get(ID_FIELD)));
String jsonData = Json.serialize(data);
return Json.deserialize(jsonData, sObjectClassType);
}
Map<String, String> buildAttributes(SObjectType soType, Id id) {
String type = String.valueOf(soType);
Map<String, String> attrs = new Map<String, String> { 'type' => type };
if (id != null) {
attrs.put('url', String.format(
'/services/data/v29.0/sobjects/{0}/{1}',
new String[] { type, id }
));
}
return attrs;
}
@TestVisible
static Id mockId(SobjectType sot) {
String prefix = sot.getDescribe().getKeyPrefix();
if (String.isBlank(prefix)) {
throw new TestSobjectBuilderException('No Key Prefix could be determined for SObjectType: ' + String.valueOf(sot));
}
return prefix + '000000000000AAA';
}
}
@isTest
class TestSobjectBuilderTests {
static final String ISO_DATE_TIME = 'yyyy-MM-dd\'T\'HH:mm:ss.SSSZ';
static final Integer MILLIS_PER_SECOND = 1000;
static TestSobjectBuilder builder;
@isTest static void build_WhenGivenAccountTypeAndNoAddedFields() {
Account actual = (Account) new TestSobjectBuilder(Account.class).build();
System.assertNotEquals(null, actual);
}
@isTest static void build_WhenGivenAccountTypeAndAddIdField() {
Id expectedId = TestSobjectBuilderTests.mockId(Account.sObjectType);
Account actual = (Account) new TestSobjectBuilder(Account.class).add('id', expectedId).build();
System.assertEquals(expectedId, actual.id);
}
@isTest static void build_WhenGivenAccountTypeAndAddedIdAndNonWritableFields() {
Id expectedId = TestSobjectBuilderTests.mockId(Account.sObjectType);
DateTime expectedLastModifiedDate = DateTime.now();
Id expectedLastModifiedById = UserInfo.getUserId();
builder = new TestSobjectBuilder(Account.class);
Account actual = (Account) builder.add(Account.id, expectedId)
.add(Account.lastModifiedById, expectedLastModifiedById)
.add(Account.lastModifiedDate, expectedLastModifiedDate.formatGmt(ISO_DATE_TIME))
.build();
System.assertEquals(expectedId, actual.id);
System.assertEquals(expectedLastModifiedById, actual.lastModifiedById);
System.assertEquals(truncateMilliSeconds(expectedLastModifiedDate), actual.lastModifiedDate);
}
@isTest static void build_WhenGivenContactTypeAndAddAllFields() {
Id expectedId = TestSobjectBuilderTests.mockId(Contact.sObjectType);
String expectedFirstName = 'John';
String expectedLastName = 'Doe';
builder = new TestSobjectBuilder(Contact.class);
Contact actual = (Contact) builder.addAll(new Map<SObjectField, String> {
Contact.id => expectedId,
Contact.firstName => expectedFirstName,
Contact.lastName => expectedLastName
}).build();
Assert.equals(expectedId, actual.id);
Assert.equals(expectedFirstName, actual.firstName);
Assert.equals(expectedLastName, actual.lastName);
}
@isTest static void build_WhenGivenContactTypeAndAddAllStringFields() {
Id expectedId = TestSobjectBuilderTests.mockId(Contact.sObjectType);
String expectedFirstName = 'John';
String expectedLastName = 'Doe';
builder = new TestSobjectBuilder(Contact.class);
Contact actual = (Contact) builder.addAll(new Map<String, String> {
'ID' => expectedId,
'firstName' => expectedFirstName,
'LastName' => expectedLastName
}).build();
System.assertEquals(expectedId, actual.id);
System.assertEquals(expectedFirstName, actual.firstName);
System.assertEquals(expectedLastName, actual.lastName);
}
@isTest static void build_WhenGivenContactTypeAndAddMockId() {
builder = new TestSobjectBuilder(Contact.class);
Contact actual = (Contact) builder.addMockId().build();
System.assertNotEquals(null, actual.id);
}
static DateTime truncateMilliSeconds(DateTime dt) {
return DateTime.newInstance(MILLIS_PER_SECOND * (dt.getTime() / MILLIS_PER_SECOND));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment