0

I have what I think is a simple controller and test class, but when I try to compile the test class, I get an error related to my deleteAddress() method:

System.QueryException: List has no rows for assignment to SObject

Is there something I am missing in my test class?

Edit: I've changed by test class (updated below) as follows: -- Moved Test.startTest(); to after the insert of testAcctAddr -- Added 3 new lines to assign the DelAddrId (24-26) -- Added an assertion at line 29

I am still getting the same error where the DelAddrId is not being populated for the deleteAddress method in my controller.

Test Class:

    @isTest 
    private class Test_AcctAddressesController {
    
        static testMethod void testMethod1() 
        {
            Account testAccount = test_CreateRecords.createAcct(0);
            insert testAccount;
            
            Contact testContact = test_CreateRecords.createCont(testAccount.Id);
            insert testContact;
            
            Account_Addresses__c testAcctAddr = new Account_Addresses__c();
                testAcctAddr.Account__c = testAccount.Id;
                testAcctAddr.Street__c = '1010 Test Ave';
                testAcctAddr.City__c = 'New York';
                testAcctAddr.State__c = 'NY';
                testAcctAddr.Zip_Postal_Code__c = '11212';
                testAcctAddr.Country__c = 'USA';
                testAcctAddr.Primary__c = TRUE;
                testAcctAddr.Current__c = TRUE;
            insert testAcctAddr;
        Test.StartTest();
    
        PageReference testPage = Page.Acct_Addresses;
            Test.setCurrentPage(testPage);
            testPage.getParameters().put('DelAddrId', String.valueOf(testAcctAddr.Id));   
            ApexPages.StandardController AcctAdd = new ApexPages.standardController(testAccount);
                AcctAddresses AcctAdd1 = new AcctAddresses(AcctAdd);
System.assertNotEquals(null,AcctAdd1.deleteAddress());
            AcctAdd1.save();
            AcctAdd1.deleteAddress();
        Test.StopTest();
        }
    }

Controller:

    public class AcctAddresses {     
    
        public List<Account_Addresses__c> addresses{get;set;}
        public string DelAddrId { get; set;}
        public Account accounts {get;set;} 
        public Account acct {get;set;} 
    
    //Constructor 
    
        public AcctAddresses(ApexPages.StandardController controller) { 
    
            acct = (account)controller.getRecord();      
    
            accounts = [SELECT Id
                        FROM account
                        WHERE id=: acct.id LIMIT 1]; 
    
            addresses = [SELECT Id, Primary__c, Current__c, Street__c, City__c, State__c, Zip_Postal_Code__c, Country__c, Account__c
                        FROM Account_Addresses__c
                        WHERE Account__r.Id = :accounts.id ORDER BY Primary__c, State__c];     
    } 
    
    //This method is to edit the existing contact record while clicking the Edit link 
    
            public pageReference Save(){
                update addresses;
            return ApexPages.CurrentPage();
            }
    
    //This method is to delete the contact record while clicking the Del link 
    
        public pageReference deleteAddress(){
    
            Account_addresses__c ToBeDeleted = [SELECT Id FROM Account_addresses__c WHERE id = : DelAddrId LIMIT 1]; 
                delete ToBeDeleted; 
                    String baseUrl = URL.getSalesforceBaseUrl().toExternalForm(); 
                    PageReference redirectPage = new PageReference(baseUrl+'/'+acct.id); 
                return redirectPage;  
        }   
    }
  • Are you sure you're getting a QueryException at compile time and not when you run the test? DelAddrId is null by default, you'll need to set it before calling deleteAddress(), otherwise you will always get that exception because you're searching for a record that can't exist. – nbrown Sep 9 at 19:52
2

You're trying to run the following query

[SELECT Id FROM Account_addresses__c WHERE id = : DelAddrId LIMIT 1]

But ask yourself, are you setting delAddrId anywhere in your test method?
Does any other code you're executing set that property?

If the answer to both of those questions is "no" (and that is indeed the case here), then you have a problem.

Remember, in a unit test, you are responsible for making sure that all of the data that the code you're testing needs to be able to run is set up and in place.

Setting delAddrId somewhere (in the code you're testing or, more likely, in a test method) should alleviate most of the issue here, but there's more you should be thinking about:

  • Assigning the result of a query to a single SObject carries some risk (it can result in the error you're currently seeing). It led you (well, us) to discover a different issue in your code, but generally you should be assigning the result of a query to a List
  • You aren't making any assertions. Assertions are one of the most important parts of a unit test. Without assertions, you don't know if your code is behaving appropriately. You could have a method called twoPlusTwo() that returns 5 instead of 4. You should make assertions against the results/output of the code your test method is testing. In this case, you are trying to delete a record, so your assertion should query for the Id of the record you wanted to delete to make sure that it actually was deleted
  • You don't have enough test methods. Coverage is nice, but a good test class doesn't necessarily focus on that. Instead, it will test many different scenarios, both positive and negative. A "positive" test would be something like "if I ask for this record to be deleted, is it actually deleted?". A "negative" test would be something like "if I fail to provide an Id to delete, does my code crash, throw an exception that is not caught, or does it handle the situation without failing?". By testing enough scenarios, your coverage will naturally be high.

Also, you don't need to query to update or delete a record. Having the Id is enough. delete new MyObject__c(Id = '012000000000123'); is enough to get the job done.

| improve this answer | |
  • Thanks Derek. I know the test is flawed. I was just trying to get it to cover initially and then break it down into components. For the delete, don't I still need to call the deleteAddress method from my controller? – J. Neilan Sep 9 at 21:06
  • @J.Neilan Yes, you do. You'd need to do something like AcctAdd1.delAddrId = testAcctAddr.Id; in your test class before you try calling the deleteAddress() method in your controller being tested (the "system under test", or SUT). In other words, you need to make sure the data you need is available/in place before you need it. David's (and my) suggestion to just use the Id instead of querying for the record is a suggestion that you'd apply to the deleteAddress() method itself. – Derek F Sep 9 at 21:47
  • I tried that both ways (AcctAdd1.delAddrId = testAcctAddr.Id & AcctAdd1.delAddrId = 'a221h000001iCRxAAM' but I still get the same error when calling the deleteAddress method. – J. Neilan Sep 9 at 22:14
  • @J.Neilan At this point, it might be helpful to edit your question to add a bit in a new section (don't overwrite the details in the question you have right now, append additional information) about the changes you've made based on David's and my answers. Also, please be sure to specify whether you've made the changes to your SUT, your test method, or both. – Derek F Sep 9 at 22:55
  • Thanks Derek. I added the additional edits above and I am still getting the same error. Any additional advice would be appreciated. – J. Neilan Sep 10 at 13:05
3

You are not populating DelAddrId, so it's null. When you run this query

Account_addresses__c ToBeDeleted = [SELECT Id FROM Account_addresses__c WHERE id = : DelAddrId LIMIT 1]; 

there are no matching records, so you get an exception since you're assigning to a single sObject instance.

You don't need to query here - you can just do delete new Account_Addresses__c(Id = DelAddrId) - but you do need to populate the Id value.

| improve this answer | |
  • Thanks David. That makes sense, but don't I need to call the "deleteAddress" method in my controller in order to test it? – J. Neilan Sep 9 at 21:05
  • Yes, you do need to call the method. – David Reed Sep 9 at 21:07
  • I populated the DelAddrId and added the line you provided, but when I call the method to deleteAddress, it still has no rows. I'm not sure how to assign the delete ID to that method. – J. Neilan Sep 9 at 21:19
  • You just need to assign to that property of the controller in your unit tests. – David Reed Sep 9 at 21:51

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.