Skip to content

Commit

Permalink
Bug fix for state not resetting and rename inner classes (#21)
Browse files Browse the repository at this point in the history
* Bug fix for state not resetting

We had a bug with state not resetting after each method chain was
completed. This fixes that and adds tests to ensure the behavior.

* Rename inner classes

Rename inner classes and add more asserts
  • Loading branch information
surajp committed May 21, 2023
1 parent 102a78b commit e2530d5
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 41 deletions.
86 changes: 48 additions & 38 deletions force-app/main/default/classes/UniversalMocker.cls
Expand Up @@ -31,10 +31,10 @@ public with sharing class UniversalMocker implements System.StubProvider {
Map<String, List<Mutator>> mutatorMap = new Map<String, List<Mutator>>();

// Inner class instances
private SetupMode_Entry setupAInstance;
private AssertMode_Entry assertAInstance;
private AssertMode_Midway assertBInstance;
private GetParamsMode_Entry getParamsAInstance;
private InitialSetupState setupAInstance;
private InitialValidationState assertAInstance;
private IntermediateValidationState assertBInstance;
private InitialParamValidationState getParamsAInstance;

private enum Modes {
SETUP,
Expand All @@ -60,19 +60,19 @@ public with sharing class UniversalMocker implements System.StubProvider {
return Test.createStub(this.mockedClass, this);
}

public class SetupMode_Entry extends SetupMode_Midway {
private SetupMode_Entry(UniversalMocker parent) {
public class InitialSetupState extends IntermediateSetupState {
private InitialSetupState(UniversalMocker parent) {
super(parent);
}
public SetupMode_Midway withParamTypes(List<Type> paramTypes) {
public IntermediateSetupState withParamTypes(List<Type> paramTypes) {
this.parent.withParamTypes(paramTypes);
return (SetupMode_Midway) this;
return (IntermediateSetupState) this;
}
}

public virtual class SetupMode_Midway {
public virtual class IntermediateSetupState {
private final UniversalMocker parent;
private SetupMode_Midway(UniversalMocker parent) {
private IntermediateSetupState(UniversalMocker parent) {
this.parent = parent;
}
public void thenReturnVoid() {
Expand All @@ -81,7 +81,7 @@ public with sharing class UniversalMocker implements System.StubProvider {
public void thenReturn(Object returnObject) {
this.parent.thenReturn(returnObject);
}
public SetupMode_Midway mutateWith(Mutator mutatorInstance) {
public IntermediateSetupState mutateWith(Mutator mutatorInstance) {
this.parent.mutateWith(mutatorInstance);
return this;
}
Expand All @@ -90,30 +90,30 @@ public with sharing class UniversalMocker implements System.StubProvider {
}
}

public class AssertMode_Entry {
public class InitialValidationState {
private final UniversalMocker parent;
private AssertMode_Entry(UniversalMocker parent) {
private InitialValidationState(UniversalMocker parent) {
this.parent = parent;
}
public AssertMode_Midway method(String methodName) {
public IntermediateValidationState method(String methodName) {
parent.method(methodName);
return parent.assertBInstance;
}
}

public class AssertMode_Midway extends AssertMode_Exit {
private AssertMode_Midway(UniversalMocker parent) {
public class IntermediateValidationState extends FinalValidationState {
private IntermediateValidationState(UniversalMocker parent) {
super(parent);
}
public AssertMode_Exit withParamTypes(List<Type> paramTypes) {
public FinalValidationState withParamTypes(List<Type> paramTypes) {
parent.withParamTypes(paramTypes);
return (AssertMode_Exit) this;
return (FinalValidationState) this;
}
}

public virtual class AssertMode_Exit {
public virtual class FinalValidationState {
private final UniversalMocker parent;
private AssertMode_Exit(UniversalMocker parent) {
private FinalValidationState(UniversalMocker parent) {
this.parent = parent;
}
public void wasCalled(Integer expectedCallCount, Times assertTypeValue) {
Expand All @@ -127,29 +127,29 @@ public with sharing class UniversalMocker implements System.StubProvider {
}
}

public class GetParamsMode_Entry extends GetParamsMode_Midway {
private GetParamsMode_Entry(UniversalMocker parent) {
public class InitialParamValidationState extends IntermediateParamValidationState {
private InitialParamValidationState(UniversalMocker parent) {
super(parent);
}
public GetParamsMode_Midway withParamTypes(List<Type> paramTypes) {
public IntermediateParamValidationState withParamTypes(List<Type> paramTypes) {
parent.withParamTypes(paramTypes);
return (GetParamsMode_Midway) this;
return (IntermediateParamValidationState) this;
}
}

public virtual class GetParamsMode_Midway extends GetParamsMode_Exit {
private GetParamsMode_Midway(UniversalMocker parent) {
public virtual class IntermediateParamValidationState extends FinalParamValidationState {
private IntermediateParamValidationState(UniversalMocker parent) {
super(parent);
}
public GetParamsMode_Exit andInvocationNumber(Integer invocation) {
public FinalParamValidationState andInvocationNumber(Integer invocation) {
parent.andInvocationNumber(invocation);
return (GetParamsMode_Exit) this;
return (FinalParamValidationState) this;
}
}

public virtual class GetParamsMode_Exit {
public virtual class FinalParamValidationState {
private final UniversalMocker parent;
private GetParamsMode_Exit(UniversalMocker parent) {
private FinalParamValidationState(UniversalMocker parent) {
this.parent = parent;
}
public Object getValueOf(String paramName) {
Expand All @@ -160,7 +160,7 @@ public with sharing class UniversalMocker implements System.StubProvider {
}
}

public SetupMode_Entry when(String stubbedMethodName) {
public InitialSetupState when(String stubbedMethodName) {
this.currentMethodName = stubbedMethodName;
return this.setupAInstance;
}
Expand Down Expand Up @@ -192,11 +192,11 @@ public with sharing class UniversalMocker implements System.StubProvider {
return returnValue;
}

public AssertMode_Entry assertThat() {
public InitialValidationState assertThat() {
return this.assertAInstance;
}

public GetParamsMode_Entry forMethod(String stubbedMethodName) {
public InitialParamValidationState forMethod(String stubbedMethodName) {
this.currentMethodName = stubbedMethodName;
return this.getParamsAInstance;
}
Expand Down Expand Up @@ -238,6 +238,7 @@ public with sharing class UniversalMocker implements System.StubProvider {
if (!this.callCountsMap.containsKey(key)) {
this.callCountsMap.put(key, 0);
}
this.reset();
}

private void thenThrow(Exception exceptionToThrow) {
Expand All @@ -258,6 +259,7 @@ public with sharing class UniversalMocker implements System.StubProvider {
//Integer actualCallCount = this.callCountsMap.get(currentKey);
Integer actualCallCount = this.getCallCountsMapInternal().get(currentKey);
String methodName = this.currentMethodName;
this.reset();
switch on assertTypeValue {
when OR_LESS {
system.assert(this.expectedCallCount >= actualCallCount, this.getMethodCallCountAssertMessage(methodName, 'less than or equal'));
Expand All @@ -275,6 +277,7 @@ public with sharing class UniversalMocker implements System.StubProvider {
String currentKey = this.getCurrentKey();
Integer actualCallCount = this.getCallCountsMapInternal().get(currentKey);
String methodName = this.currentMethodName;
this.reset();
if (actualCallCount != null) {
this.expectedCallCount = 0;
system.assertEquals(this.expectedCallCount, actualCallCount, String.format('Method {0} was called 1 or more times', new List<String>{ methodName }));
Expand All @@ -292,18 +295,20 @@ public with sharing class UniversalMocker implements System.StubProvider {
throw new IllegalArgumentException(String.format('Param name {0} not found for the method {1}', new List<Object>{ paramName, this.currentMethodName }));
}
Object returnValue = paramsMap.get(paramName.toLowerCase());
this.reset();
return returnValue;
}

private Map<String, Object> getArgumentsMap() {
String theKey = this.getCurrentKey();
Map<String, Object> returnValue = this.getArgumentsMapInternal().get(theKey).get(this.forInvocationNumber);
this.reset();
return returnValue;
}

private String getCurrentKey() {
String retVal = this.currentMethodName;
if (this.currentParamTypesString != null) {
if (!String.isEmpty(this.currentParamTypesString)) {
retVal += KEY_DELIMITER + this.currentParamTypesString;
}
return retVal.toLowerCase();
Expand Down Expand Up @@ -372,10 +377,15 @@ public with sharing class UniversalMocker implements System.StubProvider {
}

private void initInnerClassInstances() {
this.setupAInstance = new SetupMode_Entry(this);
this.assertAInstance = new AssertMode_Entry(this);
this.assertBInstance = new AssertMode_Midway(this);
this.getParamsAInstance = new GetParamsMode_Entry(this);
this.setupAInstance = new InitialSetupState(this);
this.assertAInstance = new InitialValidationState(this);
this.assertBInstance = new IntermediateValidationState(this);
this.getParamsAInstance = new InitialParamValidationState(this);
}

private void reset() {
this.currentParamTypesString = '';
this.currentMethodName = '';
}

/* End Private Methods */
Expand Down
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>53.0</apiVersion>
<apiVersion>56.0</apiVersion>
<status>Active</status>
</ApexClass>
35 changes: 33 additions & 2 deletions force-app/main/default/classes/example/AccountDomainTest.cls
Expand Up @@ -5,9 +5,9 @@ public with sharing class AccountDomainTest {
private static final AccountDomain sut; // system under test

static {
mockService = UniversalMocker.mock(AccountDBService.class);
mockService = UniversalMocker.mock(AccountDBService.class); //This is the service we are mocking
mockServiceStub = (AccountDBService) mockService.createStub();
sut = new AccountDomain(mockServiceStub);
sut = new AccountDomain(mockServiceStub); //This is the class into which we inject our mocked service
}

@IsTest
Expand Down Expand Up @@ -102,6 +102,37 @@ public with sharing class AccountDomainTest {
System.assertEquals(acctTwo.Name, acctsWithMatchingName[0].Name);
}

@IsTest
public static void shouldResetMethodAndParamsAfterEachChain() {
//setup
String mockedMethodName = 'getMatchingAccounts';
String newMethodName = 'getOneAccount';
Account acctOne = new Account(Name = 'Account with matching Id');
Account acctTwo = new Account(Name = 'Account with matching name');

mockService.when(mockedMethodName).withParamTypes(new List<Type>{ Id.class }).thenReturn(new List<Account>{ acctOne });
mockService.when(newMethodName).thenReturn(acctTwo); //This method takes no params. So by mocking this we attempt to ensure that the param type list from previous mock call (Id.class) has been cleared out

//test
Test.startTest();
Id mockAccountId = '001000000000001';
List<Account> acctsWithMatchingId = sut.getMatchingAccounts(mockAccountId);
Account anotherAccount = sut.getAccountDetail();
Test.stopTest();

//verify
mockService.assertThat().method(mockedMethodName).withParamTypes(new List<Type>{ Id.class }).wasCalled(1);
mockService.assertThat().method(newMethodName).wasCalled(1);

System.assertEquals(acctOne.Name, acctsWithMatchingId[0].Name);
System.assertEquals(acctTwo.Name, anotherAccount.Name);

Id acctIdParam = (Id) mockService.forMethod(mockedMethodName).withParamTypes(new List<Type>{ Id.class }).getValueOf('accountId');
Map<String, Object> argsMap = mockService.forMethod(newMethodName).getArgumentsMap();
Assert.areEqual(mockAccountId, acctIdParam);
Assert.areEqual(0, argsMap.size());
}

@IsTest
public static void it_should_throw_mock_exception() {
//setup
Expand Down

0 comments on commit e2530d5

Please sign in to comment.