I have an apex class that creates a custom field on an sObject via an HttpRequest and I'm not sure how to write a test for this because one of the things I need to test is that the field has the correct value, so a mock or a bypass won't work. I can't get enough code coverage for this class if I exclude the web service call. Not sure where to go with this.

This is the request portion of the class in question:

public class RollupFieldCreator {

  public static void createRollupField(String fieldName, String description) {
      String fieldApiName = fieldName.trim().toLowerCase().replace(' ', '_') + '__c';

      if (!Schema.SObjectType.Asset.fields.getMap().keySet().contains(fieldApiName)) {
          String objectApiName = 'Asset';
          String toolingApiEndpoint = '/services/data/v41.0/tooling/sobjects/CustomField/';

          HttpRequest createField = new HttpRequest();
          createField.setHeader('Authorization', 'Bearer ' + UserInfo.getSessionId());
          createField.setHeader('Content-Type', 'application/json');
          createField.setEndpoint(URL.getSalesforceBaseUrl().toExternalForm() + toolingApiEndpoint);
          String fieldDef = '{"Metadata" : {'
              + '"type" : "Text", "description" : "' + description
              + '", "inlineHelpText" : "", "precision" : null, "label" : "' + fieldName
              + '", "length" : 255, "required" : false}, '
              + '"FullName" : "' + objectApiName + '.' + fieldApiName + '"}';
          Http httpInstance = new Http();
          HttpResponse response = httpInstance.send(createField);
  • 1
    You cannot use tooling API in live/prod to create/alter metadata in live org, That being said you cannot create new fields in test environment, I wonder you have to bypass it using test.isRunningTests. Sep 20 '18 at 19:29
  • 1
    How are AppExchange packages able to create new fields then?
    – nicolevy
    Sep 20 '18 at 19:40
  • 1
    The dont create fields via APEX, they package their fields. If you really wanna create a field in Apex there is an ugly to hack to create a Metadata package and then hit deployment endpoint to get it validated and deployed. eg. github.com/afawcett/declarative-lookup-rollup-summaries . I wont advise going that way. Sep 20 '18 at 19:43
  • There are packages that allow you to create custom fields on objects. This is one: appexchange.salesforce.com/… I am creating a metadata package to create the field currently. I'm not sure how to get enough coverage in the test now, though.
    – nicolevy
    Sep 20 '18 at 19:47
  • Aint i just said the same thing a comment above? SRC: salesforce.stackexchange.com/q/32016/19118 Sep 20 '18 at 19:48

AppExchange apps that modify production (e.g. deploying new fields or code) use the Metadata API, not the Tooling API. The Tooling API is intended for development purposes. You will need to write your code in a way that uses the Metadata API instead. You can see the source code for Rollup Helper to see how it is done in Apex Code; this package also demonstrates code coverage as well for such an app.

  • There are something you can create via Tooling directly in Production, interestingly enough. Not sure if it's a good thing to actually use them in Production though.
    – Charles T
    Sep 20 '18 at 20:47

The normal way to add fields is to include the new field in the new managed package version. (The same also applies where e.g. the Ant deploy task is used.) Typically multiple other components will change at the same time:

  • Apex code if the new field is important to the Apex logic of the managed package
  • Object layouts
  • Any profiles or permission sets also in the package

Making all these changes via the Metadata API would be painful and a new install will update everything fully automatically.

Things are not so good for version upgrades though as layouts will not automatically change as they are assumed to have perhaps been locally manually edited. But in real upgrades, typically locally created layouts and profiles or permission sets will be in use. Manual instructions are normally provided so customers can edit those as only the customer will know their exact use and whether or not to include the new field. In general, upgrades are a painful area that can't be fully handled by automation. So I suggest to not try (or find some magical 3rd party product that handles all the problems).


To answer more directly the question of how you unit test this, the answer is that you can use mocks and the Stub API.

You say: I can't get enough code coverage for this class if I exclude the web service call.

And also you say: one of the things I need to test is that the field has the correct value, so a mock or a bypass won't work

The thing is, you can't actually test the end-to-end behaviour because your unit tests cannot modify the org itself. So you can only assert against the things that are allowed to happen in unit tests.

The method you have pasted here can totally work with a mock, no problem. You can't test that it actually created the metadata of course. But suppose you decompose this @future method into a non-future method that returns whether the HTTP request was successful, and a future method that calls it. Now you can get coverage on the non-future method and assert it returned the right value (provided by your mock class), and you can get coverage on the one-line future method even though there's nothing to assert.

If you have code that you absolutely cannot cover unless it can definitively tell you that a field was created, then what you'd need to do is outsource the question of whether the field exists to a helper class. Then use the Stub API to inject a mock of that class into the class you're trying to cover, so when it checks if the new field exists (it doesn't), it gets a fake yes back.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.