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
Mitchell McConnellMitchell McConnell 

List has no rows for assignment to SObject

Yes, I know this is out there, but I still do not understand what is going on.  I am pretty new to SF, but have a lot of development experience in Java and other languages.

I am working in my free developer account, and wanted to create my first Apex code using a trigger.  Here is my business process.  I added a custom field to Account called Customer Level.  It is a picklist of "NORMAL" and "PREFERRED", with the default being NORMAL.   When the level is changed to PREFERRED, I want to create a trigger to create a new custom object called Preferred Customer Data, which has a one-to-one relationship with an Account (so there is a reference back to the account).  I put in another data field called Preferred Level which is a picklist of BRONZE, SILVER, and GOLD, with the default at creation being BRONZE.

So using the online Apex documentation, I dove in.

First, though, I created my objects and fields via the GUI.

I created a class with a static method that took an account ID and a level, and which creates the Preferred Customer object with the specified level.

public class UpdatePreferredCustomer{

    public static String CreatePreferredCustomerData(String accountId, String level) {
        Account acc = [select Id, Name, Customer_Level__c, AccountNumber
                       from Account where id=:accountId];
       
        if (acc == null) {
            System.debug('>>>> ERROR: could not find account');
        }
       
        System.Debug('>>>> Found account Id: ' + acc.Id + ', Name: ' + acc.Name);
        System.Debug('>>>> Customer level: ' + acc.Customer_Level__c);
       
        Preferred_Customer_Data__c pref = new Preferred_Customer_Data__c();
       
        pref.Name = 'Preferred data, test6';
        pref.Preferred_Level__c = level;
        pref.Account__c = accountId;
       
        // NOTE: the Preferred_Customer_Data custom object uses the Account's
        // AccountNumber as a unique external reference ID so we do not create
        // multiple records
       
        pref.Account_Number__c = acc.AccountNumber;
       
        upsert pref Account_Number__c;

        System.Debug('>>>> Upserted preferred customer level: ' + level);
       
        return 'SUCCESS';
    }
}

I next tested this method manually via the Developer Console and saw that it worked.

Next, I created a trigger for both after insert and after update, as it seemed like I need to cover both of those cases.

trigger MyAccountTrigger on Account (after update) {
   
    if (Trigger.isInsert) {
        System.debug('>>>> got insert trigger');
    } else {
        System.debug('>>>> got update trigger');
    }
   
    for (Account a : Trigger.new) {
        System.debug('>>>> Found account in trigger: ' + a.Name);
        String s = UpdatePreferredCustomer.CreatePreferredCustomerData(a.ID, 'BRONZE');
    }
}

Finally, I created a test class.

@isTest
public class UpdatePreferredCustomerTestClass{
    static testMethod void validateUpdatePreferredCustomer() {
        // Create a test account
       
        Account acc = new Account(Name='PreferredTestCustomer',
                                 AccountNumber='7275551212',
                                 Customer_Level__c='NORMAL');
       
        System.debug('>>>> Inserting account');
       
        // Insert
        insert acc;
       
        // Retrieve the new account
        acc = [SELECT ID FROM Account where Id =:acc.Id];
       
        // Now set the Customer Level.. this should trigger the creation
        // of the PreferredCustomer object via our trigger.
        
        acc.Customer_Level__c = 'PREFERRED';
        upsert acc;
       
        // Validate that the Preferred Customer Object got created, and
        // that the preferred level was set initially to BRONZE
        
        Preferred_Customer_Data__c pref = acc.Preferred_Customer_Data__r;
       
        System.debug('>>>> Get the Preferred Customer Object');
       
        System.debug('>>>> Preferred Customer level: ' + pref.Preferred_Level__c);
       
        System.assertEquals('BRONZE', pref.Preferred_Level__c);
       
    }
}

So, as my title says, when I run this, I get the aforementioned error.

The error line is pointing to this line in the test class:

     Preferred_Customer_Data__c pref = acc.Preferred_Customer_Data__r;

This made me think that there could be a race condition, because it *seems* as though the Preferred Customer Data
object *is* being created, yet it is not there when I go to look for it in the test class.

I would greatly appreciate any suggestions.  I am sure this is a simple issue that I am missing.

Mitch

SFDC_DevloperSFDC_Devloper
Hi ..

Try below code,
@isTest
public class UpdatePreferredCustomerTestClass{
    static testMethod void validateUpdatePreferredCustomer() {
        // Create a test account
       
        Account acc = new Account(Name='PreferredTestCustomer',
                                 AccountNumber='7275551212',
                                 Customer_Level__c='NORMAL');
       
        System.debug('>>>> Inserting account');
       
        // Insert
        insert acc;
       
        // Retrieve the new account
        List<Account> acc = [SELECT ID,Preferred_Customer_Data__c FROM Account where Id =:acc.Id];
       
        // Now set the Customer Level.. this should trigger the creation
        // of the PreferredCustomer object via our trigger.
        
        acc[0].Customer_Level__c = 'PREFERRED';
        upsert acc;
       
        // Validate that the Preferred Customer Object got created, and
        // that the preferred level was set initially to BRONZE
        
        Preferred_Customer_Data__c pref = acc[0].Preferred_Customer_Data__r;
       
        System.debug('>>>> Get the Preferred Customer Object');
       
        System.debug('>>>> Preferred Customer level: ' + pref.Preferred_Level__c);
       
        System.assertEquals('BRONZE', pref.Preferred_Level__c);
       
    }
}


Thanks,
Rockzz
Maciej SobkowiczMaciej Sobkowicz
Hi Mitch,
The first issue that I can see in your code is that in your test you're not querying for the related list: Preferred_Customer_Data__r after upserting acc. Just add to your query additional SELECT that will fetch related objects:
// Retrieve the new account
acc = [SELECT ID, (SELECT Id, Preferred_Level__c FROM Preferred_Customer_Data__r) FROM Account where Id =:acc.Id];
Also please double check that Preferred_Customer_Data__r is the relation name.

This should partially help but you wil still have an issue of assigning List to Preferred_Customer_Data__c. Therefore, In your test you should add:
Preferred_Customer_Data__c pref = acc.Preferred_Customer_Data__r.get(0);
Please let me know if that helped.

Maciek
Mitchell McConnellMitchell McConnell
Rockzz,

This is failing to compile, because there is no filed 'Preferred_Customer_Data__c' on the Account.  There *is* a Preferred_Customer_Data__r however, because the account holds a reference to the Preferred Customer Data object.

I removed that, as it was not being used, and also changed the List name to accList to avoid conflict with the other variable named 'acc'.

Doing this, however, did not change the original behavior and error.

Does my original question, about the race condition, make sense?  The same piece of code is inserting, then immediately upserting, then (3rd) expecting to see the new object right awy.