Skip to content

Commit 144b42d

Browse files
committed
Allow for different implementations of Application factories
1 parent 1e5eb56 commit 144b42d

File tree

6 files changed

+501
-24
lines changed

6 files changed

+501
-24
lines changed

sfdx-source/apex-common/main/classes/fflib_Application.cls

Lines changed: 132 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,10 @@ public virtual class fflib_Application
112112
return new fflib_SObjectUnitOfWork(objectTypes, dml);
113113
}
114114

115-
@TestVisible
116-
protected virtual void setMock(fflib_ISObjectUnitOfWork mockUow)
115+
/**
116+
* @param mockUow A mock implementation for the unitOfWork factory
117+
*/
118+
public virtual void setMock(fflib_ISObjectUnitOfWork mockUow)
117119
{
118120
m_mockUow = mockUow;
119121
}
@@ -168,8 +170,25 @@ public virtual class fflib_Application
168170
return serviceImpl.newInstance();
169171
}
170172

171-
@TestVisible
172-
protected virtual void setMock(Type serviceInterfaceType, Object serviceImpl)
173+
/**
174+
* Creates or replaces an existing binding for another
175+
*
176+
* @param serviceInterfaceType The Interface type to replace its implementation
177+
* @param replacementImplType The implementation type of the replacement
178+
*/
179+
public virtual void replaceWith(Type serviceInterfaceType, Type replacementImplType)
180+
{
181+
this.m_serviceInterfaceTypeByServiceImplType.put(
182+
serviceInterfaceType,
183+
replacementImplType
184+
);
185+
}
186+
187+
/**
188+
* @param serviceInterfaceType The interface type to mock
189+
* @param serviceImpl The mock implementation
190+
*/
191+
public virtual void setMock(Type serviceInterfaceType, Object serviceImpl)
173192
{
174193
m_serviceInterfaceTypeByMockService.put(serviceInterfaceType, serviceImpl);
175194
}
@@ -246,6 +265,27 @@ public virtual class fflib_Application
246265
return newInstance(domainSObjectType).selectSObjectsById(recordIds);
247266
}
248267

268+
/**
269+
* Helper method to query the given SObject records
270+
* Internally creates an instance of the registered Selector and calls its
271+
* selectSObjectById method.
272+
* It assumes that all Ids are of the given SObjectType, no additional validation is done.
273+
*
274+
* @param recordIds The recordIds to query
275+
* @param sObjectType The SObjectType of the Ids
276+
*
277+
* @return The queried records
278+
* @exception fflib_Application.DeveloperException is thrown if the Ids set is empty
279+
*/
280+
public virtual List<SObject> selectById(Set<Id> recordIds, SObjectType sObjectType)
281+
{
282+
if (recordIds == null || recordIds.size() == 0)
283+
throw new fflib_Application.DeveloperException('Invalid record Id\'s set');
284+
285+
return newInstance(sObjectType)
286+
.selectSObjectsById(recordIds);
287+
}
288+
249289
/**
250290
* Helper method to query related records to those provided, for example
251291
* if passed a list of Opportunity records and the Account Id field will
@@ -270,19 +310,42 @@ public virtual class fflib_Application
270310
return selectById(relatedIds);
271311
}
272312

273-
@TestVisible
274-
protected virtual void setMock(fflib_ISObjectSelector selectorInstance)
313+
/**
314+
* Creates or replaces an existing binding for another
315+
*
316+
* @param sObjectType The SObjectType of the selector to replace
317+
* @param replacementImplType The implementation type of the replacement
318+
*/
319+
public virtual void replaceWith(SObjectType sObjectType, Type replacementImplType)
320+
{
321+
this.m_sObjectBySelectorType.put(sObjectType, replacementImplType);
322+
}
323+
324+
/**
325+
* @param selectorInstance The instance of the mocked selector
326+
*/
327+
public virtual void setMock(fflib_ISObjectSelector selectorInstance)
275328
{
276329
m_sObjectByMockSelector.put(selectorInstance.sObjectType(), selectorInstance);
277-
}
330+
}
331+
332+
/**
333+
* @param sObjectType The SObjectType of the selector mock,
334+
* avoids the need to stub the mock to return its SObjectType
335+
* @param selectorInstance The instance of the mocked selector
336+
*/
337+
public virtual void setMock(SObjectType sObjectType, fflib_ISObjectSelector selectorInstance)
338+
{
339+
this.m_sObjectByMockSelector.put(sObjectType, selectorInstance);
340+
}
278341
}
279342

280343
/**
281344
* Class implements a Domain class factory
282345
**/
283346
public virtual class DomainFactory implements fflib_IDomainFactory
284347
{
285-
protected fflib_Application.SelectorFactory m_selectorFactory;
348+
protected fflib_ISelectorFactory m_selectorFactory;
286349

287350
protected Map<Object, Type> constructorTypeByObject;
288351

@@ -302,10 +365,10 @@ public virtual class fflib_Application
302365
* @param selectorFactory , e.g. Application.Selector
303366
* @param constructorTypeByObject Map of Domain classes by ObjectType
304367
**/
305-
public DomainFactory(fflib_Application.SelectorFactory selectorFactory,
368+
public DomainFactory(fflib_ISelectorFactory selectorFactory,
306369
Map<Object, Type> constructorTypeByObject)
307370
{
308-
m_selectorFactory = selectorFactory;
371+
this.m_selectorFactory = selectorFactory;
309372
this.constructorTypeByObject = constructorTypeByObject;
310373
this.mockDomainByObject = new Map<Object, fflib_IDomain>();
311374
}
@@ -319,10 +382,10 @@ public virtual class fflib_Application
319382
* @param selectorFactory, e.g. Application.Selector
320383
* @param sObjectByDomainConstructorType Map of Apex classes by SObjectType
321384
**/
322-
public DomainFactory(fflib_Application.SelectorFactory selectorFactory,
385+
public DomainFactory(fflib_ISelectorFactory selectorFactory,
323386
Map<SObjectType, Type> sObjectByDomainConstructorType)
324387
{
325-
m_selectorFactory = selectorFactory;
388+
this.m_selectorFactory = selectorFactory;
326389
this.constructorTypeByObject = getConstructorTypeByObject(sObjectByDomainConstructorType);
327390
this.mockDomainByObject = new Map<Object, fflib_IDomain>();
328391
}
@@ -338,7 +401,22 @@ public virtual class fflib_Application
338401
public virtual fflib_IDomain newInstance(Set<Id> recordIds)
339402
{
340403
return newInstance(m_selectorFactory.selectById(recordIds));
404+
}
341405

406+
/**
407+
* Dynamically constructs an instance of a Domain class for the given record Ids
408+
* Internally uses the Selector Factory to query the records before passing to a
409+
* dynamically constructed instance of the application Apex Domain class
410+
*
411+
* @param recordIds A list of Id's of the same type
412+
* @param sObjectType The SObjectType of the given record Ids
413+
*
414+
* @return Instance of a Domain containing the queried records
415+
* @exception Throws an exception via the Selector Factory if the Ids are not all of the same SObjectType
416+
**/
417+
public virtual fflib_IDomain newInstance(Set<Id> recordIds, Schema.SObjectType sObjectType)
418+
{
419+
return newInstance(m_selectorFactory.selectById(recordIds, sObjectType), sObjectType);
342420
}
343421

344422
/**
@@ -412,18 +490,56 @@ public virtual class fflib_Application
412490
);
413491
}
414492

415-
@TestVisible
416-
protected virtual void setMock(fflib_ISObjectDomain mockDomain)
493+
/**
494+
* Creates or replaces an existing binding for another
495+
*
496+
* @param sObjectType The SObjectType of the selector to replace
497+
* @param replacementImplType The implementation type of the replacement
498+
*/
499+
public virtual void replaceWith(Schema.SObjectType sObjectType, Type replacementImplType)
500+
{
501+
this.constructorTypeByObject.put(
502+
(Object) sObjectType,
503+
replacementImplType
504+
);
505+
}
506+
507+
/**
508+
* @param mockDomain The instance of the Domain mock
509+
*/
510+
public virtual void setMock(fflib_ISObjectDomain mockDomain)
417511
{
418512
mockDomainByObject.put((Object) mockDomain.sObjectType(), (fflib_IDomain) mockDomain);
419513
}
420514

421-
@TestVisible
422-
protected virtual void setMock(fflib_IDomain mockDomain)
515+
/**
516+
* @param mockDomain The instance of the Domain mock
517+
*/
518+
public virtual void setMock(fflib_IDomain mockDomain)
423519
{
424520
mockDomainByObject.put(mockDomain.getType(), mockDomain);
425521
}
426522

523+
/**
524+
* @param sObjectType The SObjectType of the Domain mock,
525+
* avoids the need to stub the mock to return its SObjectType
526+
* @param mockDomain The instance of the Domain mock
527+
*/
528+
public virtual void setMock(Schema.SObjectType sObjectType, fflib_ISObjectDomain mockDomain)
529+
{
530+
mockDomainByObject.put((Object) sObjectType, mockDomain);
531+
}
532+
533+
/**
534+
* @param domainType The ObjectType of the Domain mock,
535+
* avoids the need to stub the mock to return its ObjectType
536+
* @param mockDomain The instance of the Domain mock
537+
*/
538+
public virtual void setMock(Object domainType, fflib_IDomain mockDomain)
539+
{
540+
mockDomainByObject.put(domainType, mockDomain);
541+
}
542+
427543
protected virtual Map<Object, Type> getConstructorTypeByObject(Map<SObjectType, Type> constructorTypeBySObjectType)
428544
{
429545
Map<Object, Type> result = new Map<Object, Type>();

sfdx-source/apex-common/main/classes/fflib_IDomainFactory.cls

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,154 @@
2525
**/
2626
public interface fflib_IDomainFactory
2727
{
28+
/**
29+
* Dynamically constructs an instance of a Domain class for the given record Ids
30+
* Internally uses the Selector Factory to query the records before passing to a
31+
* dynamically constructed instance of the application Apex Domain class
32+
*
33+
* @param recordIds A list of Id's of the same type
34+
* @exception Throws an exception via the Selector Factory if the Ids are not all of the same SObjectType
35+
*
36+
* @return Instance of the Domain
37+
**/
2838
fflib_IDomain newInstance(Set<Id> recordIds);
39+
40+
/**
41+
* Dynamically constructs an instance of a Domain class for the given record Ids
42+
* Internally uses the Selector Factory to query the records before passing to a
43+
* dynamically constructed instance of the application Apex Domain class
44+
*
45+
* @param recordIds A list of Id's of the same type
46+
* @param sObjectType The Schema.SObjectType of the record Ids,
47+
* Providing this parameter will omit the framework from checking if the Id's are all the same
48+
* and of which SObjectType they are.
49+
*
50+
* @exception Throws an exception via the Selector Factory if the Ids are not all of the same SObjectType
51+
*
52+
* @return Instance of the Domain
53+
**/
54+
fflib_IDomain newInstance(Set<Id> recordIds, Schema.SObjectType sObjectType);
55+
56+
/**
57+
* Dynamically constructs an instance of the Domain class for the given records
58+
* Will return a Mock implementation if one has been provided via setMock
59+
*
60+
* @param records A concrete list of records, e.g.; `List<Account>` or `List<SObject>`)
61+
*
62+
* @exception Throws an exception if the SObjectType cannot be determined from the list
63+
* or the constructor for Domain class was not registered for the SObjectType
64+
*
65+
* @return Instance of the Domain containing the given records
66+
**/
2967
fflib_IDomain newInstance(List<SObject> records);
68+
69+
/**
70+
* Dynamically constructs an instance of the Domain class for the given records
71+
* Will return a Mock implementation if one has been provided via setMock
72+
*
73+
* @param objects A concrete list of Objects, e.g.; `List<Account>` or `List<SObject>`)
74+
* @param objectType
75+
*
76+
* @exception Throws an exception if the SObjectType cannot be determined from the list
77+
* or the constructor for Domain class was not registered for the SObjectType
78+
*
79+
* @return Instance of the Domain containing the given Objects
80+
**/
3081
fflib_IDomain newInstance(List<Object> objects, Object objectType);
82+
83+
/**
84+
* Dynamically constructs an instance of the Domain class for the given records and SObjectType
85+
* Will return a Mock implementation if one has been provided via setMock
86+
*
87+
* @param records A list records
88+
* @param domainSObjectType SObjectType for list of records
89+
*
90+
* @exception Throws an exception if the SObjectType is not specified or if constructor for Domain class was not registered for the SObjectType
91+
*
92+
* @remark Will support List<SObject> but all records in the list will be assumed to be of
93+
* the type specified in sObjectType
94+
*
95+
* @return Instance of the Domain containing the given records
96+
**/
3197
fflib_IDomain newInstance(List<SObject> records, SObjectType domainSObjectType);
98+
99+
/**
100+
* Creates or replaces an existing binding for another
101+
*
102+
* @param sObjectType The SObjectType of the domain to replace
103+
* @param replacementImplType The implementation type of the replacement
104+
*/
105+
void replaceWith(Schema.SObjectType sObjectType, Type replacementImplType);
106+
107+
/**
108+
* Registers a mock in the Factory by providing a mock instance of a domain
109+
* This method is only available for backward compatibility for
110+
* domains which are implementations of fflib_ISObjectDomain
111+
*
112+
* @param mockDomain The instance of the Domain mock
113+
*
114+
* @example
115+
*
116+
* fflib_ApexMocks mocks = new fflib_ApexMocks();
117+
* IAccounts domainMock = (IAccounts) mocks.mock(IAccounts.class);
118+
* mocks.startStubbing();
119+
* mocks.when(domainMock.getType()).thenReturn(Schema.Account.SObjectType);
120+
* ...
121+
* mocks.stopStubbing();
122+
*
123+
* Application.Domain.setMock((fflib_ISObjectDomain) domainMock);
124+
*/
125+
void setMock(fflib_ISObjectDomain mockDomain);
126+
127+
/**
128+
* Registers a mock implementation in the Factory for the provided sObjectType
129+
* This method is only available for backward compatibility for
130+
* domains which are implementations of fflib_ISObjectDomain
131+
*
132+
* @param sObjectType The SObjectType of the Domain mock,
133+
* avoids the need to stub the mock to return its SObjectType
134+
* @param mockDomain The instance of the Domain mock
135+
*
136+
* @example
137+
*
138+
* fflib_ApexMocks mocks = new fflib_ApexMocks();
139+
* IAccounts domainMock = (IAccounts) mocks.mock(IAccounts.class);
140+
*
141+
* Application.Domain.setMock(Schema.Account.SObjectType, domainMock);
142+
*/
143+
void setMock(Schema.SObjectType sObjectType, fflib_ISObjectDomain mockDomain);
144+
145+
/**
146+
* Registers a mock in the Factory by providing a mock instance of a domain
147+
*
148+
* @param mockDomain The instance of the Domain mock
149+
*
150+
* @example
151+
*
152+
* fflib_ApexMocks mocks = new fflib_ApexMocks();
153+
* IAccounts domainMock = (IAccounts) mocks.mock(IAccounts.class);
154+
* mocks.startStubbing();
155+
* mocks.when(domainMock.getType()).thenReturn(Schema.Account.SObjectType);
156+
* ...
157+
* mocks.stopStubbing();
158+
*
159+
* Application.Domain.setMock(domainMock);
160+
*/
161+
void setMock(fflib_IDomain mockDomain);
162+
163+
/**
164+
* Registers a mock implementation in the Factory for the provided sObjectType
165+
*
166+
* @param domainType The ObjectType of the Domain mock,
167+
* avoids the need to stub the mock to return its ObjectType
168+
* @param mockDomain The instance of the Domain mock
169+
*
170+
* @example
171+
*
172+
* fflib_ApexMocks mocks = new fflib_ApexMocks();
173+
* IAccounts domainMock = (IAccounts) mocks.mock(IAccounts.class);
174+
*
175+
* Application.Domain.setMock(domainMock);
176+
*/
177+
void setMock(Object domainType, fflib_IDomain mockDomain);
32178
}

0 commit comments

Comments
 (0)