Test Factory Helper

Nate Helterbrand
salesforce apex testing

The Testing Headache

Anyone who’s written Apex tests knows this pain: you spend time crafting the perfect test method, everything works great, and then… someone adds a new required field to the Account object. Suddenly, your tests are failing everywhere because you’re not populating that new field when creating test data.

The typical solution? Hunt down every test class that creates an Account, add the new field to each one, and hope you didn’t miss any. Multiply this across multiple objects and fields, and test maintenance becomes a significant time sink.

There’s a better way.

Introducing TestFactory

TestFactory is an Apex helper class that automatically populates required fields when creating test records. Instead of manually specifying every required field, you ask TestFactory to create a record, and it dynamically determines what fields are required and populates them with valid values.

When someone adds a new required field? Your tests keep working without any code changes.

Why This Matters

Reduced Test Maintenance: Stop updating test classes every time field requirements change. TestFactory handles it automatically.

Faster Test Development: Write tests without needing to know every required field on every object. Focus on testing behavior, not field populations.

Consistent Test Data: All test records are created with valid data that passes validation rules and required field checks.

Configuration-Agnostic: Tests work across sandboxes with different field configurations, making deployments smoother.

Less Brittle Tests: Tests break because of actual code issues, not because someone changed a field from optional to required.

Installation

Deploy the TestFactory class to your org using the deploy button:

Deploy

The class is small and has no dependencies, so deployment is straightforward.

How to Use TestFactory

TestFactory provides three main methods, each designed for different scenarios.

Basic Usage: createRecord(String objectName)

The simplest way to use TestFactory is to create a record with all required fields automatically populated:

@isTest
static void testAccountCreation() {
    Test.startTest();
    Account acc = (Account)TestFactory.createRecord('Account');
    Test.stopTest();

    // The account exists and has all required fields populated
    System.assertNotEquals(null, acc.Id);
}

This method:

  • Creates a record of the specified object type
  • Populates all required fields with valid values
  • Inserts the record into the database
  • Returns the inserted record

Populating Additional Fields: createRecord(String objectName, List<String> additionalFieldsToPopulate)

Sometimes you need to populate fields that aren’t required by the system but are required by your business logic or custom validation rules:

@isTest
static void testAccountWithPhone() {
    Test.startTest();
    Account acc = (Account)TestFactory.createRecord(
        'Account',
        new List<String>{'Phone', 'Website'}
    );
    Test.stopTest();

    // Required fields plus Phone and Website are populated
    System.assertNotEquals(null, acc.Phone);
    System.assertNotEquals(null, acc.Website);
}

This is particularly useful when:

  • You have custom validation rules that require certain fields
  • Your trigger logic expects specific fields to be populated
  • You’re testing field-specific behavior

Manual Insertion: populateRequiredFields(String objectName)

For scenarios where you need to modify the record before insertion, use populateRequiredFields:

@isTest
static void testCustomAccountSetup() {
    Account acc = (Account)TestFactory.populateRequiredFields('Account');

    // Customize the record before inserting
    acc.Name = 'Test Account';
    acc.Industry = 'Technology';
    acc.AnnualRevenue = 1000000;

    Test.startTest();
    insert acc;
    Test.stopTest();

    // Verify custom values were preserved
    Account inserted = [SELECT Name, Industry FROM Account WHERE Id = :acc.Id];
    System.assertEquals('Test Account', inserted.Name);
    System.assertEquals('Technology', inserted.Industry);
}

This method returns a populated record without inserting it, giving you full control over the final state before insertion.

Real-World Examples

Testing Trigger Logic

@isTest
static void testOpportunityTrigger() {
    // Create test account with required fields
    Account acc = (Account)TestFactory.createRecord('Account');

    Test.startTest();
    // Create opportunity with required fields and link to account
    Opportunity opp = (Opportunity)TestFactory.populateRequiredFields('Opportunity');
    opp.AccountId = acc.Id;
    opp.StageName = 'Prospecting';
    insert opp;
    Test.stopTest();

    // Test your trigger logic
    Opportunity updated = [SELECT StageName FROM Opportunity WHERE Id = :opp.Id];
    // Assert expected behavior
}

Testing with Multiple Objects

@isTest
static void testContactAccountRelationship() {
    // Create parent account
    Account acc = (Account)TestFactory.createRecord('Account');

    Test.startTest();
    // Create related contact with required fields
    Contact con = (Contact)TestFactory.createRecord(
        'Contact',
        new List<String>{'Email'}
    );
    con.AccountId = acc.Id;
    update con;
    Test.stopTest();

    // Verify relationship
    Contact queried = [SELECT AccountId FROM Contact WHERE Id = :con.Id];
    System.assertEquals(acc.Id, queried.AccountId);
}

Testing Custom Objects

@isTest
static void testCustomObject() {
    Test.startTest();
    // Works with custom objects too
    CustomObject__c custom = (CustomObject__c)TestFactory.createRecord('CustomObject__c');
    Test.stopTest();

    System.assertNotEquals(null, custom.Id);
}

Tips for Daily Development

Use It Everywhere: Make TestFactory your default way of creating test records. Your future self will thank you when field requirements change.

Combine with Setup Methods: Use TestFactory in @testSetup methods to create baseline test data efficiently:

@testSetup
static void setupTestData() {
    List<Account> accounts = new List<Account>();
    for (Integer i = 0; i < 10; i++) {
        accounts.add((Account)TestFactory.populateRequiredFields('Account'));
    }
    insert accounts;
}

Handle Custom Validation: If your org has complex validation rules, use the additionalFieldsToPopulate parameter to ensure those fields are populated correctly.

Document Required Fields: While TestFactory handles system-required fields, consider documenting in your test class header any business-required fields you’re populating.

Test Factory Extensions: Consider extending TestFactory for object-specific factories that add your common business logic:

public class AccountFactory {
    public static Account createDefaultAccount() {
        Account acc = (Account)TestFactory.populateRequiredFields('Account');
        acc.Industry = 'Technology';
        acc.Type = 'Prospect';
        return acc;
    }
}

Performance Considerations: TestFactory uses describe calls, which are cached by Salesforce. The first call might be slightly slower, but subsequent calls in the same test context are fast.

Common Patterns

Bulk Data Creation:

List<Account> accounts = new List<Account>();
for (Integer i = 0; i < 200; i++) {
    Account acc = (Account)TestFactory.populateRequiredFields('Account');
    acc.Name = 'Test Account ' + i;
    accounts.add(acc);
}
insert accounts;

Related Record Creation:

Account acc = (Account)TestFactory.createRecord('Account');
Contact con = (Contact)TestFactory.populateRequiredFields('Contact');
con.AccountId = acc.Id;
insert con;

Future Enhancements

I’m considering adding support for validation rule parsing, which would automatically populate fields in a way that satisfies custom validation rules, not just required fields. This would make test data creation even more hands-off.

If you have ideas or want to contribute, check out the GitHub repository.

Conclusion

Test maintenance shouldn’t be a full-time job. By dynamically handling required fields, TestFactory lets you write tests that focus on business logic rather than field populations. When your org’s configuration changes, your tests adapt automatically.

Less time maintaining tests means more time building features. Give TestFactory a try in your next test class, and see how much time it saves you.


This post was partially written with assistance from Claude AI to provide additional context and usage examples.