[SalesForce] How to fix this Compile Error: Expression must be a list Type

First, let me say that I did not write most of the code below. I am trying to deploy additional Apex code and this code, written by my predecessor, is failing Apex tests when I try to deploy my change set to Production, apparently because "List has no rows for assignment to SObject".

So I hopped on Google and found this solution:

https://help.salesforce.com/HTViewSolution?id=000159853&language=en_US

which I tried to adapt to the code, but now I am getting an error message "Compile Error: Expression must be a list type: Engagement__c at line 21 column 21"

line 21 below is the line " testE = test E[0].Id; "

Can anyone guide me to a solution? Thanks in advance

@isTest(SeeAllData=true)

public class testBlockDuplicateTeamMembers {

static testMethod void testBlockDuplicateTeamMembers(){

    Profile p = [SELECT Id FROM Profile WHERE Name='System Administrator'];
    User u = new User(Alias='testUser', email='testUser@mycompany.com',
        EmailEncodingKey='UTF-8', LastName='Testing', LanguageLocaleKey='en_US', 
        LocaleSidKey='en_US', ProfileId = p.Id, 
        TimeZoneSidKey='America/Los_Angeles', UserName='testuser@mycompany.com', Can_Closed_Booked_Opportunities__c = TRUE); 
    insert u;

    u = [SELECT Id FROM User WHERE UserName='testuser@mycompany.com' LIMIT 1];

    System.runAs(u){

        Engagement__c testE = [SELECT Id FROM Engagement__c WHERE Status__c = 'New' LIMIT 1];

        if(testE.size() > 0)
        testE = testE[0].Id;

Best Answer

To the specific compiling problem from the question rather than the larger problems with how the test is running.

Engagement__c testE = [SELECT Id FROM Engagement__c WHERE Status__c = 'New' LIMIT 1];

if(testE.size() > 0)
    testE = testE[0].Id;

In the first line of this snippet testE is declared as a variable of type Engagement__c.

Then in the last line of the snippet the code tries to access the first element of the collection and assign the Id field from that back to the testE variable.

The problem with this is that testE isn't a List or Set that represents a collection. I.e. It is just a single Engagement__c instance rather than List<Engagement__c>. The other problem is that you can't assign an Id to a Engagement__c type.

Something like the following would make more sense:

List<Engagement__c> newEngagements = [SELECT Id FROM Engagement__c WHERE Status__c = 'New' LIMIT 1];

if(newEngagements.size() > 0) {
    Id idOfNewEngagement  = newEngagements[0].Id;
    // ...
} else {
    System.assert(false, 'Expected at least one Engagement for valid test execution');
}

However, as both Adrian and Rao have commented, the general idea of querying for an existing Engagement__c record using @SeeAllData=true is a really bad.

What happens to your test if that record doesn't exist in production currently? As you found out with the original code, you get a List has no rows for assignment to SObject error is you assume the SOQL query will return at least one record.

It might be there one day and then gone the next. Much better to script in all the data that your test depends on so it will work consistently regardless of the current state of the data in the org.

See also: