function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
Chris Walters 9Chris Walters 9 

unit test not seeing object instantiated in testsetup

version 47.0

Trying to craft a unit test. Using @testSetup method, which instantiates some objects within a System.RunAs to avoid the DML problem. But (at least) one of my objects instantiated within the System.RunAs is not visible to the test method even tho it does indeed get instantiated and inserted in the test setup method
@isTest
public class TaskHandlerTest {
    public static Id profileId  = [SELECT Id FROM Profile WHERE Name ='ASUO API User Profile'].Id;
    public static User thisUser = [SELECT Id FROM User WHERE Name = 'API ASU EdPlus ASUO'];
    
    public static UserRole testUserRole_ASU;
    public static Contact contact_ASU;

    @testSetup
    static void setUpTest() {

        testUserRole_ASU = new UserRole(
            Name = 'ASU Local',
            OpportunityAccessForAccountOwner = 'Edit'
        );
        insert testUserRole_ASU;
        System.RunAs( thisUser) {
            contact_ASU = new Contact(
                FirstName = 'Pete',
                LastName = 'Townshend',
                Email = 'thewho1@who.com',
                Last_Contact_Method__c = 'text',
                Last_Contact_Attempt__c = Datetime.valueOf('2020-04-04 00:00:00')
            );
            insert contact_ASU;
            System.debug('contact_ASU inserted with Id: ' + contact_ASU.Id);
        }
    }

    @IsTest
    static void test_pass_new_EmailOutbound() {
        Test.startTest();
        TestDataFactory.createCustomSetting('Object Triggers');
        Task task = new Task(
            Description = 'pass_new_EmailOutbound',
            TaskSubType = 'Email',
            Email_Direction__c = 'Outbound',
            WhoId = contact_ASU.Id,  // <--- throws "dereferencing null value" msg here
            Actual_End__c = Datetime.valueOf( '2020-05-20 00:00:00')
        );
        insert task;  // should trigger TaskHandler after_insert()

        System.assertEquals( contact_ASU.Last_Contact_Method__c, 'Email');
        System.assertEquals( contact_ASU.Last_Contact_Attempt__c, task.Actual_End__c);
        Test.stopTest();
    }
}
contact_ASU is declared static outside of setUpTest().
The debug log shows my contact_ASU does get instantiated and inserted and has an Id.  Both setUpTest and test_pass_new_EmailOutbound() are declared static.

So I would expect contact_ASU to be visible to test_pass_new_EmailOutbound() --  what am I doing wrong/ not doing right ?

TIA,

Chris

 
Best Answer chosen by Chris Walters 9
Andrew GAndrew G
Hi Chris

Reference:
https://success.salesforce.com/ideaView?id=08730000000Dj51AAC
From Josh Kaplan @salesforce

We intentionally clear out static variables between each test method.  If we did not, each test would cease to be an independent trial. You could modify the static in one test method, which would make the order in which tests operate relevant to the results.  This is precisely what you don't want - data dependent tests.

If you want information that is common to all tests, it can be inserted in the test setup method and queried in each test method.  The idea here is not to reduce the number of SOQL queries, it is to reduce the amount of data being inserted into the system.  If you insert 1000 records in test setup, run fifteen test methods, and you run a query 15 times to get the 1000 records each time, that's still less expensive (and faster) than inserting 1000 records 15 times.


So, even though you have declared as public and then set it, it is cleared for each test method run.

So, you would need to declare it in the Test Setup, and then do a query each time to grab the Test record.

I suspect that the compiler you are using (the same as mine) is not aware of this vagary in the way that the Test Methods work, and simply looks at the syntax to determine if it should throw an error during the code save.


Regards

Andrew
 

All Answers

Andrew GAndrew G
Hi Chris

Reference:
https://success.salesforce.com/ideaView?id=08730000000Dj51AAC
From Josh Kaplan @salesforce

We intentionally clear out static variables between each test method.  If we did not, each test would cease to be an independent trial. You could modify the static in one test method, which would make the order in which tests operate relevant to the results.  This is precisely what you don't want - data dependent tests.

If you want information that is common to all tests, it can be inserted in the test setup method and queried in each test method.  The idea here is not to reduce the number of SOQL queries, it is to reduce the amount of data being inserted into the system.  If you insert 1000 records in test setup, run fifteen test methods, and you run a query 15 times to get the 1000 records each time, that's still less expensive (and faster) than inserting 1000 records 15 times.


So, even though you have declared as public and then set it, it is cleared for each test method run.

So, you would need to declare it in the Test Setup, and then do a query each time to grab the Test record.

I suspect that the compiler you are using (the same as mine) is not aware of this vagary in the way that the Test Methods work, and simply looks at the syntax to determine if it should throw an error during the code save.


Regards

Andrew
 
This was selected as the best answer
Chris Walters 9Chris Walters 9
Thank You Andrew for that useful tidbit - I am indeed relying on what the compiler is telling me ( the fool!)  . Our existing unit tests are written in four different styles over who knows how many versions. Uff-da! I'll read the link and give it a try
David Zhu 🔥David Zhu 🔥
When testMethod runs, it run testSetup method first to generate data.

In your code, contact_ASU is created by user "API ASU EdPlus ASUO" in testSetup method setupTest.

When test_pass_new_EmailOutbound runs, it runs as the current user context. I assume it would be you and you are the admin.
Admin should have view all permission on Contact object. So,contact_ASU is visible in test_pass_new_EmailOutbound method.

It is like, in the real word, API ASU EdPlus ASUO user creates a contact record; As an admin, you are abel to see the record.