+ Start a Discussion

Bug in APEX unit-test functionality?

It appears that I have code that works perfectly in both a production and a sandbox environment, but does not work when called from a unit test that performs the same operations that work just fine using the GUI.


I've modified the CampaignCallDownController to have filters and a search box.  The below test adds one Contact and one Lead to a Campaign, then invokes the CampaignCallDown VF page and controller.  When I do those steps using the GUI in production or the sandbox, the call list shows two records, one Contact and one Lead.  But when I run this unit test, it says there are zero records in the call list.  I've tracked the problem down to this query in the CampaignCallDownController...


members = [select Id, ContactId, LeadId, Status, CampaignId, Level_of_Interest__c, 

                    Last_Action__c, Next_Action__c, Campaign.Name,

                    Lead.Id, Lead.Salutation, Lead.Title, Lead.Name, Lead.Company, Lead.Phone, 

                    Lead.Street, Lead.State, Lead.Email,

                    Contact.ID, Contact.AccountId, Contact.Salutation, Contact.Title, Contact.Name

                    Contact.Phone, Contact.MailingState, Contact.Email, Contact.Account.name

                    from CampaignMember where CampaignId = :camp AND Next_Action__c LIKE :desiredNA AND 

                    (NOT Next_Action__c LIKE :excludedNA) AND Level_of_Interest__c LIKE :desiredLI AND 

                    (NOT Level_of_Interest__c LIKE :excludedLI) AND

                    (Contact.Account.Name LIKE :searchFilter OR Contact.Name LIKE :searchFilter

ORContact.Email LIKE :searchFilter 

                    OR Contact.Title LIKE :searchFilter OR Contact.MailingState LIKE :searchFilter OR 

                    Lead.Name LIKE :searchFilter OR Lead.Company LIKE :searchFilter  OR 

                    Lead.Email LIKE :searchFilter OR Lead.Title LIKE :searchFilter OR Lead.State LIKE :searchFilter

                    ORDER BY Contact.Account.name LIMIT :recordsPerPage OFFSET :offset ];


I've highlighted the part of the query that performs the search function because that's where the problem is.  When that query gets called from the GUI, it will return all Leads *and* Contacts that match the searchFilter criteria.  But when called from the test, it will only return an empty list, not matter what search or filter criteria are input.  I've stripped the query criteria down to just the red and blue parts and found that if I remove one or the other (the Contact field matching or the Lead field matching criteria) it will return records when called from the below test.  In other words, if I remove the red part it will return 1 Lead in the test below.  Or, if I leave in the red part and remove the blue part, it will return 1 Contact in the test below.  But, if I leave both in, it will return zero leads and zero contacts.  When the same operations are performed in the GUI, it returns two records - both the Lead and the Contact.


Anybody know what's going  on?  Have I hit a bug in the way tests are executed by SFDC?  I'm about to pull my hair out - or write a unit test that is really a non-test!! :(



private class CampaignCallDownControllerTest {

     static testMethod void testNormalFlow(){
     	//get campaign member record type ID
     	RecordType rt = [select id,Name from RecordType where SobjectType='CampaignMember' and Name='Email Campaign Member' Limit 1];
        Campaign testCampaign       = new Campaign();
        testCampaign.name           = 'TestCampaign';
        testCampaign.CampaignMemberRecordTypeId = rt.ID;
        insert testCampaign;
        Account testAccount		= new Account();
        testAccount.name		= 'TestAccount';
        insert testAccount;  
        Lead testLead1          = new Lead();
        testLead1.FirstName     = 'LeadFirstName1';
        testLead1.LastName      = 'LeadLastName1';
        testLead1.Company       = 'Test1';
        insert testLead1;
        Contact testContact1         = new Contact();
        testContact1.FirstName       = 'ContactFirstName';
        testContact1.LastName        ='ContactLastName';
        testContact1.Email           = 'Adress1@Adress.com';
        testContact1.Title           = 'ContactTitle';
        testContact1.AccountID 		 = testAccount.ID;
        insert testContact1;
        //make campaign members
        CampaignMember testCM1       = new CampaignMember();
        testCM1.leadID               = testLead1.Id;
        testCM1.contactID			 = null;
        testCM1.campaignID           = testCampaign.Id;
        testCM1.Status               = 'Sent';
        testCM1.Level_of_Interest__c = 'Interested';
        testCM1.Next_Action__c 		 = 'contact now';
        insert testCM1;
        CampaignMember testCM3      = new CampaignMember();
        testCM3.ContactId           = testContact1.Id;
        testCM3.LeadID			 	= null;
        testCM3.CampaignId          = testCampaign.Id;
        testCM3.Status              = 'Sent';
        testCM3.Level_of_Interest__c= 'Interested';        
        testCM3.Next_Action__c 		= 'contact now';
        insert testCM3;
		// Check to make sure the contacts and leads made it into the campaign        
        testCampaign = [SELECT ID, NumberofContacts, NumberOfLeads FROM Campaign WHERE ID = :testCampaign.ID];       
        //begin tests
 		// instantiate page
 		PageReference pageRef = Page.CampaignCallDown;

        CampaignCallDownController ccdc1 = new CampaignCallDownController();
	System.assert(ccdc1.getHasCampaignID() == TRUE);
	System.assertequals(2, ccdc1.getLeadPlusTasks().size()); // FAILS HERE



Put a system.debug in your controller class to check if searchFilter variable has any value or its NULL.


How are you assigning value to this searchFilter variable, I dont see this field/variable being used in your test method.





Good question, Rahul.  In my effort to reduce the test down to something that wouldn't be a pain for forum readers to review, I cut this:


    System.assert(ccdc1.getSearchFilter() == '');


That checks to make sure that the search box is empty for the purposes of the test.  It is.  When the search box is empty, the CampaignCallDownController transforms the searchFilter value to be '%%' right before the query and then strips the '%' marks back off right after the query.  I do have a debug statement that confirms that is happening.  Here is the value of the searchFilter immediately before the query in question is called during the test run:


   |DEBUG|QV: searchFilter=%% [BEFORE QUERY]


And here it is right after the % signs have been stripped out of it after the query:


   |DEBUG|QV: searchFilter= [AFTER QUERY]


This way, the search box in the UI is still blank after the list has been reloaded.  If the search box on the CampaignCallDown page were populated with 'Smith' the CampaignCallDownController would add a '%' to either side of it right before the query and strip it back off right after the query so that the search box will continue to be populated with 'Smith' in the UI after the list reloads.  Like so:


|DEBUG|QV: searchFilter=%Smith% [BEFORE QUERY]

|DEBUG|QV: searchFilter=Smith [AFTER QUERY]

MJ Kahn / OpFocusMJ Kahn / OpFocus
Take a look at the Apex Language Reference's documentation of @isTest with SeeAllData. Chances are that your unit tests are written under Salesforce Spring 12 or later, in which case, by default, almost all data in your org is hidden from you. You'll have to change your unit test to create the data you need or set SeeAllData = true.

Thanks for the suggestion, MJ.  I do believe that I'm creating all the data I need in the test.  Do you see data needed for the test that is not being created in the test?  I'm even checking to make sure that the data I created for the test is available to me in the test class.  These assertEquals pass with no problem:


	// Check to make sure the contacts and leads made it into the campaign        
        testCampaign = [SELECT ID, NumberofContacts, NumberOfLeads FROM Campaign WHERE ID = :testCampaign.ID];       
MJ Kahn / OpFocusMJ Kahn / OpFocus

Sorry for not seeing the test data you're creating! Yes, it does look like you're creating the data you need, assuming the values for desiredNA, excludedNA, desiredLI, excludedLI, recordsPerPage, and offset are correct.


My next step would be to add some System.Debug statements to the controller to display all those values, then temporarily add the query that the controller is doing into the unit test, using the same values. If those values are correct, you should get the same results. Then start whittling away at the query in the unit test until it starts returning values. 


I suspect you'll find that when the controller is called from the unit tests, one of those values isn't being initialized as you expect. I'll look forward to hearing your results!