Skip to content

Commit d20eb9d

Browse files
committed
Updated MockedMethod
Now accepts an optional .withParameterValues() flag of ANYPARAMETER to allow a method mock match with any runtime param.
1 parent d8e6049 commit d20eb9d

9 files changed

Lines changed: 321 additions & 327 deletions

File tree

config/project-scratch-def.json

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,19 @@
11
{
2-
"orgName": "ApexKit Company",
3-
"description": "ApexKitV2.5",
4-
"edition": "Developer",
5-
6-
"hasSampleData": true,
7-
"features": [
8-
"EinsteinGPTForDevelopers",
9-
"EnableSetPasswordInApi",
10-
"PlatformCache"
11-
],
12-
"settings": {
13-
"lightningExperienceSettings": {
14-
"enableS1DesktopEnabled": true
15-
},
16-
"mobileSettings": {
17-
"enableS1EncryptedStoragePref2": false
18-
}
19-
}
20-
}
2+
"orgName": "Ignoti Et Quasi Occulti",
3+
"description": "ApexKit",
4+
"edition": "Developer",
5+
"hasSampleData": false,
6+
"features": [
7+
"EinsteinGPTForDevelopers",
8+
"EnableSetPasswordInApi",
9+
"PlatformCache"
10+
],
11+
"settings": {
12+
"lightningExperienceSettings": {
13+
"enableS1DesktopEnabled": true
14+
},
15+
"mobileSettings": {
16+
"enableS1EncryptedStoragePref2": false
17+
}
18+
}
19+
}

force-app/main/default/classes/test utilities/MockedMethod.cls

Lines changed: 39 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
@isTest
1+
@IsTest
22
/**
33
* @description Represents a method call that is mocked as part of a Stub
4-
* object. This class is marked @isTest, as the object and it's methods are
4+
* object. This class is marked @IsTest, as the object and it's methods are
55
* not useful outside of a Test context.
66
*
77
* This file contains not only the MockedMethod class, but an inner Builder
@@ -13,15 +13,15 @@ public with sharing class MockedMethod {
1313
* @description Every MockedMethod has a methodSignature defining it's
1414
* 'signature' or combination of name, parameter names and parameter types.
1515
*/
16-
@testVisible
16+
@TestVisible
1717
private MethodSignature methodSignature;
1818
private List<Object> expectedParameters;
1919
private Object returnValue;
2020
private Boolean throwException = false;
2121
private List<Id> sObjectIds;
2222
private Exception customException;
2323
private Integer countOfMethodInvocations = 0;
24-
@testVisible
24+
@TestVisible
2525
private static String exceptionMessage = 'Exception thrown by SuperStub.';
2626

2727
/**
@@ -31,7 +31,7 @@ public with sharing class MockedMethod {
3131
* being mocked.
3232
*/
3333
public MockedMethod(MethodSignature methodSignature) {
34-
this.MethodSignature = methodSignature;
34+
this.methodSignature = methodSignature;
3535
}
3636

3737
/**
@@ -58,7 +58,7 @@ public with sharing class MockedMethod {
5858
if (this.customException != null) {
5959
throw this.customException;
6060
}
61-
// or throw a new custom exeption if it's just supposed to throw an
61+
// or throw a new custom exception if it's just supposed to throw an
6262
// exception
6363
throw new Stub.StubException(exceptionMessage);
6464
}
@@ -99,7 +99,7 @@ public with sharing class MockedMethod {
9999
/**
100100
* @description Sets this MockedMethod's return value. This is the
101101
* value that will be returned by this mocked method when the stub calls
102-
* a method that has an identical name, signature and input paramter list.
102+
* a method that has an identical name, signature and input parameter list.
103103
* @param returnValue This is the object you want returned.
104104
* @return `MockedMethod`
105105
*/
@@ -137,7 +137,7 @@ public with sharing class MockedMethod {
137137
* @description Use this variant to have this mocked method return
138138
* a developer-specified exception object. Useful for testing exception
139139
* handling with specific exception types.
140-
* @param customException
140+
* @param customException The exception you want thrown.
141141
* @return `MockedMethod`
142142
*/
143143
public MockedMethod throwingException(Exception customException) {
@@ -154,12 +154,8 @@ public with sharing class MockedMethod {
154154
* values - at runtime - of the method call.
155155
* @return `Boolean`
156156
*/
157-
public Boolean doMethodSignaturesAndParametersMatch(
158-
MethodSignature methodSignature,
159-
List<Object> runtimeParameters
160-
) {
161-
return this.methodSignature.verifySignatureMatch(methodSignature) &&
162-
doRuntimeParametersMatch(runtimeParameters);
157+
public Boolean doMethodSignaturesAndParametersMatch(MethodSignature methodSignature, List<Object> runtimeParameters) {
158+
return this.methodSignature.verifySignatureMatch(methodSignature) && doRuntimeParametersMatch(runtimeParameters);
163159
}
164160

165161
/**
@@ -169,7 +165,7 @@ public with sharing class MockedMethod {
169165
* Note: I thought I could replace this with
170166
* `return this.methodParamsAtExecutionTime.equals(compareTo);`
171167
* but this doesn't work, and to be honest, I'm still investigating why.
172-
* @param compareTo
168+
* @param compareTo A list of objects representing the actual values - at
173169
* @return `Boolean`
174170
*/
175171
private Boolean doRuntimeParametersMatch(List<Object> compareTo) {
@@ -178,9 +174,17 @@ public with sharing class MockedMethod {
178174
if (!matchesSoFar) {
179175
break;
180176
}
181-
matchesSoFar &=
182-
String.valueOf(this.expectedParameters[i]) ==
183-
String.valueOf(compareTo[i]);
177+
178+
// If the expected parameter is ParameterMatcher.ANY, we accept any value for this parameter
179+
if (
180+
this.expectedParameters[i] instanceof ParameterMatcher &&
181+
this.expectedParameters[i] == ParameterMatcher.ANYPARAMETER
182+
) {
183+
// Skip equality check for this parameter - it automatically matches
184+
continue;
185+
}
186+
187+
matchesSoFar &= String.valueOf(this.expectedParameters[i]) == String.valueOf(compareTo[i]);
184188
}
185189
return matchesSoFar;
186190
}
@@ -193,7 +197,7 @@ public with sharing class MockedMethod {
193197
* MockedMethod Objects.
194198
*/
195199
public class Builder {
196-
private MethodSignature.Builder methodSignatureBuilder;
200+
private final MethodSignature.Builder methodSignatureBuilder;
197201
private List<Object> runtimeParameters;
198202
private List<Id> returnSObjectIds;
199203
private Exception customException;
@@ -203,13 +207,10 @@ public with sharing class MockedMethod {
203207
/**
204208
* @description Constructor requiring a methodSignatureBuilder object
205209
* and a list of runtime arguments
206-
* @param methodSignatureBuilder
207-
* @param args
210+
* @param methodSignatureBuilder MethodSignature.Builder object to use
211+
* @param args List of System.Type
208212
*/
209-
public Builder(
210-
MethodSignature.Builder methodSignatureBuilder,
211-
List<System.Type> args
212-
) {
213+
public Builder(MethodSignature.Builder methodSignatureBuilder, List<System.Type> args) {
213214
this.runtimeParameters = args;
214215
this.methodSignatureBuilder = methodSignatureBuilder;
215216
}
@@ -226,7 +227,7 @@ public with sharing class MockedMethod {
226227
}
227228

228229
/**
229-
* @description convenience methodfor setting a single parameter type
230+
* @description convenience method for setting a single parameter type
230231
* @param parameter System.Type
231232
* @return this
232233
*/
@@ -235,37 +236,28 @@ public with sharing class MockedMethod {
235236
}
236237

237238
/**
238-
* @description convenience methodfor setting two params
239+
* @description convenience method for setting two params
239240
* @param parameter System.Type
240241
* @param parameter2 System.Type
241242
* @return this
242243
*/
243-
public MockedMethod.Builder withParameterValues(
244-
Object parameter,
245-
Object parameter2
246-
) {
244+
public MockedMethod.Builder withParameterValues(Object parameter, Object parameter2) {
247245
return this.setParameterValues(new List<Object>{ parameter, parameter2 });
248246
}
249247

250248
/**
251-
* @description convenience methodfor setting three params
249+
* @description convenience method for setting three params
252250
* @param parameter System.Type
253251
* @param parameter2 System.Type
254252
* @param parameter3 System.Type
255253
* @return return description
256254
*/
257-
public MockedMethod.Builder withParameterValues(
258-
Object parameter,
259-
Object parameter2,
260-
Object parameter3
261-
) {
262-
return this.setParameterValues(
263-
new List<Object>{ parameter, parameter2, parameter3 }
264-
);
255+
public MockedMethod.Builder withParameterValues(Object parameter, Object parameter2, Object parameter3) {
256+
return this.setParameterValues(new List<Object>{ parameter, parameter2, parameter3 });
265257
}
266258

267259
/**
268-
* @description convenience methodfor setting four parameters
260+
* @description convenience method for setting four parameters
269261
* @param parameter parameter description
270262
* @param parameter2 parameter2 description
271263
* @param parameter3 parameter3 description
@@ -279,9 +271,7 @@ public with sharing class MockedMethod {
279271
Object parameter3,
280272
Object parameter4
281273
) {
282-
return this.setParameterValues(
283-
new List<Object>{ parameter, parameter2, parameter3, parameter4 }
284-
);
274+
return this.setParameterValues(new List<Object>{ parameter, parameter2, parameter3, parameter4 });
285275
}
286276

287277
/**
@@ -299,7 +289,7 @@ public with sharing class MockedMethod {
299289
* @description This variant allows developers to specify the
300290
* object that will be returned when this mocked method is executed by
301291
* its parent stub.
302-
* @param returnValue
292+
* @param returnValue The object you want returned.
303293
* @return `Stub.Builder`
304294
*/
305295
public Stub.Builder returning(Object returnValue) {
@@ -332,10 +322,10 @@ public with sharing class MockedMethod {
332322
*
333323
* Note: This only works for custom exceptions.
334324
* Note: Developers construct your exception like this:
335-
* `<CustomExceptioType> customException = `
325+
* `<CustomExceptionType> customException = `
336326
* `new <CustomExceptionType>('message');`
337327
*
338-
* @param customException
328+
* @param customException The exception you want thrown.
339329
* @return `MockedMethod`
340330
*/
341331
public Stub.Builder throwingException(Exception customException) {
@@ -347,12 +337,11 @@ public with sharing class MockedMethod {
347337
/**
348338
* @description Responsible for returning a fully formed
349339
* MockedMethod instance.
350-
* @param signature
340+
* @param signature A MethodSignature object
351341
* @return `MockedMethod`
352342
*/
353343
public MockedMethod createMockedMethod(MethodSignature signature) {
354-
MockedMethod mockedMethod = new MockedMethod(signature)
355-
.withParameterValues(runtimeParameters);
344+
MockedMethod mockedMethod = new MockedMethod(signature).withParameterValues(runtimeParameters);
356345
if (returnSObjectIds != null) {
357346
mockedMethod.returning(returnSObjectIds);
358347
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/**
2+
* @description Enum to define special parameter matching behavior for the Stub framework
3+
*/
4+
5+
public enum ParameterMatcher {
6+
/**
7+
* @description Matches any parameter value regardless of the actual runtime value
8+
*/
9+
ANYPARAMETER
10+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
3+
<apiVersion>63.0</apiVersion>
4+
<status>Active</status>
5+
</ApexClass>
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
@IsTest
2+
private class ParameterMatcherTest {
3+
4+
private class TestClass {
5+
public String methodWithParameters(String param1, Integer param2) {
6+
return param1 + String.valueOf(param2);
7+
}
8+
}
9+
10+
@IsTest
11+
static void testAnyParameterMatcher() {
12+
// Arrange
13+
TestClass mockObject = (TestClass) new Stub.Builder(TestClass.class)
14+
.mockingMethodCall('methodWithParameters')
15+
.withParameterTypes(String.class, Integer.class)
16+
.withParameterValues('testValue', ParameterMatcher.ANY)
17+
.returning('mocked result')
18+
.defineStub(true);
19+
20+
// Act
21+
String result1 = mockObject.methodWithParameters('testValue', 42);
22+
String result2 = mockObject.methodWithParameters('testValue', 999);
23+
24+
// Assert
25+
Assert.areEqual('mocked result', result1, 'Should return mocked result with any second parameter (42)');
26+
Assert.areEqual('mocked result', result2, 'Should return mocked result with any second parameter (999)');
27+
}
28+
29+
@IsTest
30+
static void testMultipleAnyParameterMatchers() {
31+
// Arrange
32+
TestClass mockObject = (TestClass) new Stub.Builder(TestClass.class)
33+
.mockingMethodCall('methodWithParameters')
34+
.withParameterTypes(String.class, Integer.class)
35+
.withParameterValues(ParameterMatcher.ANY, ParameterMatcher.ANY)
36+
.returning('any params result')
37+
.defineStub(true);
38+
39+
// Act
40+
String result1 = mockObject.methodWithParameters('value1', 42);
41+
String result2 = mockObject.methodWithParameters('value2', 999);
42+
43+
// Assert
44+
Assert.areEqual('any params result', result1, 'Should return mocked result with any parameters (1)');
45+
Assert.areEqual('any params result', result2, 'Should return mocked result with any parameters (2)');
46+
}
47+
48+
@IsTest
49+
static void testMixedExactAndAnyMatchers() {
50+
// Arrange
51+
TestClass mockObject = (TestClass) new Stub.Builder(TestClass.class)
52+
// First stub with exact first parameter and ANY second parameter
53+
.mockingMethodCall('methodWithParameters')
54+
.withParameterTypes(String.class, Integer.class)
55+
.withParameterValues('exact', ParameterMatcher.ANY)
56+
.returning('exact first, any second')
57+
// Second stub with ANY first parameter and exact second parameter
58+
.mockingMethodCall('methodWithParameters')
59+
.withParameterTypes(String.class, Integer.class)
60+
.withParameterValues(ParameterMatcher.ANY, 42)
61+
.returning('any first, exact second')
62+
.defineStub(true);
63+
64+
// Act & Assert
65+
Assert.areEqual(
66+
'exact first, any second',
67+
mockObject.methodWithParameters('exact', 999),
68+
'Should match first stub with exact first parameter'
69+
);
70+
71+
Assert.areEqual(
72+
'any first, exact second',
73+
mockObject.methodWithParameters('different', 42),
74+
'Should match second stub with exact second parameter'
75+
);
76+
}
77+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
3+
<apiVersion>63.0</apiVersion>
4+
<status>Active</status>
5+
</ApexClass>

0 commit comments

Comments
 (0)