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
Holly Havelka 10Holly Havelka 10 

Test Class for Synchronizing Portal Users with Contact Record Data

Hello all,

I am struggling to write a Test Class for the following trigger and static class that synchronizes portal users with contact record data:
trigger UpdateContactFromPortalUser on User (after update) {
    //We only want to run on the single item that the user edited 
    if (Trigger.new.size()==1) 
    { 
        User u =_ Trigger.new[0]; 
        //And only if it's a portal user 
        if (u.ContactId!=null) { 
            UpdateContactFromPortalUser.updateContacts(u.Id); 
        } 
    } 
}
global class UpdateContactFromPortalUser { 
    @future 
    public static void updateContacts(String userId) {
        User u = [select ContactId,Email,FirstName,LastName,Title
                    from User
                    where Id=:userId];

        if (u!=null && u.ContactId!=null) {
            Contact c = new Contact(Id=u.ContactId,Email=u.Email,FirstName=u.FirstName,LastName=u.LastName,Title=u.Title);
            update c; 
        }
    }
}
Here is my test class code:
 
@isTest
private class UpdateContactFromPortalUserTest {

static testMethod void testUpdateContacts() {

        Test.startTest(); 
        
        User u = [select Id,ContactId,FirstName from User where ContactId<>'' limit 1]; 

        u.FirstName='Bar';
         
        update u; 

        Test.stopTest(); 

        Contact c = [select FirstName from Contact where Id=:u.ContactId]; 
        System.assertEquals(c.FirstName,u.FirstName); 
    }
}

I am getting no code coverage when I runt the test.  Can someone help point me to what I am missing?

Thanks in advance for any help.

 
Best Answer chosen by Holly Havelka 10
Eric PepinEric Pepin
There's a couple of things going on here...
First, your trigger should be bulkified to process any number of objects and not just one. Running on a single item is against Salesforce best practices and can get you in trouble with Salesforce resource limits.
Second, the trigger objects have all of the field values that you are requerying in Lines 4-6 of UpdateContactFromPortalUser.updateContacts....so that query is unnecessary and should be removed.
Third, best practices for unit tests are to insert your own data. You haven't inserted a Contact in your test method, therefore there is no Contact to update. If you were to use an existing user (also against best practices), you would be using a ContactId that won't exist in the test context. I've corrected a few of the issues below in the code example, I hope it helps. The code hasn't been run/tested, but should give you an idea of how you can solve your issue.
 
trigger UpdateContactFromPortalUser on User (after update) {
	// run on every item that meets the condition, not just one
	List<User> usersToProcess = new List<User>();
	for(User u : Trigger.New) {
		if(u.ContactId != null) {
			usersToProcess.add(u);
		}
	}
	
	if(usersToProcess.size() > 0) {
		UpdateContactFromPortalUser.updateContacts(usersToProcess);
	}
}

global class UpdateContactFromPortalUser {
	@future
	public static void updateContacts(List<User> users) {
		// SOQL query is unnecessary here because Trigger objects have all of the data
		// NULL checks already done in trigger code
		List<Contact> contactsToUpdate = new List<Contact>();
		for(User u : users) {
			Contact c = new Contact(
				Id = u.ContactId,
				Email = u.Email,
				FirstName = u.FirstName,
				LastName = u.LastName,
				Title = u.Title
			);
			contactsToUpdate.add(c);
		}
	
		if(contactsToUpdate.size() > 0) {
			update contactsToUpdate;
		}
	}
}

// test class
@isTest
private class UpdateContactFromPortalUserTest {
	static testMethod void testUpdateContacts() {
			// will need to populate Contact with required field values before inserting, FirstName something other than 'Bar'
			Contact testContact = new Contact();
			insert testContact;
			
			// will need to populate User with required field values before inserting, especially ContactId from Contact above
			User u = new User();
			insert u;
	 
	        Test.startTest();
			
	        u.FirstName = 'Bar';
	        update u;
	 
	        Test.stopTest();
	 
	        Contact c = [SELECT FirstName FROM Contact WHERE Id = :testContact.Id];
	        System.assertEquals(u.FirstName, c.FirstName);
	}
}

 

All Answers

JSingh9JSingh9
You should Create Test data for your Org, You are trying to query existing data for that u need to set annocation seeAllData = true, but that's not a good practice
Prosenjit Sarkar 7Prosenjit Sarkar 7
Hi Holly, 

Your trigger is using protal user and the triggerhandler is using future annotation. This is too much to complicate the test class without having seeAllData=true. Intead of that I can suggest you only to get some code coverage without using seeAllData=true
 
@isTest
private class UpdateContactFromPortalUserTest {

static testMethod void testUpdateContacts() {
		Profile p = [SELECT Id FROM Profile WHERE Name='System Administrator']; 
      User u = new User(Alias = 'standt', Email='standarduser@testorg.com', 
      EmailEncodingKey='UTF-8', LastName='Testing', LanguageLocaleKey='en_US', 
      LocaleSidKey='en_US', ProfileId = p.Id, 
      TimeZoneSidKey='America/Los_Angeles', UserName='standarduser@testorg.com');
        
		
		Test.startTest(); 
        
         
        insert u;
		UpdateContactFromPortalUser.updateContacts(u.Id);
        Test.stopTest(); 

        
    }
}

Thanks, 
Prosenjit
Eric PepinEric Pepin
There's a couple of things going on here...
First, your trigger should be bulkified to process any number of objects and not just one. Running on a single item is against Salesforce best practices and can get you in trouble with Salesforce resource limits.
Second, the trigger objects have all of the field values that you are requerying in Lines 4-6 of UpdateContactFromPortalUser.updateContacts....so that query is unnecessary and should be removed.
Third, best practices for unit tests are to insert your own data. You haven't inserted a Contact in your test method, therefore there is no Contact to update. If you were to use an existing user (also against best practices), you would be using a ContactId that won't exist in the test context. I've corrected a few of the issues below in the code example, I hope it helps. The code hasn't been run/tested, but should give you an idea of how you can solve your issue.
 
trigger UpdateContactFromPortalUser on User (after update) {
	// run on every item that meets the condition, not just one
	List<User> usersToProcess = new List<User>();
	for(User u : Trigger.New) {
		if(u.ContactId != null) {
			usersToProcess.add(u);
		}
	}
	
	if(usersToProcess.size() > 0) {
		UpdateContactFromPortalUser.updateContacts(usersToProcess);
	}
}

global class UpdateContactFromPortalUser {
	@future
	public static void updateContacts(List<User> users) {
		// SOQL query is unnecessary here because Trigger objects have all of the data
		// NULL checks already done in trigger code
		List<Contact> contactsToUpdate = new List<Contact>();
		for(User u : users) {
			Contact c = new Contact(
				Id = u.ContactId,
				Email = u.Email,
				FirstName = u.FirstName,
				LastName = u.LastName,
				Title = u.Title
			);
			contactsToUpdate.add(c);
		}
	
		if(contactsToUpdate.size() > 0) {
			update contactsToUpdate;
		}
	}
}

// test class
@isTest
private class UpdateContactFromPortalUserTest {
	static testMethod void testUpdateContacts() {
			// will need to populate Contact with required field values before inserting, FirstName something other than 'Bar'
			Contact testContact = new Contact();
			insert testContact;
			
			// will need to populate User with required field values before inserting, especially ContactId from Contact above
			User u = new User();
			insert u;
	 
	        Test.startTest();
			
	        u.FirstName = 'Bar';
	        update u;
	 
	        Test.stopTest();
	 
	        Contact c = [SELECT FirstName FROM Contact WHERE Id = :testContact.Id];
	        System.assertEquals(u.FirstName, c.FirstName);
	}
}

 
This was selected as the best answer
Holly Havelka 10Holly Havelka 10
Thanks Eric, I am attempting to make the changes you suggested.  I am running into a new error, though: Unsupported parameter type List<User> at line 3 column 24.

Any thoughts on this issue?
Holly Havelka 10Holly Havelka 10
Eric - below is my code that I am trying to use, but I am now getting this error when I try to save: Error: Unsupported parameter type List<SObject> at line 3 column 24    
 
global class UpdateContactFromPortalUser {
@future
    public static void updateContacts(List<sObject> users) {
    
        List<Contact> contactsToUpdate = new List<Contact>();
        for(sObject sObj : users) {
            User u = (User)sObj;
            Contact c = new Contact(
                Id = u.ContactId,
                Partner_User_Profile_Name__c = u.ProfileId
            );
            contactsToUpdate.add(c);
        }
     
        if(contactsToUpdate.size() > 0) {
            update contactsToUpdate;
        }
    }
}

 
Arvind KumarArvind Kumar
Hi,

You can use system.runAs() method to run the test class through portal user. You have to profile with name portal profile like portal community user and create a user corresponding to that profile and then after use system.runAs() method to execute your logic inside that.

Thanks
Eric PepinEric Pepin
My apologies as my last post was inaccurate and misleading (deleted to avoid confusion).
Object/sObjects cannot be passed in as parameters in @future context.

If you remove the @future, then your original code will work.
(Signature = public static void updateContacts(List<User> users))

If you must use the @future context, then you can only pass in a primitive data type and would need to do something different.
Sample code below: 
trigger UpdateContactFromPortalUser on User (after update) {
	// run on every item that meets the condition, not just one
    // to use @future context, collect Set<Id>, then requery in future method
	List<Id> userIds = new List<Id>();
	for(User u : Trigger.New) {
		if(u.ContactId != null) {
			userIds.add(u.Id);
		}
	}
	
	if(userIds.size() > 0) {
		UpdateContactFromPortalUser.updateContacts(userIds);
	}
}

global class UpdateContactFromPortalUser {
	@future
	public static void updateContacts(List<Id> userIds) {
		// Can't input objects, so must query
        List<User> users = [SELECT ContactId, Email, FirstName, LastName, Title
                            FROM User
                            WHERE Id IN :userIds];
        
		List<Contact> contactsToUpdate = new List<Contact>();
        for(User u : users) {
			Contact c = new Contact(
				Id = u.ContactId,
				Email = u.Email,
				FirstName = u.FirstName,
				LastName = u.LastName,
				Title = u.Title
			);
			contactsToUpdate.add(c);
		}
	
		if(contactsToUpdate.size() > 0) {
			update contactsToUpdate;
		}
	}
}