6

I have a page which displays an entity that belongs to another package. In order not have a hard dependency to this package, I build a wrapper object and make this wrapper object accessible to the page via a custom controller.

So it looks like this:

public class UsageStatsWrapper {
    public Decimal SummActions{get;set;} // 
    public Decimal AM_Details{get;set;} // 

    UsageStatsWrapper(SObject objectInstance) {
        this.SummActions = (Decimal)objectInstance.get('OTHERPCK__Actions__c');
        this.AM_Details = (Decimal)objectInstance.get('OTHERPCK__AM_Details__c');
        // ...
        // ...

I want to test this (have to, to make the 75%).

The problem is I can't instantiate the SOBject in my test code. I also, can't really do something like:

Schema.getGlobalDescribe().get(ObjectName).newSObject() ; 

As that can ends up putting a dependency to the other package.

Any tips?

2 Answers 2

3

This is a good question, I can think of two options, to Mock this situation.

Mock Custom Object: Here you create in your package an object that is identical to that in the external package. You then use the Test.isRunningTest to conditionally apply the namespace prefix or not at runtime. Since your code would never reference this mock object you don't have to package it in your package. To avoid your tests failing in the subscriber org you would have to have them fail gracefully as your Mock test object will not be present.

Mock Apex Code: This one attempts to wrap / emulate the SObject methods get and put, behind an Apex interface. e.g. ISObject.get, ISObject.put and ISObject.getSObject. This would require you using this in your UsageStatsWrapper class as apposed to SObject directly, e.g. UsageStatsWrapper(ISObject objectInstance). You would then have two implementations of this interface, SObjectRuntimeImpl and SObjectTestImpl. Your test implementation class constructor (used by your test code) can be given the field values explicitly and when asked via its 'get' method return them. The runtime implemetnation of the class would receive the SObject itself and the 'get' method simply delegate to the real SObject.get method. This would allow you to get more coverage over UsageStatsWrapper.

Summary: The Mock Custom Object route requires less changes to your existing code, but may grow in complexity for you beyond what you are showing in your question. The Mock Apex route is more invasive in your code to retrofit potentially, but does avoid creating a custom object. I've used both routes effectively.

4

This shouldn't actually cause a dependency on the object named by ObjectName (which may be in another package):

Schema.getGlobalDescribe().get(ObjectName).newSObject();

I am working on a prototype at the moment using this technique.

Actually, in my prototype I don't even have the base package installed in my packaging org - as in this prototype I wanted to see how far I could go avoiding any package dependencies. I have unmanaged proxy objects in my packaging org for test purposes, and these objects are not included in my package. The application can work with either unmanaged objects or managed packaged objects (naturally I need to prefix the namespace in the latter case).


So, this is essentially the same as Mock Custom Object in Andy's answer. In my case, rather than branching on Test.isRunningTest, I check for the namespace prefix in a (known) key in the global describe map, and the entire app switches between mock objects and actual managed objects - this means that apex tests succeed in either context.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .