2

I have a Salesforce test class with @TestSetup alongside two test methods. In the last method (testQueryDeletedRecord), I attempt to query a record that I deleted in a previous test method (testDeleteRecord). However, despite deleting the record, the query in testQueryDeletedRecord does not return null. Instead, it returns the deleted record. Can someone explain why this is happening? Here's my test class for reference:

@isTest
private class TestClassExample {
    
    @TestSetup
    static void setupTestData() {
        // Create test records
        Account acc = new Account(Name = 'Test Account');
        insert acc;
    }

    @isTest
    static void testDeleteRecord() {
        // Retrieve the test Account created in setupTestData
        Account testAcc = [SELECT Id, Name FROM Account WHERE Name = 'Test Account' LIMIT 1];
        System.assertEquals('Test Account', testAcc.Name);
        
        // Delete the test Account
        delete testAcc;
    }

    @isTest
    static void testQueryDeletedRecord() {
        // Attempt to query the deleted Account
        Account queriedAcc = [SELECT Id, Name FROM Account WHERE Name = 'Test Account' LIMIT 1];
        // Assert that the queriedAcc is null since the record has been deleted
        System.assert(queriedAcc == null, 'The result is not null');
    }
}

1 Answer 1

2

Each individual test is run in its own "environment", so to speak.

Between each test, all the changes from the previous tests are reverted and you're starting with a clean slate. So the record you inserted in your @SetupTest method is restored for the second test.

This is a Good Thing (TM) because I believe that test methods are not guaranteed to run in any particular order. Even if there were an order, writing tests that can be run independent of order seems like a good idea.

To get things working as you want them to here, you could always just call testDeleteRecord() (it's a method, just like any other method) inside of testQueryDeletedRecord() before you run the query. That feels like poor practice to me, but it is possible.

5
  • 3
    Also, queries will never return null. Assigning a result of a query to a single SObject instance is a bit of syntactic sugar that Salesforce provides. If you do call testDeleteRecord() from testQueryDeletedRecord(), you'd get a No rows for assignment to SObject exception. Putting the result of a query into a list is the safe way to go about things.
    – Derek F
    Commented May 13 at 21:33
  • 1
    Unless the new Null Coalescing Operator is used, in which case a query for a single record can return null. e.g Account myAccount = [SELECT Id FROM Account WHERE Id = :myVar] ?? null;
    – Nick C
    Commented May 13 at 23:35
  • 1
    @NickC Not quite. You'd still get the exception because a query will never return null, so it'll always take the left arg.
    – Derek F
    Commented May 14 at 1:48
  • 1
    are you sure about that? I haven't used this much, but if I do System.debug([SELECT Id FROM Account WHERE Id = '001000000000123'] ?? null); in execute anonymous, it outputs null without throwing an exception.
    – Nick C
    Commented May 14 at 2:09
  • 1
    @NickC looks like you're right. It's even documented. Apex supports assignment of a single resultant record from a SOQL query, but throws an exception if there are no rows returned by the query. The null coalescing operator can be used to gracefully deal with the case where the query doesn’t return any rows. If a SOQL query is used as the left-hand operand of the operator and rows are returned, then the null coalescing operator returns the query results. If no rows are returned, the null coalescing operator returns the right-hand operand.
    – Derek F
    Commented May 14 at 2:20

You must log in to answer this question.

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