-
Notifications
You must be signed in to change notification settings - Fork 15
feat/refactorTestFactory #342
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,95 @@ | ||
| /** | ||
| * @description A test-only helper for manipulating users' permissions, permission sets and permission set groups | ||
| * | ||
| * Note: This class does *no* error handling. Because it's used in the setup and preparation of test data, | ||
| * it's on the developer to ensure the methods are called with existing, valid data like permission set names. | ||
| * If, for instance, a developer fat-fingers a permission set name, the query on ~ line 74 will fail, and throw | ||
| * an exception. | ||
| */ | ||
|
|
||
| @IsTest | ||
| public class PermissionsHelper { | ||
| /** | ||
| * @description creates and inserts a permission set assignment record | ||
| * @param userToAssignTo User the user whom the permission set will be applied to | ||
| * @param permSetId Id The Id of the permission set to assign to the user. | ||
| */ | ||
| public static void assignPermissionSetToUser( | ||
| User userToAssignTo, | ||
| Id permSetId | ||
| ) { | ||
| PermissionSetAssignment permissionSetAssignment = new PermissionSetAssignment( | ||
| AssigneeId = userToAssignTo.Id, | ||
| PermissionSetId = permSetId | ||
| ); | ||
| insert permissionSetAssignment; | ||
| } | ||
|
|
||
| /** | ||
| * @description Assigns a permission set to a given user. | ||
| * @param userToAssignTo User to assign the permission set to. | ||
| * @param permSetName String name of the permission set. | ||
| */ | ||
| public static void assignPermSetToUser( | ||
| User userToAssignTo, | ||
| String permSetName | ||
| ) { | ||
| assignPermissionSetToUser( | ||
| userToAssignTo, | ||
| UserFactoryHelper.fetchPermissionSetIdByName(permSetName) | ||
| ); | ||
| } | ||
|
|
||
| /** | ||
| * @description Generates a test permission set record - no permissions are added to it | ||
| * @param permSetName String what to call your perm set | ||
| * @param doInsert Boolean true if you want this to insert your perm set record. | ||
| * @return PermissionSet the created permission set. | ||
| */ | ||
| public static PermissionSet createPermissionSet( | ||
| String permSetName, | ||
| Boolean doInsert | ||
| ) { | ||
| PermissionSet newPermSet = new PermissionSet( | ||
| Name = permSetName, | ||
| Label = 'Test Permission Set' | ||
| ); | ||
| if (doInsert) { | ||
| insert newPermSet; | ||
| } | ||
| return newPermSet; | ||
| } | ||
|
|
||
| /** | ||
| * @description Enables a custom permission using a permission set | ||
| * @param permissionName String name of the custom permission you want created | ||
| * @param forUserId Id user to assign the custom permission to. | ||
| */ | ||
| public static void enableCustomPermission( | ||
| String permissionName, | ||
| Id forUserId | ||
| ) { | ||
| PermissionSet permSet = createPermissionSet('TestPermSet', true); | ||
|
|
||
| Id customPermissionId = [ | ||
| SELECT Id | ||
| FROM CustomPermission | ||
| WHERE DeveloperName = :permissionName | ||
| WITH SYSTEM_MODE | ||
| LIMIT 1 | ||
| ] | ||
| .Id; | ||
|
|
||
| SetupEntityAccess permSetPermission = new SetupEntityAccess( | ||
| ParentId = permSet.Id, | ||
| SetupEntityId = customPermissionId | ||
| ); | ||
|
|
||
| PermissionSetAssignment permSetAssignment = new PermissionSetAssignment( | ||
| AssigneeId = forUserId, | ||
| PermissionSetId = permSet.Id | ||
| ); | ||
|
|
||
| insert new List<SObject>{ permSetPermission, permSetAssignment }; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <ApexClass xmlns="http://soap.sforce.com/2006/04/metadata"> | ||
| <apiVersion>63.0</apiVersion> | ||
| <status>Active</status> | ||
| </ApexClass> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,197 @@ | ||
| /** | ||
| * @description a factory class for generating test data. | ||
| * This class auto populates required fields and, by default, generates a plausible but fake ID for the record. | ||
| * You can use this class directly, or use one of the associated builder classes for generating | ||
| * complex data structures. | ||
| * | ||
| * This class has the ability to set default values for fields in 3, hierarchical ways. | ||
| * 1. The default values passed into the SObjectFactory via the sObject prototype. | ||
| * 2. The default values defined by the custom default class specified by the usingDefaultsClassName parameter. | ||
| * 3. The default values found in the 'org-wide' defaults class defined in the SObjectFactoryDefaults' subclasses. | ||
| * | ||
| * It's important to note that *nothing* overwrites field values specified in the SObject prototype. | ||
| * | ||
| * Some profiling information: | ||
| * | Action | Avg of 10 test executions | | ||
| * |-----------|------------------------| | ||
| * | Create 10 Single SObject w/ fake Id | 58ms | | ||
| * | Create 10 Single SObjects w/ fake Id and Custom Defaults | 73ms | | ||
| * | Create 10 Single SObjects & insert them | 550ms | | ||
| * | Create list of 200 SObjects w/ fake Id | 115ms | | ||
| * | Create list of 200 SObjects & insert them | 1216ms | | ||
| */ | ||
| @IsTest | ||
| public with sharing class SObjectFactory { | ||
| /** | ||
|
Comment on lines
+23
to
+25
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| * @description Use the FieldDefaults interface to set up field/value keys you want to routinely impose on your | ||
| * factory generated objects. | ||
| */ | ||
| public interface FieldDefaults { | ||
| /** | ||
| * @description Interface used by implementing classes to define defaults. | ||
| * @return `Map<Schema.SObjectField, Object>` | ||
| */ | ||
| Map<Schema.SObjectField, Object> getFieldDefaults(); | ||
| } | ||
|
|
||
| /** | ||
| * @description convenience method allowing you to specify just the prototype | ||
| * @param prototype SObject any SObject | ||
| * @return A created SObject with required fields populated | ||
| */ | ||
| public static SObject createSObject(SObject prototype) { | ||
| return createSObject(prototype, null, false); | ||
| } | ||
|
|
||
| /** | ||
| * @description Convenience method for creating SObjects using just the prototype and a custom defaults class | ||
| * @param prototype SObject - any constructed SObject | ||
| * @param usingDefaultsClassName String - the name of the class to use for custom default values | ||
| * @return A created SObject with required fields populated | ||
| */ | ||
| public static SObject createSObject( | ||
| SObject prototype, | ||
| String usingDefaultsClassName | ||
| ) { | ||
| return createSObject(prototype, usingDefaultsClassName, false); | ||
| } | ||
|
|
||
| /** | ||
| * @description Creates an SObject with the given prototype and forceInsert flag. | ||
| * | ||
| * @param prototype The prototype SObject. | ||
| * @param forceInsert Flag indicating whether to force insertion. | ||
| * | ||
| * @return The created SObject. | ||
| */ | ||
| public static SObject createSObject(SObject prototype, Boolean forceInsert) { | ||
| return createSObject(prototype, null, forceInsert); | ||
| } | ||
|
|
||
| /** | ||
| * @description Creates an SObject with the given prototype, using defaults from the specified class, and optionally inserts it into the database. | ||
| * | ||
| * @param prototype The prototype SObject to be created. | ||
| * @param usingDefaultsClassName The name of the class providing default values for the SObject. | ||
| * @param forceInsert Indicates whether to insert the SObject into the database. | ||
| * | ||
| * @return The created SObject. | ||
| */ | ||
| public static SObject createSObject( | ||
| SObject prototype, | ||
| String usingDefaultsClassName, | ||
| Boolean forceInsert | ||
| ) { | ||
| prototype = internalCreateSObject(prototype, usingDefaultsClassName); | ||
| if (forceInsert) { | ||
| Database.insert(prototype, AccessLevel.SYSTEM_MODE); | ||
| } else { | ||
| prototype.Id = IdFactory.get(prototype); | ||
| } | ||
| return prototype; | ||
| } | ||
|
Comment on lines
+70
to
+92
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In line 87, you are using - Database.insert(prototype, AccessLevel.SYSTEM_MODE);
+ insert prototype; |
||
|
|
||
| private static SObject internalCreateSObject( | ||
| SObject prototype, | ||
| String usingDefaultsClassName | ||
| ) { | ||
| // ensure the defaults class is not null | ||
| String functionalDefaultsClassName = String.isNotEmpty( | ||
| usingDefaultsClassName | ||
| ) | ||
| ? usingDefaultsClassName | ||
| : ''; | ||
| // First the specified defaults class | ||
| prototype = SObjectFactoryHelper.applyCustomDefaults( | ||
| prototype, | ||
| functionalDefaultsClassName | ||
| ); | ||
| // then the 'org-wide' defaults | ||
| prototype = SObjectFactoryHelper.applyOrgWideObjectDefaults(prototype); | ||
| return prototype; | ||
| } | ||
|
|
||
| /** | ||
| * @description Creates a list of SObjects based on the provided prototype and count. | ||
| * | ||
| * @param prototype The SObject prototype to base the created SObjects on. | ||
| * @param count The number of SObjects to create. | ||
| * | ||
| * @return A list of created SObjects. | ||
| */ | ||
| public static List<SObject> createSObjects(SObject prototype, Integer count) { | ||
| return createSObjects(prototype, count, null, false); | ||
| } | ||
|
|
||
| /** | ||
| * @description Creates a list of SObjects based on the provided prototype, count, and defaults class name. | ||
| * | ||
| * @param prototype The prototype SObject to base the new SObjects on. | ||
| * @param count The number of SObjects to create. | ||
| * @param usingDefaultsClassName The name of the class to use for default values. | ||
| * | ||
| * @return A list of created SObjects. | ||
| */ | ||
| public static List<SObject> createSObjects( | ||
| SObject prototype, | ||
| Integer count, | ||
| String usingDefaultsClassName | ||
| ) { | ||
| return createSObjects(prototype, count, usingDefaultsClassName, false); | ||
| } | ||
|
|
||
| /** | ||
| * @description Creates a list of sObjects based on the provided prototype, count, and forceInsert flag. | ||
| * | ||
| * @param prototype The prototype SObject to base the created objects on. | ||
| * @param count The number of SObjects to create. | ||
| * @param forceInsert Flag indicating whether to force insertion of the created SObjects. | ||
| * | ||
| * @return List of created SObjects. | ||
| */ | ||
| public static List<SObject> createSObjects( | ||
| SObject prototype, | ||
| Integer count, | ||
| Boolean forceInsert | ||
| ) { | ||
| return createSObjects(prototype, count, null, forceInsert); | ||
| } | ||
|
|
||
| /** | ||
| * @description Creates a list of SObjects based on a prototype, count, defaults class, and force insert flag. | ||
| * | ||
| * @param prototype The prototype SObject to clone. | ||
| * @param count The number of SObjects to create. | ||
| * @param usingDefaultsClassName The name of the defaults class to use. | ||
| * @param forceInsert Whether to force insert the created SObjects. | ||
| * | ||
| * @return A list of created SObjects. | ||
| */ | ||
| @SuppressWarnings('PMD.ExcessiveParameterList') | ||
| public static List<SObject> createSObjects( | ||
| SObject prototype, | ||
| Integer count, | ||
| String usingDefaultsClassName, | ||
| Boolean forceInsert | ||
| ) { | ||
| List<SObject> createdSObjects = new List<SObject>(); | ||
| SObject constructedFromPrototype = internalCreateSObject( | ||
| prototype, | ||
| usingDefaultsClassName | ||
| ); | ||
| for ( | ||
| Integer iterationCounter = 0; iterationCounter < count; iterationCounter++ | ||
| ) { | ||
| SObject clonedSObject = constructedFromPrototype.clone(false, true); | ||
| createdSObjects.add( | ||
| SObjectFactoryHelper.mutateCloneToRespectNameAndAutonumberRules( | ||
| clonedSObject, | ||
| !forceInsert, | ||
| iterationCounter | ||
| ) | ||
| ); | ||
| } | ||
| SObjectFactoryHelper.insertIfForced(createdSObjects, forceInsert); | ||
| return createdSObjects; | ||
| } | ||
|
Comment on lines
+159
to
+196
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In line 194, you are using - SObjectFactoryHelper.insertIfForced(createdSObjects, forceInsert);
+ if (forceInsert) {
+ insert createdSObjects;
+ } |
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <ApexClass xmlns="http://soap.sforce.com/2006/04/metadata"> | ||
| <apiVersion>63.0</apiVersion> | ||
| <status>Active</status> | ||
| </ApexClass> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,72 @@ | ||
| @IsTest | ||
| public class SObjectFactoryDefaults { | ||
| // To specify defaults for objects, use the naming convention [ObjectName]Defaults. | ||
| // For custom objects, omit the __c from the Object Name | ||
|
|
||
| /** | ||
| * @description Default values for Account object | ||
| */ | ||
| public class AccountDefaults implements SObjectFactory.FieldDefaults { | ||
| /** | ||
| * @description Returns a map of field defaults for the Account object. | ||
| * | ||
| * @return A map containing the default values for specific Account fields. | ||
| */ | ||
| public Map<Schema.SObjectField, Object> getFieldDefaults() { | ||
| return new Map<Schema.SObjectField, Object>{ | ||
| Account.Name => 'Test Account' | ||
| }; | ||
| } | ||
|
Comment on lines
+15
to
+19
Check warningCode scanning / PMD Missing ApexDoc comment Warning test
Missing ApexDoc comment
|
||
| } | ||
|
|
||
| /** | ||
| * @description This class provides default field values for Contact objects. | ||
| */ | ||
| public class ContactDefaults implements SObjectFactory.FieldDefaults { | ||
| /** | ||
| * @description Returns a map of field defaults for the Contact object. | ||
| * | ||
| * @return A map where keys are Schema.SObjectField representing Contact fields and values are the default values for those fields. | ||
| */ | ||
| public Map<Schema.SObjectField, Object> getFieldDefaults() { | ||
| return new Map<Schema.SObjectField, Object>{ | ||
| Contact.FirstName => 'First', | ||
| Contact.LastName => 'Last' | ||
| }; | ||
| } | ||
|
Comment on lines
+31
to
+36
Check warningCode scanning / PMD Missing ApexDoc comment Warning test
Missing ApexDoc comment
|
||
| } | ||
|
|
||
| /** | ||
| * @description This class provides default field values for Opportunity objects. | ||
| */ | ||
| public class OpportunityDefaults implements SObjectFactory.FieldDefaults { | ||
| /** | ||
| * @description Returns a map of default field values for Opportunity. | ||
| * | ||
| * @return Map<Schema.SObjectField, Object> A map containing default field values. | ||
| */ | ||
| public Map<Schema.SObjectField, Object> getFieldDefaults() { | ||
| return new Map<Schema.SObjectField, Object>{ | ||
| Opportunity.Name => 'Test Opportunity', | ||
| Opportunity.StageName => 'Closed Won', | ||
| Opportunity.CloseDate => System.today() | ||
| }; | ||
| } | ||
|
Comment on lines
+48
to
+54
Check warningCode scanning / PMD Missing ApexDoc comment Warning test
Missing ApexDoc comment
|
||
| } | ||
|
|
||
| /** | ||
| * @description Defaults for Case SObjectFactory | ||
| */ | ||
| public class CaseDefaults implements SObjectFactory.FieldDefaults { | ||
| /** | ||
| * @description Returns a map of field defaults for the Case object. | ||
| * | ||
| * @return A map where the key is the Schema.SObjectField and the value is the default value. | ||
| */ | ||
| public Map<Schema.SObjectField, Object> getFieldDefaults() { | ||
| return new Map<Schema.SObjectField, Object>{ | ||
| Case.Subject => 'Test Case' | ||
| }; | ||
| } | ||
|
Comment on lines
+66
to
+70
Check warningCode scanning / PMD Missing ApexDoc comment Warning test
Missing ApexDoc comment
|
||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code looks good overall, but there's a potential issue with the
enableCustomPermissionmethod. If the custom permission with the provided name doesn't exist, the query on line 74 will return no results and an attempt to access the.Idproperty on line 81 will result in aNullPointerException. To avoid this, you should check if the query returned any results before trying to access the Id.In this fix, I've added a check for an empty list after the query. If the list is empty, it throws a
CustomPermissionNotFoundException, which is a custom exception that you would need to define. This way, the error message can be more specific about what went wrong.