When unit testing code that interacts with SObjects in other managed packages, being able to mock those SObjects would be helpful so the packages don't have to be installed to run the unit tests. The context here is code that makes use of dynamic SOQL and the generic get
/put
methods of SObject i.e. code that is not tied to the other managed packages at compile time.
So it would be great if this was possible in tests:
@IsTest
private class MocksTest {
@IsTest
static void getSObjectType() {
SObjectType t = Mocks.getSObjectType('abc__Def__c');
System.assertNotEquals(null, t);
}
@IsTest
static void newSObject() {
SObject sob = Mocks.newSObject('abc__Def__c');
System.assertNotEquals(null, sob);
}
}
where the test could simulate some aspects of the missing SObjects:
public inherited sharing class Mocks {
public class MyProvider implements System.StubProvider {
public Object handleMethodCall(...) {
...
}
}
public static SObjectType getSObjectType(String type) {
return (SObjectType) Test.createStub(SObjectType.class, new MyProvider());
}
public static SObject newSObject(String type) {
return (SObject) Test.createStub(SObject.class, new MyProvider());
}
}
But per the Build a Mocking Framework with the Stub API documentation this doesn't work with these errors resulting instead:
MocksTest.getSObjectType
Fail
System.TypeException: Test.createStub() can only be called with classes in the current namespace
and:
MocksTest.newSObject
Fail
System.TypeException: Test.createStub() can only be invoked on user defined types.
This mattaddy / SObjectFabricator project handles part of this problem relies on real Type
values so it can instantiate SObject
s like this:
private Type sType;
public SObject toSObject() {
return (SObject)JSON.deserialize(jsonString, sType);
}
I'm starting to think the only viable approach is to wrap SObjectType
and SObject
in local objects so I can mock those using Test.createStub
or just implement the mocking directly. But that makes me think TDD (Test Driven Damage).
If you have tackled this problem, please share your solution.
(Ideally Test.createStub
would be improved by Salesforce to support these two types, but presumably, that isn't trivial for them to do.)
Schema.getGlobalDescribe()
and then getting theSObjectType
by name does in fact create a soft reference that will still compile if the other package is not installed. I don't really follow where the problem is.