One of XFTY's primary goals is to let tests describe only the data they care about.
Rather than constructing complete records manually, tests customize a centrally-defined Master Template.
Most customization falls into one of four categories:
- Override Templates
- Override Template Lists
put(...)expressions- Removing values from the Master Template
Understanding when to use each technique helps keep tests concise while avoiding unnecessary object construction.
The most common customization technique is an Override Template.
An Override Template is a partially-populated SObject whose values replace those generated by the Master Template.
Contact contact = (Contact) new XFTY_DummySObjectProvider(Contact.SObjectType, providerLookup)
.setOverrideTemplate(new Contact(
FirstName = 'Alice',
LastName = 'Smith'
))
.supply();Only the specified fields are overridden.
Every other field continues to be generated by the Master Template.
Suppose the Contact Provider normally generates:
FirstName = "Contact First Name 1"
LastName = "Contact Last Name 1"
Email = "test.contact1@example.com"
After applying the Override Template, the generated record becomes:
FirstName = "Alice"
LastName = "Smith"
Email = "test.contact1@example.com"
The Override Template replaces only the fields supplied by the test.
Without Override Templates, every test would need to construct complete, valid records.
Instead of this:
new Contact(
FirstName = 'Alice',
LastName = 'Smith',
Email = 'alice@example.com',
...
)tests simply specify what matters.
new Contact(
FirstName = 'Alice'
)This keeps tests focused on the behavior being verified rather than the mechanics of satisfying validation rules.
Sometimes each generated record should have different values.
In these cases, provide a list of Override Templates.
List<Contact> contacts =
(List<Contact>) new XFTY_DummySObjectProvider(Contact.SObjectType, providerLookup)
.setOverrideTemplateList(new List<Contact>{
new Contact(FirstName='Alice'),
new Contact(FirstName='Bob'),
new Contact(FirstName='Charlie')
})
.supplyList();One record is generated for each template.
Each template inherits the remaining values from the Master Template.
Generating multiple copies of each template is controlled independently.
.setQuantityPerTemplate(2)For example:
.setOverrideTemplateList(new List<Contact>{
new Contact(FirstName='Alice'),
new Contact(FirstName='Bob')
})
.setQuantityPerTemplate(2)produces four Contacts.
Alice
Bob
Alice
Bob
Notice that quantity is applied outside the template loop.
The generated order is:
ABCABC
rather than
AABBCC
Override Templates replace generated values.
Sometimes you want to change how values are generated instead.
The put(...) methods modify the generation strategy used by the Master Template.
XFTY_DummySObjectBundle bundle =
new XFTY_DummySObjectProvider(Contact.SObjectType, providerLookup)
.put(Contact.FirstName, new XFTY_DummyDefaultValueIncrementingString('Test Contact'))
.supplyBundle();Instead of replacing one generated value, every generated Contact now receives an incrementing first name.
Test Contact 1
Test Contact 2
Test Contact 3
...
Simple values are generated using implementations of
XFTY_DummyDefaultValueIntf.
Several implementations are included with XFTY.
Examples include:
| Strategy | Purpose |
|---|---|
XFTY_DummyDefaultValueExact |
Always returns the same value |
XFTY_DummyDefaultValueIncrementingString |
Prefix with incrementing suffix |
XFTY_DummyDefaultValueUniqueString |
Guaranteed unique strings |
XFTY_DummyDefaultValueUniqueStringLength |
Unique strings with fixed length |
XFTY_DummyDefaultValueUniqueEmail |
Generates unique email addresses |
XFTY_DummyDefaultIncrementingDecimal |
Incrementing decimal values |
Additional strategies can easily be created by implementing
XFTY_DummyDefaultValueIntf.
Relationships are also configured using put(...).
For example:
.put(
Contact.AccountId,
new XFTY_DummyDefaultRelationshipRequired(new Account(
Description = 'Integration Test Account'
))
)Whenever a Contact requires an Account, XFTY generates one automatically.
The supplied Account acts as an Override Template for the generated Account.
This is why relationship providers receive an SObject rather than an SObjectType.
Each generated Account inherits its remaining values from the Account Provider's Master Template.
Two relationship strategies are available.
new XFTY_DummyDefaultRelationshipRequired(...)The relationship is generated whenever relationship generation includes required relationships.
Use this only for relationships that are genuinely required for valid test data.
new XFTY_DummyDefaultRelationshipOptional(...)Optional relationships are generated only when the Provider is configured with:
.setInclusivity(XFTY_InsertInclusivityEnum.ALL)Optional relationships make test data richer without unnecessarily increasing the size of generated object graphs.
Whenever possible, relationships should be optional rather than required.
Both techniques customize generated data, but they solve different problems.
Use an Override Template when:
- customizing one or two generated records
- supplying exact field values
- making a specific test easier to read
Use put(...) when:
- every generated record should behave differently
- replacing the generation strategy
- customizing relationships
- generating unique values
As a general rule, Override Templates describe data.
put(...) describes generation.
Customization is applied in a predictable order.
Master Template
│
▼
put(...)
│
▼
Override Template
If multiple customizations affect the same field, the Override Template always wins.
For example:
.put(Contact.FirstName, new XFTY_DummyDefaultValueExact('Generated'))
.setOverrideTemplate(new Contact(
FirstName='Alice'
))produces
Alice
not
Generated
Sometimes a Master Template supplies values that a test intentionally does not want.
Rather than replacing the value with another value, it can be removed.
.removeFromMasterTemplate(Contact.Email)This is particularly useful when testing:
- validation rules
- required field behavior
- error handling
- partially populated records
The methods describe above can be combined.
XFTY_DummySObjectBundle bundle = new XFTY_DummySObjectProvider(Contact.SObjectType, DEFAULT_SOBJECT_PROVIDER)
.put(Contact.FirstName, new XFTY_DummyDefaultValueIncrementingString('Test'))
.put(Contact.AccountId, new XFTY_DummyDefaultRelationshipRequired(
new Account(Description = 'Integration Test Account')
))
.setQuantityPerTemplate(2)
.setInclusivity(XFTY_InsertInclusivityEnum.ALL)
.setInsertMode(XFTY_InsertModeEnum.MOCK)
.supplyBundle();This example:
- gives each an incrementing first name
- automatically creates an
Accountfor each contact - customises the generated
Account - generates two
Contactrecords - assigns mock Ids without performing any database operations
Several default value strategies are included, including exact values, incrementing strings and unique email generation. Additional strategies can be added simply by implementing XFTY_DummyDefaultValueIntf.
Override Templates are usually the simplest solution.
However, they still allow the Master Template to generate values that may later be replaced.
When generating large amounts of data, using put(...) can avoid generating values that will never be used.
Most tests should prioritize readability over micro-optimizations, but understanding this distinction can be useful when constructing very large object graphs.
- Override only the fields your test actually cares about.
- Prefer Override Templates over manually constructing complete records.
- Use
put(...)to change generation strategies rather than generated data. - Keep required relationships to the absolute minimum.
- Make relationships optional unless they are genuinely required.
- Expose important default values as named constants.
- Keep Master Templates declarative rather than imperative.
Now that you've seen how generated data can be customized, the next guide explores how XFTY creates and organizes related records.
See Relationships for:
- Relationship generation
- Relationship inclusivity
- Bundles
- Navigating generated object graphs