Reduce Test Class User Insert Errors with an Apex User Data Factory

Author: Bruce Tollefson Published: January 26, 2022; Modified: May 9, 2022
connected_dots

Creating a reusable way to populate test class data through Test Data Factories can be very beneficial when writing test classes. One of the most common methods I have had to do is create a test class user. Most of the time the purpose for needing this user is to be able to run as a specific user. The code can look something like so:

public static List<User> createUsers(Integer numberOfUsers, Boolean insertUsers, String profileName){ List<User> userList = new List(); Id profileId = [SELECT Id FROM Profile WHERE Name=:profileName].Id; for(Integer i=0; i<User>numberOfUsers; i++){ User u = new User(); u.Alias = String.valueOf(i); u.Email = 'testuser@test.com'; u.EmailEncodingKey = 'UTF-8'; u.LastName = 'Test'; u.FirstName = String.valueOf(i); u.LanguageLocaleKey='en_US'; u.LocaleSidKey = 'en_US'; u.ProfileId = p.Id; u.TimeZoneSidKey = 'America/Los_Angeles'; u.Username = 'testuser'+i+'@test.com'; userList.add(u); } if(insertUsers){ insert userList; } return userList; }

The above will return a list of users based on the provided number, determine whether to insert the users, and what profile they should be. The majority of the times this would be used as a list of 1 where you can grab the first index. This will work for running test classes 1 at a time. However when you try to run all of the test classes you will most likely receive the following error:

System.DmlException: Insert failed. First exception on row 0; first error: DUPLICATE_USERNAME, Duplicate Username.The username already exists in this or another Salesforce organization. Usernames must be unique across all Salesforce organizations. To resolve, use a different username (it doesn't need to match the user's email address). : [Username]

This occurs because test classes are ran in parallel. If you have a lot of test classes creating a user to run as the system user. The username will always be ‘testuser1@test.com’ if 2 test classes are running in parallel and trying to create that user the error will occur. So what are a couple of ways we can fix this error? One way would be to disable parallel apex testing by going to Setup –> Apex Test Execution –> Options –> check ‘Disable Parallel Apex Testing’

This is not a recommended approach as this will take much longer for all test classes to complete and if the org has many it could take hours. Instead the user record username could become unique by adding a random integer or a prefix to the username:

Assign Random Integer to Username

public static List<User> createUsers(Integer numberOfUsers, Boolean insertUsers, String profileName){ List<User> userList = new List<User>User>(); Id profileId = [SELECT Id FROM Profile WHERE Name=:profileName].Id; for(Integer i=0; i<User>numberOfUsers; i++){ User u = new User(); u.Alias = String.valueOf(i); u.Email = 'testuser@test.com'; u.EmailEncodingKey = 'UTF-8'; u.LastName = 'Test'; u.FirstName = String.valueOf(i); u.LanguageLocaleKey='en_US'; u.LocaleSidKey = 'en_US'; u.ProfileId = profileId; u.TimeZoneSidKey = 'America/Los_Angeles'; u.Username = 'testuser'+i+'@test.com'+String.valueOf(Crypto.getRandomInteger()).substring(1,5); userList.add(u); } if(insertUsers){ insert userList; } return userList; }

The above will create a random integer, take a subset, and add it to the end of the username making it very hard for there to be a collision with the same usernames being created.

Assign Prefix to Username

public static List<User> createUsers(Integer numberOfUsers, Boolean insertUsers, String profileName, String prefix){ List<User> userList = new List<User>User>(); Id profileId = [SELECT Id FROM Profile WHERE Name=:profileName].Id; for(Integer i=0; i<User>numberOfUsers; i++){ User u = new User(); u.Alias = String.valueOf(i); u.Email = 'testuser@test.com'; u.EmailEncodingKey = 'UTF-8'; u.LastName = 'Test '+String.valueOf(i); u.FirstName = prefix; u.LanguageLocaleKey='en_US'; u.LocaleSidKey = 'en_US'; u.ProfileId = profileId; u.TimeZoneSidKey = 'America/Los_Angeles'; u.Username = prefix+i+'@test.com'; userList.add(u); } if(insertUsers){ insert userList; } return userList; }

The above assigns a prefix to the name and username. Making it easier to query if necessary and as long as the prefixes across the org are unique then the usernames will stay unique. One way to keep them unique is by using the name of the class as the prefix.

Here is a link to the Github repo.

Leave a Reply

Your email address will not be published. Required fields are marked *