1

I'm getting null in my code here, while running a unit test, but in Execute Anonimous Window there is a right value in System.debug(). What the hell? Maybe I should run the test as System Administrator? If so, how to do that?

public String getName(){
    Id adminProfileId = [SELECT Id FROM Profile WHERE Name = 'System Administrator'].Id;
    TestOpportunity__c customSettingsForTestOpp = TestOpportunity__c.getInstance(adminProfileId);
    String nameOfTestOpp = customSettingsForTestOpp.Opportunity_name__c;
    //Here is my NULL for unit tests and the right value for Execute Anonimous Window:
    System.debug(nameOfTestOpp);
    return nameOfTestOpp;
}

3 Answers 3

2

What both @Bilal and @Samir have said is accurate and true. However, I strongly recommend you try to avoid inserting custom settings instances into the database as part of your unit testing; we used to do this and it basically prevented us from using parallel test execution.

There is an "edge case" (or perhaps infrastructure bug) in the way that Salesforce manages the transaction isolation of inserts of custom settings into the database - we would find that two potentially entirely independent tests that happened to run in parallel and where both inserted a custom setting instance (for the same custom settings type) into the database could randomly fail with a database locking error.

The solution we applied was to ensure that all access to custom settings instances was indirected via a class, along these lines (this is actually simplified compared with what we do, but gives an idea of the approach):

public class Settings {
    /**
     * The cached example settings instance.
     */
    private static Example_Settings__c exampleSettings = null;

    /**
     * Returns the org default settings instance for example settings. This value
     * is cached, so any updates to the instance during a given session/request
     * against the Salesforce org will be retained through to the end of the session.
     */
    public static Example_Settings__c getExample() {
        if (exampleSettings == null) {
            // There's no cache settings instance, so get one from the database
            exampleSettings = Example_Settings__c.getInstance();

            if (exampleSettings == null) {
                // There's none in the database so construct a "default" instance
                exampleSettings = (Example_Settings__c) Example_Settings__c.getSObjectType().newSObject(null, true);
            }
        }

        return exampleSettings;
    }
}

Because this code basically caches the settings in memory, this works nicely where the unit test wants to initialize the settings that the production code will then use, like:

@IsTest
void testWithSettings() {
    Example_Settings__c exampleSettings = Settings.getExample();

    // This updates the cached instance, so when the production code gets the settings
    // using Settings.getExample() the production code will see these values
    exampleSettings.Some_Setting_Value__c = 123;
    exampleSettings.Some_Other_Setting_Value__c = 'abc';

    // So now you can call the production code and it will see the values 123 and abc
    ...
}

Clearly this could be quite a task to implement, but you will avoid problems caused by this "edge case" behaviour whilst still being able to run tests in parallel.

3
  • Is this documented?
    – Samir
    Commented Mar 10, 2020 at 14:26
  • 1
    Not come across it in documentation, just empirically. It really did cause us a lot of trouble.
    – Phil W
    Commented Mar 10, 2020 at 14:53
  • +1. I work on a very large managed package that does insert custom settings and the impact on Apex unit testing time is very substantial.
    – David Reed
    Commented Mar 11, 2020 at 13:39
2

Just like normal SObjects, your Test Context doesn't have access to the custom setting records already in the database.

solution 1 (Recommended)

You need to insert a new Custom Setting record, in your test context like you would normally do with an SObject

Whatever_custom_setting__c setting = new Whatever_custom_setting__c();
setting.Name = 'Test Setting';
setting.Value__c = 'Whatever';
insert setting;

and then your function should return the newly created test setting.

solution 2 (Not Recommended)

Annotate your method with the @isTest(SeeAllData=true). This way your testmethod has visility over the data in your database, outside of the test context. This solution can fail in production if the records are not found.

2

You need to insert/create custom setting as test data like you setup other test data in your apex test class. custom setting also behave like sObject. so the original data in them is not available until you set (SeeAllData=true) or insert custom setting data in test class.

You can insert custom setting in your test class like following.

TestOpportunity__c customSettings = TestOpportunity__c.getInstance(adminProfileId);
customSettings.Opportunity_name__c = 'What ever you want to setup';
insert customSettings;

You must log in to answer this question.

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