Skip to content

Commit 2753a9d

Browse files
authored
toIdsOf mocking fix (#196) (#197)
Signed-off-by: Piotr PG Gajek <[email protected]>
1 parent 9afd3fc commit 2753a9d

File tree

3 files changed

+136
-16
lines changed

3 files changed

+136
-16
lines changed

force-app/main/default/classes/main/standard-soql/SOQL.cls

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* Licensed under the MIT License (https://github.com/beyond-the-cloud-dev/soql-lib/blob/main/LICENSE)
44
*
55
* v6.0.0
6-
*
6+
*
77
* PMD False Positives:
88
* - ExcessivePublicCount: It is a library class and exposes all necessary methods to construct a query
99
* - ExcessiveClassLength: It is a library and we tried to put everything into ONE class
@@ -382,7 +382,7 @@ public virtual inherited sharing class SOQL implements Queryable {
382382

383383
@TestVisible
384384
private static Mockable mock(String mockId) {
385-
if (!SOQL.queryIdToMock.containsKey(mockId)) {
385+
if (!SOQL.queryIdToMock.containsKey(mockId)) {
386386
SOQL.queryIdToMock.put(mockId, new List<SoqlMock>());
387387
}
388388
SOQL.queryIdToMock.get(mockId).add(new SoqlMock());
@@ -1131,13 +1131,13 @@ public virtual inherited sharing class SOQL implements Queryable {
11311131

11321132
public override String toString() {
11331133
List<String> queryParts = new List<String>();
1134-
1134+
11351135
for (QueryClause clause : this.clauses) {
11361136
if (clause != null) {
11371137
queryParts.add(clause.toString());
11381138
}
11391139
}
1140-
1140+
11411141
return String.join(queryParts, ' ').trim();
11421142
}
11431143
}
@@ -2393,7 +2393,8 @@ public virtual inherited sharing class SOQL implements Queryable {
23932393
}
23942394

23952395
public List<SObject> get(SoqlFields fields, SoqlSubQueries subQueries) {
2396-
if (this.useLegacyMockingBehavior || this.mockedRecords.isEmpty()) {
2396+
// AggregateResult can be mocked only with Id field for toIdsOf, toValueOf
2397+
if (this.useLegacyMockingBehavior || this.mockedRecords.isEmpty() || this.mockedRecords[0]?.getSObjectType() == AggregateResult.SObjectType) {
23972398
return this.mockedRecords;
23982399
}
23992400

@@ -2851,4 +2852,4 @@ public virtual inherited sharing class SOQL implements Queryable {
28512852
return Id.valueOf(prefix + '0000' + randomPart);
28522853
}
28532854
}
2854-
}
2855+
}

force-app/main/default/classes/main/standard-soql/SOQL_Test.cls

Lines changed: 79 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* Licensed under the MIT License (https://github.com/beyond-the-cloud-dev/soql-lib/blob/main/LICENSE)
44
*
55
* v6.0.0
6-
*
6+
*
77
* PMD False Positives:
88
* - CyclomaticComplexity: It is a library and we tried to put everything into ONE test class
99
* - CognitiveComplexity: It is a library and we tried to put everything into ONE test class
@@ -3893,7 +3893,7 @@ private class SOQL_Test {
38933893
SOQL.mock('mockingQuery').thenReturn(1);
38943894
SOQL.mock('mockingQuery').thenReturn(2);
38953895
SOQL.mock('mockingQuery').thenReturn(3);
3896-
3896+
38973897
// Test
38983898
Integer result1 = SOQL.of(Account.SObjectType).mockId('mockingQuery').count().toInteger();
38993899
Integer result2 = SOQL.of(Account.SObjectType).mockId('mockingQuery').count().toInteger();
@@ -3913,7 +3913,7 @@ private class SOQL_Test {
39133913
SOQL.mock('mockingQuery').thenReturn(new Map<String, Object>{ 'LeadSource' => 'Web', 'total' => 10});
39143914
SOQL.mock('mockingQuery').thenReturn(new Map<String, Object>{ 'LeadSource' => 'Phone', 'total' => 5});
39153915
SOQL.mock('mockingQuery').thenReturn(new Map<String, Object>{ 'LeadSource' => 'Email', 'total' => 3});
3916-
3916+
39173917
// Test
39183918
List<SOQL.AggregateResultProxy> result1 = SOQL.of(Lead.SObjectType)
39193919
.with(Lead.LeadSource)
@@ -3974,6 +3974,18 @@ private class SOQL_Test {
39743974
Assert.areEqual(acc.Id, accountId, 'The returned Id should match the expected one.');
39753975
}
39763976

3977+
@IsTest
3978+
static void toIdWithMocking() {
3979+
// Setup
3980+
SOQL.mock('mockingQuery').thenReturn(new Account(Name = 'Test 1'));
3981+
3982+
// Test
3983+
Id accountId = SOQL.of(Account.SObjectType).mockId('mockingQuery').toId();
3984+
3985+
// Verify
3986+
Assert.isNotNull(accountId, 'The returned Id should match the expected one.');
3987+
}
3988+
39773989
@IsTest
39783990
static void toIds() {
39793991
// Setup
@@ -3986,6 +3998,21 @@ private class SOQL_Test {
39863998
Assert.areEqual(accounts.size(), accountIds.size(), 'The size of the returned set should be equal to the size of the inserted accounts.');
39873999
}
39884000

4001+
@IsTest
4002+
static void toIdsWithMocking() {
4003+
// Setup
4004+
SOQL.mock('mockingQuery').thenReturn(new List<SObject>{
4005+
new Account(Name = 'Test 1'),
4006+
new Account(Name = 'Test 2')
4007+
});
4008+
4009+
// Test
4010+
Set<Id> accountIds = SOQL.of(Account.SObjectType).mockId('mockingQuery').toIds();
4011+
4012+
// Verify
4013+
Assert.areEqual(2, accountIds.size(), 'The size of the returned set should be equal to the size of the mocked accounts.');
4014+
}
4015+
39894016
@IsTest
39904017
static void toIdsOf() {
39914018
// Setup
@@ -3998,6 +4025,30 @@ private class SOQL_Test {
39984025
Assert.areEqual(accounts.size(), parentIds.size(), 'The size of the returned set should be equal to the size of the inserted accounts.');
39994026
}
40004027

4028+
@IsTest
4029+
static void toIdsOfWithMocking() {
4030+
// Setup
4031+
SOQL.mock('mockingQuery').thenReturn(
4032+
(List<AggregateResult>) JSON.deserialize(
4033+
JSON.serialize(new List<Map<String, Object>>{
4034+
new Map<String, Object>{
4035+
'Id' => SOQL.IdGenerator.get(Account.SObjectType)
4036+
},
4037+
new Map<String, Object>{
4038+
'Id' => SOQL.IdGenerator.get(Account.SObjectType)
4039+
}
4040+
}),
4041+
List<AggregateResult>.class
4042+
)
4043+
);
4044+
4045+
// Test
4046+
Set<Id> parentIds = SOQL.of(Account.SObjectType).mockId('mockingQuery').toIdsOf(Account.ParentId);
4047+
4048+
// Verify
4049+
Assert.areEqual(2, parentIds.size(), 'The size of the returned set should be equal to the size of the mocked accounts.');
4050+
}
4051+
40014052
@IsTest
40024053
static void toIdsOfRelationshipField() {
40034054
// Setup
@@ -4010,6 +4061,25 @@ private class SOQL_Test {
40104061
Assert.areEqual(1, createdByIds.size(), 'The size of the returned set should be 1, because the same user inserted the accounts.');
40114062
}
40124063

4064+
@IsTest
4065+
static void toIdsOfRelationshipFieldWithMocking() {
4066+
// Setup
4067+
SOQL.mock('mockingQuery').thenReturn(
4068+
(AggregateResult) JSON.deserialize(
4069+
JSON.serialize(new Map<String, Object>{
4070+
'Id' => SOQL.IdGenerator.get(User.SObjectType)
4071+
}),
4072+
AggregateResult.class
4073+
)
4074+
);
4075+
4076+
// Test
4077+
Set<Id> createdByIds = SOQL.of(Account.SObjectType).mockId('mockingQuery').toIdsOf('Parent', Account.CreatedById);
4078+
4079+
// Verify
4080+
Assert.areEqual(1, createdByIds.size(), 'The size of the returned set should be 1, because the same user inserted the accounts.');
4081+
}
4082+
40134083
@IsTest
40144084
static void doExist() {
40154085
// Setup
@@ -4488,7 +4558,7 @@ private class SOQL_Test {
44884558
new Account(Name = 'Test 1'),
44894559
new Account(Name = 'Test 2')
44904560
});
4491-
4561+
44924562
// Test
44934563
try {
44944564
Test.startTest();
@@ -4559,9 +4629,9 @@ private class SOQL_Test {
45594629

45604630
// Test
45614631
Set<Id> accountIds = new SOQL.Converter('Account').transform(accounts).toIdsOf(Account.ParentId);
4562-
4632+
45634633
// Verify
4564-
Assert.areEqual(accounts.size(), accountIds.size(), 'The size of the returned set should be equal to the size of the inserted accounts.');
4634+
Assert.areEqual(accounts.size(), accountIds.size(), 'The size of the returned set should be equal to the size of the inserted accounts.');
45654635
}
45664636

45674637
@IsTest
@@ -4574,9 +4644,9 @@ private class SOQL_Test {
45744644

45754645
// Test
45764646
Set<Id> accountIds = new SOQL.Converter('Account').transform(accounts).toIdsOf('Parent', Account.Id);
4577-
4647+
45784648
// Verify
4579-
Assert.areEqual(accounts.size(), accountIds.size(), 'The size of the returned set should be equal to the size of the inserted accounts.');
4649+
Assert.areEqual(accounts.size(), accountIds.size(), 'The size of the returned set should be equal to the size of the inserted accounts.');
45804650
}
45814651

45824652
@IsTest
@@ -4589,7 +4659,7 @@ private class SOQL_Test {
45894659

45904660
// Test
45914661
Set<String> accountNames = new SOQL.Converter('Account').transform(accounts).toValuesOf(Account.Name);
4592-
4662+
45934663
// Verify
45944664
Assert.areEqual(accounts.size(), accountNames.size(), 'The size of the returned set should be equal to the size of the inserted accounts.');
45954665
Assert.isTrue(accountNames.contains('Test 1'), 'The returned set should contain the account name "Test 1".');

website/docs/soql/advanced/mocking.md

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ SOQL.mock('testQuery').thenReturn(new Account(Name = 'Third Call'));
4747
// First execution returns "First Call", then removes that mock
4848
Account result1 = SOQL.of(Account.SObjectType).mockId('testQuery').toObject();
4949
50-
// Second execution returns "Second Call", then removes that mock
50+
// Second execution returns "Second Call", then removes that mock
5151
Account result2 = SOQL.of(Account.SObjectType).mockId('testQuery').toObject();
5252
5353
// Third execution returns "Third Call", but do not removes that mock - it's the last mock on the stack
@@ -376,3 +376,52 @@ public class ExampleControllerTest {
376376
}
377377
}
378378
```
379+
380+
# toIdsOf, toValuesOf
381+
382+
We are using field aliasing: https://salesforce.stackexchange.com/questions/393308/get-a-list-of-one-column-from-a-soql-result
383+
384+
It’s approximately 2x more efficient than a standard for loop. Because of this, mocking works differently for the following methods:
385+
386+
- `toIdsOf(SObjectField field)`
387+
- `toIdsOf(String relationshipName, SObjectField field)`
388+
- `toValuesOf(SObjectField fieldToExtract)`
389+
- `toValuesOf(String relationshipName, SObjectField targetKeyField)`
390+
391+
```apex title="ExampleController.cls"
392+
public with sharing class ExampleController {
393+
public Set<String> getAccountNames() {
394+
return SOQL.of(Account.SObjectType)
395+
.mockId('ExampleController.getAccountNames')
396+
.toValuesOf(Account.Name);
397+
}
398+
}
399+
```
400+
401+
```apex title="ExampleControllerTest.cls"
402+
@IsTest
403+
public class ExampleControllerTest {
404+
@IsTest
405+
static void getAccountByName() {
406+
SOQL.mock('mockingQuery').thenReturn(
407+
(List<AggregateResult>) JSON.deserialize(
408+
JSON.serialize(new List<Map<String, Object>>{
409+
new Map<String, Object>{
410+
'Id' => 'Account Name 1'
411+
},
412+
new Map<String, Object>{
413+
'Id' => 'Account Name 2'
414+
}
415+
}),
416+
List<AggregateResult>.class
417+
)
418+
);
419+
420+
Test.startTest();
421+
Set<String> result = ExampleController.getAccountNames();
422+
Test.stopTest();
423+
424+
Assert.areEqual(2, result.size());
425+
}
426+
}
427+
```

0 commit comments

Comments
 (0)