From 1b3ba7b167cfdf6ae2433e4a03b308049f9ce020 Mon Sep 17 00:00:00 2001 From: Robert Baillie Date: Wed, 4 May 2022 11:51:53 +0100 Subject: [PATCH] Put apex-mocks and apex-extensions into the fflib directory --- sfdx-source/apex-mocks/fflib_Answer.cls | 19 + .../apex-mocks/fflib_Answer.cls-meta.xml | 5 + sfdx-source/apex-mocks/fflib_AnyOrder.cls | 105 + .../apex-mocks/fflib_AnyOrder.cls-meta.xml | 5 + sfdx-source/apex-mocks/fflib_ApexMocks.cls | 432 ++++ .../apex-mocks/fflib_ApexMocks.cls-meta.xml | 5 + .../apex-mocks/fflib_ApexMocksConfig.cls | 20 + .../fflib_ApexMocksConfig.cls-meta.xml | 5 + .../apex-mocks/fflib_ApexMocksUtils.cls | 250 +++ .../fflib_ApexMocksUtils.cls-meta.xml | 4 + .../apex-mocks/fflib_ArgumentCaptor.cls | 118 ++ .../fflib_ArgumentCaptor.cls-meta.xml | 5 + sfdx-source/apex-mocks/fflib_IDGenerator.cls | 43 + .../apex-mocks/fflib_IDGenerator.cls-meta.xml | 4 + sfdx-source/apex-mocks/fflib_IMatcher.cls | 36 + .../apex-mocks/fflib_IMatcher.cls-meta.xml | 4 + sfdx-source/apex-mocks/fflib_InOrder.cls | 356 ++++ .../apex-mocks/fflib_InOrder.cls-meta.xml | 5 + sfdx-source/apex-mocks/fflib_Inheritor.cls | 14 + .../apex-mocks/fflib_Inheritor.cls-meta.xml | 4 + .../apex-mocks/fflib_InvocationOnMock.cls | 84 + .../fflib_InvocationOnMock.cls-meta.xml | 5 + sfdx-source/apex-mocks/fflib_Match.cls | 1158 +++++++++++ .../apex-mocks/fflib_Match.cls-meta.xml | 4 + .../apex-mocks/fflib_MatcherDefinitions.cls | 1390 +++++++++++++ .../fflib_MatcherDefinitions.cls-meta.xml | 4 + .../apex-mocks/fflib_MatchersReturnValue.cls | 36 + .../fflib_MatchersReturnValue.cls-meta.xml | 4 + .../apex-mocks/fflib_MethodArgValues.cls | 70 + .../fflib_MethodArgValues.cls-meta.xml | 4 + .../apex-mocks/fflib_MethodCountRecorder.cls | 61 + .../fflib_MethodCountRecorder.cls-meta.xml | 4 + .../apex-mocks/fflib_MethodReturnValue.cls | 162 ++ .../fflib_MethodReturnValue.cls-meta.xml | 4 + .../fflib_MethodReturnValueRecorder.cls | 121 ++ ...lib_MethodReturnValueRecorder.cls-meta.xml | 4 + .../apex-mocks/fflib_MethodVerifier.cls | 154 ++ .../fflib_MethodVerifier.cls-meta.xml | 5 + .../apex-mocks/fflib_QualifiedMethod.cls | 82 + .../fflib_QualifiedMethod.cls-meta.xml | 4 + .../fflib_QualifiedMethodAndArgValues.cls | 40 + ...b_QualifiedMethodAndArgValues.cls-meta.xml | 5 + sfdx-source/apex-mocks/fflib_System.cls | 52 + .../apex-mocks/fflib_System.cls-meta.xml | 5 + .../apex-mocks/fflib_VerificationMode.cls | 139 ++ .../fflib_VerificationMode.cls-meta.xml | 5 + .../apex-mocks/tests/fflib_AnswerTest.cls | 438 ++++ .../tests/fflib_AnswerTest.cls-meta.xml | 5 + .../apex-mocks/tests/fflib_AnyOrderTest.cls | 1195 +++++++++++ .../tests/fflib_AnyOrderTest.cls-meta.xml | 5 + .../apex-mocks/tests/fflib_ApexMocksTest.cls | 1627 +++++++++++++++ .../tests/fflib_ApexMocksTest.cls-meta.xml | 5 + .../tests/fflib_ApexMocksUtilsTest.cls | 261 +++ .../fflib_ApexMocksUtilsTest.cls-meta.xml | 4 + .../tests/fflib_ArgumentCaptorTest.cls | 702 +++++++ .../fflib_ArgumentCaptorTest.cls-meta.xml | 5 + .../tests/fflib_IDGeneratorTest.cls | 56 + .../tests/fflib_IDGeneratorTest.cls-meta.xml | 4 + .../apex-mocks/tests/fflib_InOrderTest.cls | 1831 +++++++++++++++++ .../tests/fflib_InOrderTest.cls-meta.xml | 5 + .../apex-mocks/tests/fflib_InheritorTest.cls | 33 + .../tests/fflib_InheritorTest.cls-meta.xml | 4 + .../apex-mocks/tests/fflib_MatchTest.cls | 1387 +++++++++++++ .../tests/fflib_MatchTest.cls-meta.xml | 4 + .../tests/fflib_MatcherDefinitionsTest.cls | 1634 +++++++++++++++ .../fflib_MatcherDefinitionsTest.cls-meta.xml | 4 + .../tests/fflib_MethodArgValuesTest.cls | 108 + .../fflib_MethodArgValuesTest.cls-meta.xml | 4 + sfdx-source/apex-mocks/tests/fflib_MyList.cls | 67 + .../tests/fflib_MyList.cls-meta.xml | 5 + .../fflib_QualifiedMethodAndArgValuesTest.cls | 24 + ...alifiedMethodAndArgValuesTest.cls-meta.xml | 5 + .../tests/fflib_QualifiedMethodTest.cls | 152 ++ .../fflib_QualifiedMethodTest.cls-meta.xml | 4 + .../apex-mocks/tests/fflib_SystemTest.cls | 136 ++ .../tests/fflib_SystemTest.cls-meta.xml | 5 + .../apex-mocks/tests/mocks/fflib_Mocks.cls | 60 + .../tests/mocks/fflib_Mocks.cls-meta.xml | 5 + .../classes/criteria/fflib_Comparator.cls | 186 ++ .../criteria/fflib_Comparator.cls-meta.xml | 5 + .../classes/criteria/fflib_Criteria.cls | 1086 ++++++++++ .../criteria/fflib_Criteria.cls-meta.xml | 5 + .../classes/criteria/fflib_Operator.cls | 47 + .../criteria/fflib_Operator.cls-meta.xml | 5 + .../classes/domains/fflib_DateTimes.cls | 58 + .../domains/fflib_DateTimes.cls-meta.xml | 5 + .../default/classes/domains/fflib_Dates.cls | 58 + .../classes/domains/fflib_Dates.cls-meta.xml | 5 + .../classes/domains/fflib_Decimals.cls | 58 + .../domains/fflib_Decimals.cls-meta.xml | 5 + .../default/classes/domains/fflib_Doubles.cls | 59 + .../domains/fflib_Doubles.cls-meta.xml | 5 + .../default/classes/domains/fflib_Ids.cls | 75 + .../classes/domains/fflib_Ids.cls-meta.xml | 5 + .../classes/domains/fflib_Integers.cls | 58 + .../domains/fflib_Integers.cls-meta.xml | 5 + .../default/classes/domains/fflib_Longs.cls | 58 + .../classes/domains/fflib_Longs.cls-meta.xml | 5 + .../classes/domains/fflib_Objects2.cls | 48 + .../domains/fflib_Objects2.cls-meta.xml | 5 + .../classes/domains/fflib_SObjects2.cls | 719 +++++++ .../domains/fflib_SObjects2.cls-meta.xml | 5 + .../default/classes/domains/fflib_Strings.cls | 75 + .../domains/fflib_Strings.cls-meta.xml | 5 + .../tests/fflib_PrimitiveDomainsTest.cls | 184 ++ .../fflib_PrimitiveDomainsTest.cls-meta.xml | 5 + .../domains/tests/fflib_SObjects2Test.cls | 445 ++++ .../tests/fflib_SObjects2Test.cls-meta.xml | 5 + .../classes/tests/fflib_ComparatorTest.cls | 296 +++ .../tests/fflib_ComparatorTest.cls-meta.xml | 5 + .../classes/tests/fflib_CriteriaTest.cls | 650 ++++++ .../tests/fflib_CriteriaTest.cls-meta.xml | 5 + .../classes/utils/fflib_MockSObjectUtil.cls | 18 + .../utils/fflib_MockSObjectUtil.cls-meta.xml | 5 + .../classes/utils/fflib_OperatorUtils.cls | 75 + .../utils/fflib_OperatorUtils.cls-meta.xml | 5 + .../classes/utils/fflib_StringUtils.cls | 47 + .../utils/fflib_StringUtils.cls-meta.xml | 5 + .../utils/tests/fflib_OperatorUtilsTest.cls | 49 + .../fflib_OperatorUtilsTest.cls-meta.xml | 5 + 120 files changed, 19283 insertions(+) create mode 100644 sfdx-source/apex-mocks/fflib_Answer.cls create mode 100644 sfdx-source/apex-mocks/fflib_Answer.cls-meta.xml create mode 100644 sfdx-source/apex-mocks/fflib_AnyOrder.cls create mode 100644 sfdx-source/apex-mocks/fflib_AnyOrder.cls-meta.xml create mode 100644 sfdx-source/apex-mocks/fflib_ApexMocks.cls create mode 100644 sfdx-source/apex-mocks/fflib_ApexMocks.cls-meta.xml create mode 100644 sfdx-source/apex-mocks/fflib_ApexMocksConfig.cls create mode 100644 sfdx-source/apex-mocks/fflib_ApexMocksConfig.cls-meta.xml create mode 100644 sfdx-source/apex-mocks/fflib_ApexMocksUtils.cls create mode 100644 sfdx-source/apex-mocks/fflib_ApexMocksUtils.cls-meta.xml create mode 100644 sfdx-source/apex-mocks/fflib_ArgumentCaptor.cls create mode 100644 sfdx-source/apex-mocks/fflib_ArgumentCaptor.cls-meta.xml create mode 100644 sfdx-source/apex-mocks/fflib_IDGenerator.cls create mode 100644 sfdx-source/apex-mocks/fflib_IDGenerator.cls-meta.xml create mode 100644 sfdx-source/apex-mocks/fflib_IMatcher.cls create mode 100644 sfdx-source/apex-mocks/fflib_IMatcher.cls-meta.xml create mode 100644 sfdx-source/apex-mocks/fflib_InOrder.cls create mode 100644 sfdx-source/apex-mocks/fflib_InOrder.cls-meta.xml create mode 100644 sfdx-source/apex-mocks/fflib_Inheritor.cls create mode 100644 sfdx-source/apex-mocks/fflib_Inheritor.cls-meta.xml create mode 100644 sfdx-source/apex-mocks/fflib_InvocationOnMock.cls create mode 100644 sfdx-source/apex-mocks/fflib_InvocationOnMock.cls-meta.xml create mode 100644 sfdx-source/apex-mocks/fflib_Match.cls create mode 100644 sfdx-source/apex-mocks/fflib_Match.cls-meta.xml create mode 100644 sfdx-source/apex-mocks/fflib_MatcherDefinitions.cls create mode 100644 sfdx-source/apex-mocks/fflib_MatcherDefinitions.cls-meta.xml create mode 100644 sfdx-source/apex-mocks/fflib_MatchersReturnValue.cls create mode 100644 sfdx-source/apex-mocks/fflib_MatchersReturnValue.cls-meta.xml create mode 100644 sfdx-source/apex-mocks/fflib_MethodArgValues.cls create mode 100644 sfdx-source/apex-mocks/fflib_MethodArgValues.cls-meta.xml create mode 100644 sfdx-source/apex-mocks/fflib_MethodCountRecorder.cls create mode 100644 sfdx-source/apex-mocks/fflib_MethodCountRecorder.cls-meta.xml create mode 100644 sfdx-source/apex-mocks/fflib_MethodReturnValue.cls create mode 100644 sfdx-source/apex-mocks/fflib_MethodReturnValue.cls-meta.xml create mode 100644 sfdx-source/apex-mocks/fflib_MethodReturnValueRecorder.cls create mode 100644 sfdx-source/apex-mocks/fflib_MethodReturnValueRecorder.cls-meta.xml create mode 100644 sfdx-source/apex-mocks/fflib_MethodVerifier.cls create mode 100644 sfdx-source/apex-mocks/fflib_MethodVerifier.cls-meta.xml create mode 100644 sfdx-source/apex-mocks/fflib_QualifiedMethod.cls create mode 100644 sfdx-source/apex-mocks/fflib_QualifiedMethod.cls-meta.xml create mode 100644 sfdx-source/apex-mocks/fflib_QualifiedMethodAndArgValues.cls create mode 100644 sfdx-source/apex-mocks/fflib_QualifiedMethodAndArgValues.cls-meta.xml create mode 100644 sfdx-source/apex-mocks/fflib_System.cls create mode 100644 sfdx-source/apex-mocks/fflib_System.cls-meta.xml create mode 100644 sfdx-source/apex-mocks/fflib_VerificationMode.cls create mode 100644 sfdx-source/apex-mocks/fflib_VerificationMode.cls-meta.xml create mode 100644 sfdx-source/apex-mocks/tests/fflib_AnswerTest.cls create mode 100644 sfdx-source/apex-mocks/tests/fflib_AnswerTest.cls-meta.xml create mode 100644 sfdx-source/apex-mocks/tests/fflib_AnyOrderTest.cls create mode 100644 sfdx-source/apex-mocks/tests/fflib_AnyOrderTest.cls-meta.xml create mode 100644 sfdx-source/apex-mocks/tests/fflib_ApexMocksTest.cls create mode 100644 sfdx-source/apex-mocks/tests/fflib_ApexMocksTest.cls-meta.xml create mode 100644 sfdx-source/apex-mocks/tests/fflib_ApexMocksUtilsTest.cls create mode 100644 sfdx-source/apex-mocks/tests/fflib_ApexMocksUtilsTest.cls-meta.xml create mode 100644 sfdx-source/apex-mocks/tests/fflib_ArgumentCaptorTest.cls create mode 100644 sfdx-source/apex-mocks/tests/fflib_ArgumentCaptorTest.cls-meta.xml create mode 100644 sfdx-source/apex-mocks/tests/fflib_IDGeneratorTest.cls create mode 100644 sfdx-source/apex-mocks/tests/fflib_IDGeneratorTest.cls-meta.xml create mode 100644 sfdx-source/apex-mocks/tests/fflib_InOrderTest.cls create mode 100644 sfdx-source/apex-mocks/tests/fflib_InOrderTest.cls-meta.xml create mode 100644 sfdx-source/apex-mocks/tests/fflib_InheritorTest.cls create mode 100644 sfdx-source/apex-mocks/tests/fflib_InheritorTest.cls-meta.xml create mode 100644 sfdx-source/apex-mocks/tests/fflib_MatchTest.cls create mode 100644 sfdx-source/apex-mocks/tests/fflib_MatchTest.cls-meta.xml create mode 100644 sfdx-source/apex-mocks/tests/fflib_MatcherDefinitionsTest.cls create mode 100644 sfdx-source/apex-mocks/tests/fflib_MatcherDefinitionsTest.cls-meta.xml create mode 100644 sfdx-source/apex-mocks/tests/fflib_MethodArgValuesTest.cls create mode 100644 sfdx-source/apex-mocks/tests/fflib_MethodArgValuesTest.cls-meta.xml create mode 100644 sfdx-source/apex-mocks/tests/fflib_MyList.cls create mode 100644 sfdx-source/apex-mocks/tests/fflib_MyList.cls-meta.xml create mode 100644 sfdx-source/apex-mocks/tests/fflib_QualifiedMethodAndArgValuesTest.cls create mode 100644 sfdx-source/apex-mocks/tests/fflib_QualifiedMethodAndArgValuesTest.cls-meta.xml create mode 100644 sfdx-source/apex-mocks/tests/fflib_QualifiedMethodTest.cls create mode 100644 sfdx-source/apex-mocks/tests/fflib_QualifiedMethodTest.cls-meta.xml create mode 100644 sfdx-source/apex-mocks/tests/fflib_SystemTest.cls create mode 100644 sfdx-source/apex-mocks/tests/fflib_SystemTest.cls-meta.xml create mode 100644 sfdx-source/apex-mocks/tests/mocks/fflib_Mocks.cls create mode 100644 sfdx-source/apex-mocks/tests/mocks/fflib_Mocks.cls-meta.xml create mode 100644 sfdx-source/fflib-apex-extensions/default/classes/criteria/fflib_Comparator.cls create mode 100644 sfdx-source/fflib-apex-extensions/default/classes/criteria/fflib_Comparator.cls-meta.xml create mode 100755 sfdx-source/fflib-apex-extensions/default/classes/criteria/fflib_Criteria.cls create mode 100644 sfdx-source/fflib-apex-extensions/default/classes/criteria/fflib_Criteria.cls-meta.xml create mode 100644 sfdx-source/fflib-apex-extensions/default/classes/criteria/fflib_Operator.cls create mode 100644 sfdx-source/fflib-apex-extensions/default/classes/criteria/fflib_Operator.cls-meta.xml create mode 100644 sfdx-source/fflib-apex-extensions/default/classes/domains/fflib_DateTimes.cls create mode 100644 sfdx-source/fflib-apex-extensions/default/classes/domains/fflib_DateTimes.cls-meta.xml create mode 100644 sfdx-source/fflib-apex-extensions/default/classes/domains/fflib_Dates.cls create mode 100644 sfdx-source/fflib-apex-extensions/default/classes/domains/fflib_Dates.cls-meta.xml create mode 100644 sfdx-source/fflib-apex-extensions/default/classes/domains/fflib_Decimals.cls create mode 100644 sfdx-source/fflib-apex-extensions/default/classes/domains/fflib_Decimals.cls-meta.xml create mode 100644 sfdx-source/fflib-apex-extensions/default/classes/domains/fflib_Doubles.cls create mode 100644 sfdx-source/fflib-apex-extensions/default/classes/domains/fflib_Doubles.cls-meta.xml create mode 100644 sfdx-source/fflib-apex-extensions/default/classes/domains/fflib_Ids.cls create mode 100644 sfdx-source/fflib-apex-extensions/default/classes/domains/fflib_Ids.cls-meta.xml create mode 100644 sfdx-source/fflib-apex-extensions/default/classes/domains/fflib_Integers.cls create mode 100644 sfdx-source/fflib-apex-extensions/default/classes/domains/fflib_Integers.cls-meta.xml create mode 100644 sfdx-source/fflib-apex-extensions/default/classes/domains/fflib_Longs.cls create mode 100644 sfdx-source/fflib-apex-extensions/default/classes/domains/fflib_Longs.cls-meta.xml create mode 100644 sfdx-source/fflib-apex-extensions/default/classes/domains/fflib_Objects2.cls create mode 100644 sfdx-source/fflib-apex-extensions/default/classes/domains/fflib_Objects2.cls-meta.xml create mode 100644 sfdx-source/fflib-apex-extensions/default/classes/domains/fflib_SObjects2.cls create mode 100644 sfdx-source/fflib-apex-extensions/default/classes/domains/fflib_SObjects2.cls-meta.xml create mode 100644 sfdx-source/fflib-apex-extensions/default/classes/domains/fflib_Strings.cls create mode 100644 sfdx-source/fflib-apex-extensions/default/classes/domains/fflib_Strings.cls-meta.xml create mode 100644 sfdx-source/fflib-apex-extensions/default/classes/domains/tests/fflib_PrimitiveDomainsTest.cls create mode 100644 sfdx-source/fflib-apex-extensions/default/classes/domains/tests/fflib_PrimitiveDomainsTest.cls-meta.xml create mode 100644 sfdx-source/fflib-apex-extensions/default/classes/domains/tests/fflib_SObjects2Test.cls create mode 100644 sfdx-source/fflib-apex-extensions/default/classes/domains/tests/fflib_SObjects2Test.cls-meta.xml create mode 100644 sfdx-source/fflib-apex-extensions/default/classes/tests/fflib_ComparatorTest.cls create mode 100644 sfdx-source/fflib-apex-extensions/default/classes/tests/fflib_ComparatorTest.cls-meta.xml create mode 100755 sfdx-source/fflib-apex-extensions/default/classes/tests/fflib_CriteriaTest.cls create mode 100644 sfdx-source/fflib-apex-extensions/default/classes/tests/fflib_CriteriaTest.cls-meta.xml create mode 100644 sfdx-source/fflib-apex-extensions/default/classes/utils/fflib_MockSObjectUtil.cls create mode 100644 sfdx-source/fflib-apex-extensions/default/classes/utils/fflib_MockSObjectUtil.cls-meta.xml create mode 100644 sfdx-source/fflib-apex-extensions/default/classes/utils/fflib_OperatorUtils.cls create mode 100644 sfdx-source/fflib-apex-extensions/default/classes/utils/fflib_OperatorUtils.cls-meta.xml create mode 100644 sfdx-source/fflib-apex-extensions/default/classes/utils/fflib_StringUtils.cls create mode 100644 sfdx-source/fflib-apex-extensions/default/classes/utils/fflib_StringUtils.cls-meta.xml create mode 100644 sfdx-source/fflib-apex-extensions/default/classes/utils/tests/fflib_OperatorUtilsTest.cls create mode 100644 sfdx-source/fflib-apex-extensions/default/classes/utils/tests/fflib_OperatorUtilsTest.cls-meta.xml diff --git a/sfdx-source/apex-mocks/fflib_Answer.cls b/sfdx-source/apex-mocks/fflib_Answer.cls new file mode 100644 index 00000000000..874e6f80c81 --- /dev/null +++ b/sfdx-source/apex-mocks/fflib_Answer.cls @@ -0,0 +1,19 @@ +/* + Copyright (c) 2017 FinancialForce.com, inc. All rights reserved. + */ + +/** + * Interface for the answering framework. + * This interface must be implemented inside the test class and implement the call back method answer. + * @group Core + */ +public interface fflib_Answer +{ + /** + * Method to be implemented in the test class to implement the call back method. + * @param invocation The invocation on the mock. + * @throws The exception to be thrown. + * @return The value to be returned. + */ + Object answer(fflib_InvocationOnMock invocation); +} \ No newline at end of file diff --git a/sfdx-source/apex-mocks/fflib_Answer.cls-meta.xml b/sfdx-source/apex-mocks/fflib_Answer.cls-meta.xml new file mode 100644 index 00000000000..dd61d1f917e --- /dev/null +++ b/sfdx-source/apex-mocks/fflib_Answer.cls-meta.xml @@ -0,0 +1,5 @@ + + + 52.0 + Active + diff --git a/sfdx-source/apex-mocks/fflib_AnyOrder.cls b/sfdx-source/apex-mocks/fflib_AnyOrder.cls new file mode 100644 index 00000000000..07826c393e4 --- /dev/null +++ b/sfdx-source/apex-mocks/fflib_AnyOrder.cls @@ -0,0 +1,105 @@ +/* + Copyright (c) 2017 FinancialForce.com, inc. All rights reserved. + */ + +/** + * 'Classic' invocation verifier - checks that a method was called with the given arguments the expected number of times. + * The order of method calls is not important. + * @group Core + */ +public class fflib_AnyOrder extends fflib_MethodVerifier +{ + /* + * Verifies a method was invoked the expected number of times, with the expected arguments. + * @param qualifiedMethod The method to be verified. + * @param methodArg The arguments of the method that needs to be verified. + * @param verificationMode The verification mode that holds the setting about how the verification should be performed. + */ + protected override void verify( + fflib_QualifiedMethod qm, + fflib_MethodArgValues expectedArguments, + fflib_VerificationMode verificationMode) + { + List expectedMatchers = fflib_Match.Matching ? fflib_Match.getAndClearMatchers(expectedArguments.argValues.size()) : null; + List actualArguments = fflib_MethodCountRecorder.getMethodArgumentsByTypeName().get(qm); + + Integer methodCount = getMethodCount(expectedArguments, expectedMatchers, actualArguments); + + String qualifier = ''; + Integer expectedCount = null; + + if((verificationMode.VerifyMin == verificationMode.VerifyMax) && methodCount != verificationMode.VerifyMin) + { + expectedCount = verificationMode.VerifyMin; + } + else if (verificationMode.VerifyMin != null && verificationMode.VerifyMin > methodCount) + { + expectedCount = verificationMode.VerifyMin; + qualifier = ' or more times'; + } + else if (verificationMode.VerifyMax != null && verificationMode.VerifyMax < methodCount) + { + expectedCount = verificationMode.VerifyMax; + qualifier = ' or fewer times'; + } + + if (expectedCount != null) + { + throwException(qm, '', expectedCount, qualifier, methodCount, verificationMode.CustomAssertMessage, expectedArguments, expectedMatchers, actualArguments); + } + } + + private Integer getMethodCount(fflib_MethodArgValues methodArg, List matchers, List methodArgs) + { + Integer retval = 0; + + if (methodArgs != null) + { + if (matchers != null) + { + for (fflib_MethodArgValues args : methodArgs) + { + if (fflib_Match.matchesAllArgs(args, matchers)) + { + capture(matchers); + retval ++; + } + } + } + else + { + return countCalls(methodArgs, methodArg); + } + } + + return retval; + } + + private Integer countCalls(List methodArgs, fflib_MethodArgValues methodArg) + { + Integer count = 0; + + for(fflib_MethodArgValues arg: methodArgs) + { + if( arg == methodArg) count++; + } + + return count; + } + + /* + * Method that validate the verification mode used in the verify. + * Not all the methods from the fflib_VerificationMode are implemented for the different classes that extends the fflib_MethodVerifier. + * The error is thrown at run time, so this method is called in the method that actually performs the verify. + * @param verificationMode The verification mode that have to been verified. + * @throws Exception with message for the fflib_VerificationMode not implemented. + */ + protected override void validateMode(fflib_VerificationMode verificationMode) + { + if(verificationMode.Method == fflib_VerificationMode.ModeName.CALLS) + { + throw new fflib_ApexMocks.ApexMocksException( + 'The calls() method is available only in the InOrder Verification.'); + } + } +} diff --git a/sfdx-source/apex-mocks/fflib_AnyOrder.cls-meta.xml b/sfdx-source/apex-mocks/fflib_AnyOrder.cls-meta.xml new file mode 100644 index 00000000000..dd61d1f917e --- /dev/null +++ b/sfdx-source/apex-mocks/fflib_AnyOrder.cls-meta.xml @@ -0,0 +1,5 @@ + + + 52.0 + Active + diff --git a/sfdx-source/apex-mocks/fflib_ApexMocks.cls b/sfdx-source/apex-mocks/fflib_ApexMocks.cls new file mode 100644 index 00000000000..be18f4aeafe --- /dev/null +++ b/sfdx-source/apex-mocks/fflib_ApexMocks.cls @@ -0,0 +1,432 @@ +/* + Copyright (c) 2014-2017 FinancialForce.com, inc. All rights reserved. + */ + +/** + * @group Core + */ +public with sharing class fflib_ApexMocks implements System.StubProvider +{ + public static final Integer NEVER = 0; + + private final fflib_MethodCountRecorder methodCountRecorder; + private final fflib_MethodReturnValueRecorder methodReturnValueRecorder; + + private fflib_MethodVerifier methodVerifier; + private fflib_VerificationMode verificationMode; + private fflib_Answer myAnswer; + + public Boolean verifying { get; set; } + + public Boolean Stubbing + { + get + { + return methodReturnValueRecorder.Stubbing; + } + + private set; + } + + public List DoThrowWhenExceptions + { + get + { + return methodReturnValueRecorder.DoThrowWhenExceptions; + } + + set + { + methodReturnValueRecorder.DoThrowWhenExceptions = value; + } + } + + /** + * Construct an ApexMocks instance. + */ + public fflib_ApexMocks() + { + this.verifying = false; + + this.methodCountRecorder = new fflib_MethodCountRecorder(); + this.verificationMode = new fflib_VerificationMode(); + this.methodVerifier = new fflib_AnyOrder(); + + this.methodReturnValueRecorder = new fflib_MethodReturnValueRecorder(); + + this.methodReturnValueRecorder.Stubbing = false; + } + + /** + * Creates mock object of given class or interface. + * @param classToMock class or interface to mock. + * @return mock object. + */ + public Object mock(Type classToMock) + { + return Test.createStub(classToMock, this); + } + + /** + * Inherited from StubProvider. + * @param stubbedObject The stubbed object. + * @param stubbedMethodName The name of the invoked method. + * @param returnType The return type of the invoked method. + * @param listOfParamTypes A list of the parameter types of the invoked method. + * @param listOfParamNames A list of the parameter names of the invoked method. + * @param listOfArgs The actual argument values passed into this method at runtime. + * @return The stubbed return value. Null by default, unless you prepared one that matches this method and argument values in stubbing. + */ + public Object handleMethodCall(Object stubbedObject, String stubbedMethodName, Type returnType, + List listOfParamTypes, List listOfParamNames, List listOfArgs) + { + return mockNonVoidMethod(stubbedObject, stubbedMethodName, listOfParamTypes, listOfArgs); + } + + public static String extractTypeName(Object mockInstance) + { + return String.valueOf(mockInstance).split(':').get(0); + } + + /** + * Verify a method was called on a mock object. + * @param mockInstance The mock object instance. + * @return The mock object instance. + */ + public Object verify(Object mockInstance) + { + return verify(mockInstance, this.times(1)); + } + + /** + * Verify a method was called on a mock object. + * @param mockInstance The mock object instance. + * @param verificationMode Defines the constraints for performing the verification (e.g. the minimum and maximum expected invocation counts). + * @return The mock object instance. + */ + public Object verify(Object mockInstance, fflib_VerificationMode verificationMode) + { + this.verifying = true; + this.verificationMode = verificationMode; + + return mockInstance; + } + + /** + * Verify a method was called on a mock object. + * @param mockInstance The mock object instance. + * @param times The number of times you expect the method to have been called. + * @return The mock object instance. + */ + public Object verify(Object mockInstance, Integer times) + { + return verify(mockInstance, this.times(times)); + } + + /** + * Verfiy a method was called on a mock object. + * @param mockInvocation The invocation on the mock containing information about the method and the arguments. + */ + public void verifyMethodCall(fflib_InvocationOnMock mockInvocation) + { + this.methodVerifier.verifyMethodCall(mockInvocation, verificationMode); + this.methodVerifier = new fflib_AnyOrder(); + + this.verifying = false; + } + + /** + * Tell ApexMocks framework you are about to start stubbing using when() calls. + */ + public void startStubbing() + { + methodReturnValueRecorder.Stubbing = true; + } + + /** + * Tell ApexMocks framework you are about to stop stubbing using when() calls. + */ + public void stopStubbing() + { + methodReturnValueRecorder.Stubbing = false; + } + + /** + * Setup when stubbing for a mock object instance. + * @param ignoredRetVal This is the return value from the method called on the mockInstance, and is ignored here since we are about to setup + * the stubbed return value using thenReturn() (see MethodReturnValue class below). + */ + public fflib_MethodReturnValue when(Object ignoredRetVal) + { + return methodReturnValueRecorder.MethodReturnValue; + } + + /** + * Record a method was called on a mock object. + * @param mockInvocation The invocation on the mock containing information about the method and the arguments. + */ + public void recordMethod(fflib_InvocationOnMock mockInvocation) + { + methodCountRecorder.recordMethod(mockInvocation); + } + + /** + * Prepare a stubbed method return value. + * @param mockInvocation The invocation on the mock containing information about the method and the arguments. + * @return The MethodReturnValue instance. + */ + public fflib_MethodReturnValue prepareMethodReturnValue(fflib_InvocationOnMock mockInvocation) + { + return methodReturnValueRecorder.prepareMethodReturnValue(mockInvocation); + } + + /** + * Get the method return value for the given method call. + * @param mockInvocation The invocation on the mock containing information about the method and the arguments. + * @return The MethodReturnValue instance. + */ + public fflib_MethodReturnValue getMethodReturnValue(fflib_InvocationOnMock mockInvocation) + { + return methodReturnValueRecorder.getMethodReturnValue(mockInvocation); + } + + /** + * Setup exception stubbing for a void method. + * @param e The exception to throw. + * @param mockInstance The mock object instance. + */ + public Object doThrowWhen(Exception e, Object mockInstance) + { + methodReturnValueRecorder.prepareDoThrowWhenExceptions(new List{e}); + return mockInstance; + } + + /** + * Setup exception stubbing for a void method. + * @param exps The list of exceptions to throw. + * @param mockInstance The mock object instance. + */ + public Object doThrowWhen(List exps, Object mockInstance) + { + methodReturnValueRecorder.prepareDoThrowWhenExceptions(exps); + return mockInstance; + } + + /** + * Setup answer stubbing for a void method. + * @param answer The answer to invoke. + * @param mockInstance The mock object instance. + */ + public Object doAnswer(fflib_Answer answer, Object mockInstance) + { + this.myAnswer = answer; + return mockInstance; + } + + /** + * Mock a void method. Called by generated mock instance classes, not directly by a developers + * code. + * @param mockInstance The mock object instance. + * @param methodName The method for which to prepare a return value. + * @param methodArgTypes The method argument types for which to prepare a return value. + * @param methodArgValues The method argument values for which to prepare a return value. + */ + public void mockVoidMethod(Object mockInstance, String methodName, List methodArgTypes, List methodArgValues) + { + mockNonVoidMethod(mockInstance, methodName, methodArgTypes, methodArgValues); + } + + /** + * Mock a non-void method. Called by generated mock instance classes, not directly by a developers + * code. + * @param mockInstance The mock object instance. + * @param methodName The method for which to prepare a return value. + * @param methodArgTypes The method argument types for which to prepare a return value. + * @param methodArgValues The method argument values for which to prepare a return value. + */ + public Object mockNonVoidMethod(Object mockInstance, String methodName, List methodArgTypes, List methodArgValues) + { + fflib_QualifiedMethod qm = new fflib_QualifiedMethod(extractTypeName(mockInstance), methodName, methodArgTypes, mockInstance); + fflib_MethodArgValues argValues = new fflib_MethodArgValues(methodArgValues); + + fflib_InvocationOnMock invocation = new fflib_InvocationOnMock(qm, argValues, mockInstance); + + if (this.verifying) + { + verifyMethodCall(invocation); + } + else if (Stubbing) + { + fflib_MethodReturnValue methotReturnValue = prepareMethodReturnValue(invocation); + + if(DoThrowWhenExceptions != null) + { + methotReturnValue.thenThrowMulti(DoThrowWhenExceptions); + DoThrowWhenExceptions = null; + return null; + } + + if(this.myAnswer != null) + { + methotReturnValue.thenAnswer(this.myAnswer); + this.myAnswer = null; + return null; + } + + return null; + } + else + { + recordMethod(invocation); + return returnValue(invocation); + } + + return null; + } + + public class ApexMocksException extends Exception + { + + } + + private Object returnValue(fflib_InvocationOnMock invocation) + { + fflib_MethodReturnValue methodReturnValue = getMethodReturnValue(invocation); + + if (methodReturnValue != null) + { + if(methodReturnValue.Answer == null) + { + throw new fflib_ApexMocks.ApexMocksException( + 'The stubbing is not correct, no return values have been set.'); + } + + Object returnedValue = methodReturnValue.Answer.answer(invocation); + + if(returnedValue == null) + { + return null; + } + + if (returnedValue instanceof Exception) + { + throw ((Exception) returnedValue); + } + + return returnedValue; + } + + return null; + } + + /** + * Sets how many times the method is expected to be called. + * For InOrder verification we copy Mockito behavior which is as follows; + *
    + *
  • Consume the specified number of matching invocations, ignoring non-matching invocations in between
  • + *
  • Fail an assert if the very next invocation matches, but additional matches can still exist so long as at least one non-matching invocation exists before them
  • + *
+ * For example if you had a(); a(); b(); a(); + * then inOrder.verify(myMock, 2)).a(); or inOrder.verify(myMock, 3)).a(); would pass but not inOrder.verify(myMock, 1)).a(); + * @param times The number of times you expect the method to have been called. + * @return The fflib_VerificationMode object instance with the proper settings. + */ + public fflib_VerificationMode times(Integer times) + { + return new fflib_VerificationMode().times(times); + } + + /** + * Sets how many times the method is expected to be called for an InOrder verifier. Available Only with the InOrder verification. + * A verification mode using calls will not fail if the method is called more times than expected. + * @param times The number of times you expect the method to have been called in the InOrder verifying ( no greedy verify). + * @return The fflib_VerificationMode object instance with the proper settings. + */ + public fflib_VerificationMode calls(Integer times) + { + return new fflib_VerificationMode().calls(times); + } + + /** + * Sets a custom assert message for the verify. + * @param customAssertMessage The custom message for the assert in case the assert is false. The custom message is queued to the default message. + * @return The fflib_VerificationMode object instance with the proper settings. + */ + public fflib_VerificationMode description(String customAssertMessage) + { + return new fflib_VerificationMode().description(customAssertMessage); + } + + /** + * Sets the minimum number of times the method is expected to be called. + * With the InOrder verification it performs a greedy verification, which means it would consume all the instances of the method verified. + * @param atLeastTimes The minimum number of times you expect the method to have been called. + * @return The fflib_VerificationMode object instance with the proper settings. + */ + public fflib_VerificationMode atLeast(Integer atLeastTimes) + { + return new fflib_VerificationMode().atLeast(atLeastTimes); + } + + /** + * Sets the maximum number of times the method is expected to be called. Not available in the InOrder verification. + * @param atMostTimes The maximum number of times the method is expected to be called. + * @return The fflib_VerificationMode object instance with the proper settings. + */ + public fflib_VerificationMode atMost(Integer atMostTimes) + { + return new fflib_VerificationMode().atMost(atMostTimes); + } + + /** + * Sets that the method is called at least once. + * With the InOrder verification it performs a greedy verification, which means it would consume all the instances of the method verified. + * @return The fflib_VerificationMode object instance with the proper settings. + */ + public fflib_VerificationMode atLeastOnce() + { + return new fflib_VerificationMode().atLeastOnce(); + } + + /** + * Sets the range of how many times the method is expected to be called. Not available in the InOrder verification. + * @param atLeastTimes The minimum number of times you expect the method to have been called. + * @param atMostTimes The maximum number of times the method is expected to be called. + * @return The fflib_VerificationMode object instance with the proper settings. + */ + public fflib_VerificationMode between(Integer atLeastTimes, Integer atMostTimes) + { + return new fflib_VerificationMode().between(atLeastTimes, atMostTimes); + } + + /** + * Sets that the method is not expected to be called. + * @return The fflib_VerificationMode object instance with the proper settings. + */ + public fflib_VerificationMode never() + { + return new fflib_VerificationMode().never(); + } + + /** + * Sets the fflib_VerificationMode object. + * To internal use only. + * Used to pass the verification mode that has been set in the verify of the fflib_InOrder class. + * @return The fflib_VerificationMode object instance with the proper settings. + */ + public void setOrderedVerifier(fflib_InOrder verifyOrderingMode) + { + this.methodVerifier = verifyOrderingMode; + } + + /** + * A simple override to suppress the default toString logic, which is noisy and rarely useful. + * In some cases, the Salesforce default implementation of toString here can cause internal Salesforce errors + * if it has circular references. + * @return "fflib_ApexMocks" String literal + */ + public override String toString() + { + return 'fflib_ApexMocks'; + } +} diff --git a/sfdx-source/apex-mocks/fflib_ApexMocks.cls-meta.xml b/sfdx-source/apex-mocks/fflib_ApexMocks.cls-meta.xml new file mode 100644 index 00000000000..dd61d1f917e --- /dev/null +++ b/sfdx-source/apex-mocks/fflib_ApexMocks.cls-meta.xml @@ -0,0 +1,5 @@ + + + 52.0 + Active + diff --git a/sfdx-source/apex-mocks/fflib_ApexMocksConfig.cls b/sfdx-source/apex-mocks/fflib_ApexMocksConfig.cls new file mode 100644 index 00000000000..47d4d8a2bb5 --- /dev/null +++ b/sfdx-source/apex-mocks/fflib_ApexMocksConfig.cls @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2017 FinancialForce.com, inc. All rights reserved. + */ +@IsTest +public class fflib_ApexMocksConfig +{ + /** + * When false, stubbed behaviour and invocation counts are shared among all test spies. + * - See fflib_ApexMocksTest.thatMultipleInstancesCanBeMockedDependently + * - This is the default for backwards compatibility. + * When true, each test spy instance has its own stubbed behaviour and invocations. + * - See fflib_ApexMocksTest.thatMultipleInstancesCanBeMockedIndependently + */ + public static Boolean HasIndependentMocks {get; set;} + + static + { + HasIndependentMocks = false; + } +} \ No newline at end of file diff --git a/sfdx-source/apex-mocks/fflib_ApexMocksConfig.cls-meta.xml b/sfdx-source/apex-mocks/fflib_ApexMocksConfig.cls-meta.xml new file mode 100644 index 00000000000..dd61d1f917e --- /dev/null +++ b/sfdx-source/apex-mocks/fflib_ApexMocksConfig.cls-meta.xml @@ -0,0 +1,5 @@ + + + 52.0 + Active + diff --git a/sfdx-source/apex-mocks/fflib_ApexMocksUtils.cls b/sfdx-source/apex-mocks/fflib_ApexMocksUtils.cls new file mode 100644 index 00000000000..f24434b6912 --- /dev/null +++ b/sfdx-source/apex-mocks/fflib_ApexMocksUtils.cls @@ -0,0 +1,250 @@ +/** + * Copyright (c) 2014-2016, FinancialForce.com, inc + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the FinancialForce.com, inc nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +public class fflib_ApexMocksUtils +{ + /** + * This is taken from https://gist.github.com/afawcett/8dbfc0e1d8c43c982881. + * + * This method works on the principle that serializing and deserialising child records is supported + * + * System.assertEquals(1, ((List) + * JSON.deserialize( + * JSON.serialize( + * [select Id, Name, + * (select Id, Name from Children__r) from Master__c]), List.class)) + * [0].Children__r.size()); + * + * This method results internally in constructing this JSON, before deserialising it back into SObject's + * + * [ + * { + * "attributes": { + * "type": "Master__c", + * "url": "/services/data/v32.0/sobjects/Master__c/a0YG0000005Jn5uMAC" + * }, + * "Name": "Fred", + * "Id": "a0YG0000005Jn5uMAC", + * "Children__r": { + * "totalSize": 1, + * "done": true, + * "records": [ + * { + * "attributes": { + * "type": "Child__c", + * "url": "/services/data/v32.0/sobjects/Child__c/a0ZG0000006JGPAMA4" + * }, + * "Name": "Bob", + * "Id": "a0ZG0000006JGPAMA4", + * "Master__c": "a0YG0000005Jn5uMAC" + * } + * ] + * } + * ] + */ + public static Object makeRelationship(Type parentsType, List parents, SObjectField relationshipField, List> children) { + + // Find out more about this relationship... + String relationshipFieldName = relationshipField.getDescribe().getName(); + DescribeSObjectResult parentDescribe = parents.getSObjectType().getDescribe(); + return deserializeParentsAndChildren(parentsType, parentDescribe, relationshipField, parents, children); + } + + /** + * Generic overload to makeRelationship. Enables creation of + * relationships in a loosely-coupled manner. + */ + public static Object makeRelationship( + String parentTypeName, + String childTypeName, + List parents, + String relationshipFieldName, + List> children) { + + // Find out more about this relationship... + SObjectType parentType = getType(parentTypeName); + SObjectField relationshipField = getField(childTypeName, relationshipFieldName); + DescribeSObjectResult parentDescribe = parentType.getDescribe(); + Type parentsType = List.class; + return deserializeParentsAndChildren(parentsType, parentDescribe, relationshipField, parents, children); + } + + /** + * Gives the ability to set test values on formula + * and other read-only fields of mock SObjects + */ + public static Object setReadOnlyFields(SObject objInstance, Type deserializeType, Map properties) { + + Map mergedMap = new Map(objInstance.getPopulatedFieldsAsMap()); + for (SObjectField field : properties.keySet()) { + // Merge the values from the properties map into the fields already set on the object + mergedMap.put(field.getDescribe().getName(), properties.get(field)); + } + // Serialize the merged map, and then deserialize it as the desired object type. + String jsonString = JSON.serializePretty(mergedMap); + return (SObject) JSON.deserialize(jsonString, deserializeType); + } + + /** + * Helper Methods + */ + private static Object deserializeParentsAndChildren( + Type parentsType, + DescribeSObjectResult parentDescribe, + SObjectField relationshipField, + List parents, + List> children + ) { + List childRelationships = parentDescribe.getChildRelationships(); + + String relationshipName = null; + for(Schema.ChildRelationship childRelationship : childRelationships) { + if(childRelationship.getField() == relationshipField) { + relationshipName = childRelationship.getRelationshipName(); + break; + } + } + + // Stream the parsed JSON representation of the parent objects back out, injecting children as it goes + JSONParser parentsParser = JSON.createParser(JSON.serialize(parents)); + JSONParser childrenParser = JSON.createParser(JSON.serialize(children)); + JSONGenerator combinedOutput = JSON.createGenerator(false); + streamTokens(parentsParser, combinedOutput, new InjectChildrenEventHandler(childrenParser, relationshipName, children) ); + + // Derserialise back into SObject list complete with children + return JSON.deserialize(combinedOutput.getAsString(), parentsType); + } + + /** + * Monitors stream events for end of object for each SObject contained in the parent list + * then injects the respective childs record list into the stream + */ + private class InjectChildrenEventHandler implements JSONParserEvents + { + private JSONParser childrenParser; + private String relationshipName; + private List> children; + private Integer childListIdx = 0; + + public InjectChildrenEventHandler(JSONParser childrenParser, String relationshipName, List> children) { + this.childrenParser = childrenParser; + this.relationshipName = relationshipName; + this.children = children; + this.childrenParser.nextToken(); // Consume the outer array token + } + + public void nextToken(JSONParser fromStream, Integer depth, JSONGenerator toStream) { + // Inject children? + JSONToken currentToken = fromStream.getCurrentToken(); + if(depth == 2 && currentToken == JSONToken.END_OBJECT ) { + toStream.writeFieldName(relationshipName); + toStream.writeStartObject(); + toStream.writeNumberField('totalSize', children[childListIdx].size()); + toStream.writeBooleanField('done', true); + toStream.writeFieldName('records'); + streamTokens(childrenParser, toStream, null); + toStream.writeEndObject(); + childListIdx++; + } + } + } + + /** + * Utility function to stream tokens from a reader to a write, while providing a basic eventing model + */ + private static void streamTokens(JSONParser fromStream, JSONGenerator toStream, JSONParserEvents events) + { + Integer depth = 0; + while (fromStream.nextToken()!=null) + { + // Give event handler chance to inject + if(events!=null) + events.nextToken(fromStream, depth, toStream); + // Forward to output stream + JSONToken currentToken = fromStream.getCurrentToken(); + if(currentToken == JSONToken.START_ARRAY) { + toStream.writeStartArray(); + depth++; + } + else if(currentToken == JSONToken.START_OBJECT) { + toStream.writeStartObject(); + depth++; + } + else if(currentToken == JSONToken.FIELD_NAME) + toStream.writeFieldName(fromStream.getCurrentName()); + else if(currentToken == JSONToken.VALUE_STRING || + currentToken == JSONToken.VALUE_FALSE || + currentToken == JSONToken.VALUE_TRUE || + currentToken == JSONToken.VALUE_NUMBER_FLOAT || + currentToken == JSONToken.VALUE_NUMBER_INT) + toStream.writeString(fromStream.getText()); + else if(currentToken == JSONToken.END_OBJECT) { + toStream.writeEndObject(); + depth--; + } + else if(currentToken == JSONToken.END_ARRAY) { + toStream.writeEndArray(); + depth--; + } + // Don't continue to stream beyond the initial starting point + if(depth==0) + break; + } + } + + /** + * Basic event used during the above streaming + */ + private interface JSONParserEvents + { + void nextToken(JSONParser fromStream, Integer depth, JSONGenerator toStream); + } + + /** + * Gets the SObjectType by name + */ + private static Schema.SObjectType getType(String typeName) { + Map gd = Schema.getGlobalDescribe(); + SObjectType sobjType = gd.get(typeName); + if (sobjType == null) { + throw new fflib_ApexMocks.ApexMocksException('SObject type not found: ' + typeName); + } + return sobjType; + } + + /** + * Gets the SObjectField of an object by name + */ + private static Schema.SObjectField getField(String objectName, String fieldName) { + + SObjectType sobjType = getType(objectName); + Map objectFields = sobjType.getDescribe().fields.getMap(); + Schema.SObjectField sobjField = objectFields.get(fieldName); + if (sobjField == null) { + throw new fflib_ApexMocks.ApexMocksException('SObject field not found: ' + fieldName); + } + return sobjField; + } +} \ No newline at end of file diff --git a/sfdx-source/apex-mocks/fflib_ApexMocksUtils.cls-meta.xml b/sfdx-source/apex-mocks/fflib_ApexMocksUtils.cls-meta.xml new file mode 100644 index 00000000000..08c4dbd5cfa --- /dev/null +++ b/sfdx-source/apex-mocks/fflib_ApexMocksUtils.cls-meta.xml @@ -0,0 +1,4 @@ + + + 52.0 + diff --git a/sfdx-source/apex-mocks/fflib_ArgumentCaptor.cls b/sfdx-source/apex-mocks/fflib_ArgumentCaptor.cls new file mode 100644 index 00000000000..cfd5bd304fd --- /dev/null +++ b/sfdx-source/apex-mocks/fflib_ArgumentCaptor.cls @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2016, FinancialForce.com, inc + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the FinancialForce.com, inc nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * This class implements the capturing framework for ApexMocks + * According to Mockito's syntax the type is passed in the capturer construction, + * however Apex cannot perform the auto casting that Java can. + * To be consistent with Mockito, the capturer does not perform any checks on the type of the argument. + * @group Core + */ +public with sharing class fflib_ArgumentCaptor +{ + protected List argumentsCaptured = new List(); + + /** + * Factory method to create a new fflib_ArgumentCaptor. + * Takes the captured argument's Type for consistency with Mockito syntax. + * The Type is IGNORED because we can't determine an object instance's Type at runtime unlike in Java. + * Rigorous type checking may be introduced in a future release, so you should specify the expected argument type correctly. + * + * @param ignoredCaptureType Type (class) of the captured argument + * @return A new fflib_ArgumentCaptor. + */ + public static fflib_ArgumentCaptor forClass(Type ignoredCaptureType) + { + return new fflib_ArgumentCaptor(); + } + + /** + * Use it to capture the argument. This method must be used inside verification. + * Internally, this method registers a special implementation of a Matcher. + * This argument matcher stores the argument value so that you can use it later to perform assertions. + * + * @return a special matcher that matches any argument and remembers the value. + */ + public Object capture() + { + AnyObject myMatcher = new AnyObject(this); + + return fflib_Match.matches(myMatcher); + } + + /** + * Returns the captured value of the argument. When capturing all arguments use getAllValues(). + * If verified method was called multiple times then this method returns the latest captured value. + * + * @return captured argument value. + */ + public Object getValue() + { + if( argumentsCaptured == null || + argumentsCaptured.size() == 0) + { + return null; + } + + //returns the last argument called + return argumentsCaptured.get( argumentsCaptured.size() - 1 ); + } + + /** + * Returns all captured values. Use it when capturing multiple arguments or when the verified method was called multiple times. + * When capturing multiple arguments is called multiple times, this method returns a merged list of all values from all invocations. + * + * @return Returns all captured values. Use it when capturing multiple arguments on the same call or when the verified method was called multiple times. + */ + public List getAllValues() + { + return argumentsCaptured; + } + + public class AnyObject implements fflib_IMatcher + { + private fflib_ArgumentCaptor captor; + private Object value; + + public AnyObject(fflib_ArgumentCaptor captor) + { + this.captor = captor; + } + + //match with all the possible values and store the arg value + public Boolean matches(Object arg) + { + value = arg; + return true; + } + + //store the argument in the list ( this would be called inside the method counter where is compared with the matchers of the method) + public void storeArgument() + { + captor.argumentsCaptured.add(value); + } + } +} \ No newline at end of file diff --git a/sfdx-source/apex-mocks/fflib_ArgumentCaptor.cls-meta.xml b/sfdx-source/apex-mocks/fflib_ArgumentCaptor.cls-meta.xml new file mode 100644 index 00000000000..dd61d1f917e --- /dev/null +++ b/sfdx-source/apex-mocks/fflib_ArgumentCaptor.cls-meta.xml @@ -0,0 +1,5 @@ + + + 52.0 + Active + diff --git a/sfdx-source/apex-mocks/fflib_IDGenerator.cls b/sfdx-source/apex-mocks/fflib_IDGenerator.cls new file mode 100644 index 00000000000..ddeb27c9dfa --- /dev/null +++ b/sfdx-source/apex-mocks/fflib_IDGenerator.cls @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2014, FinancialForce.com, inc + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the FinancialForce.com, inc nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +public with sharing class fflib_IDGenerator +{ + private static Integer fakeIdCount = 0; + private static final String ID_PATTERN = '000000000000'; + + /** + * Generate a fake Salesforce Id for the given SObjectType + */ + public static Id generate(Schema.SObjectType sobjectType) + { + String keyPrefix = sobjectType.getDescribe().getKeyPrefix(); + fakeIdCount++; + + String fakeIdPrefix = ID_PATTERN.substring(0, ID_PATTERN.length() - String.valueOf(fakeIdCount).length()); + + return Id.valueOf(keyPrefix + fakeIdPrefix + fakeIdCount); + } +} \ No newline at end of file diff --git a/sfdx-source/apex-mocks/fflib_IDGenerator.cls-meta.xml b/sfdx-source/apex-mocks/fflib_IDGenerator.cls-meta.xml new file mode 100644 index 00000000000..08c4dbd5cfa --- /dev/null +++ b/sfdx-source/apex-mocks/fflib_IDGenerator.cls-meta.xml @@ -0,0 +1,4 @@ + + + 52.0 + diff --git a/sfdx-source/apex-mocks/fflib_IMatcher.cls b/sfdx-source/apex-mocks/fflib_IMatcher.cls new file mode 100644 index 00000000000..d8b9729a36d --- /dev/null +++ b/sfdx-source/apex-mocks/fflib_IMatcher.cls @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2014-2016, FinancialForce.com, inc + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the FinancialForce.com, inc nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +public interface fflib_IMatcher +{ + /** + * Whether or not the supplied argument is a match. + * Any supplementary information (e.g. boundary conditions, objects to match to etc) + * should be cached by the matcher constructor. + * @param arg The argument value supplied to the method + * @return Boolean True if the argument value is a match, false otherwise. + */ + Boolean matches(Object arg); +} \ No newline at end of file diff --git a/sfdx-source/apex-mocks/fflib_IMatcher.cls-meta.xml b/sfdx-source/apex-mocks/fflib_IMatcher.cls-meta.xml new file mode 100644 index 00000000000..08c4dbd5cfa --- /dev/null +++ b/sfdx-source/apex-mocks/fflib_IMatcher.cls-meta.xml @@ -0,0 +1,4 @@ + + + 52.0 + diff --git a/sfdx-source/apex-mocks/fflib_InOrder.cls b/sfdx-source/apex-mocks/fflib_InOrder.cls new file mode 100644 index 00000000000..39d0bf59ad2 --- /dev/null +++ b/sfdx-source/apex-mocks/fflib_InOrder.cls @@ -0,0 +1,356 @@ +/* + Copyright (c) 2017 FinancialForce.com, inc. All rights reserved. + */ + + +/** + * @group Core + */ +public with sharing class fflib_InOrder extends fflib_MethodVerifier +{ + private final List unorderedMockInstances; + private Integer idxMethodCall = 0; + + private Set notImplementedMethods = + new Set + { + fflib_VerificationMode.ModeName.atMost, + fflib_VerificationMode.ModeName.between + }; + + private final fflib_ApexMocks mocks; + + /** + * Construct the InOrder instance. + * @param mocks The apex mock object instance. + * @param unorderedMockInstances One or more mock implementation classes (listed in any order), whose ordered method calls require verification. + */ + public fflib_InOrder(fflib_ApexMocks mocks, List unorderedMockInstances) + { + this.unorderedMockInstances = unorderedMockInstances; + this.mocks = mocks; + } + + /** + * Verify a method was called on a mock object. + * It performs a no strict ordered verification. + * The verification could be either greedy or not depending of the verificationMode passed. + * Check the fflib_VerificationMode methods for details. + * @param mockInstance The mock object instance. + * @param verificationMode Defines the constraints for performing the verification (e.g. the minimum and maximum expected invocation counts). + * @return The mock object instance. + */ + public Object verify(Object mockInstance, fflib_VerificationMode verificationMode) + { + mocks.setOrderedVerifier(this); + return mocks.verify(mockInstance, verificationMode); + } + + /** + * Verify a method was called on a mock object. + * It performs the default times(1) verification for the InOrder. + * @param mockInstance The mock object instance. + * @return The mock object instance. + */ + public Object verify(Object mockInstance) + { + mocks.setOrderedVerifier(this); + return mocks.verify(mockInstance); + } + + /** + * Verify a method was called on a mock object. + * Wrapper for the new syntax call to be conformed to the old style notation + * It performs the equivalent of times(times) verification for the InOrder. + * @param mockInstance The mock object instance. + * @param times The number of times you expect the method to have been called. + * @return The mock object instance. + */ + public Object verify(Object mockInstance, Integer times) + { + mocks.setOrderedVerifier(this); + return mocks.verify(mockInstance, times); + } + + /** + * Verify that after the last successful verified method no more interactions happened on the inOrderMock instance. + * @throws Exception with message to help to identify the last method called. + */ + public void verifyNoMoreInteractions() + { + if(idxMethodCall == 0) + { + verifyNoInteractions(); + } + + if(hasNextInteraction(unorderedMockInstances, idxMethodCall)) + { + fflib_InvocationOnMock invocation = + fflib_MethodCountRecorder.getOrderedMethodCalls().get(idxMethodCall -1); + + throw new fflib_ApexMocks.ApexMocksException( + 'No more Interactions were expected after the ' + invocation.getMethod() +' method.'); + } + } + + /** + * Verify that no interactions at all happened on the inOrderMock instance. + * @throws Exception with message. + */ + public void verifyNoInteractions() + { + if(hasNextInteraction(unorderedMockInstances, 0)) + { + throw new fflib_ApexMocks.ApexMocksException( + 'No Interactions expected on this InOrder Mock instance!'); + } + } + + /* + * Verifies a method was invoked the expected number of times, with the expected arguments. + * The in-order verifier remembers the last method invocation it successfully verified, + * and only considers subsequent method invocations for subsequent verifications. + * @param qualifiedMethod The method to be verified. + * @param expectedArguments The arguments of the method that needs to be verified. + * @param verificationMode The verification mode that holds the setting about how the verification should be performed. + */ + protected override void verify( + fflib_QualifiedMethod qm, + fflib_MethodArgValues expectedArguments, + fflib_VerificationMode verificationMode) + { + String inOrder = ' in order'; + List matchers = fflib_Match.Matching ? fflib_Match.getAndClearMatchers(expectedArguments.argValues.size()) : null; + List actualInvocations = fflib_MethodCountRecorder.getOrderedMethodCalls(); + List actualArguments = new List(); + for (fflib_InvocationOnMock invocation : actualInvocations) + { + actualArguments.add(invocation.getMethodArgValues()); + } + + if( verificationMode.VerifyMin == 0 && verificationMode.VerifyMax == 0) + { + Integer methodCounts = countInteractions(matchers, qm, expectedArguments); + if(methodCounts != 0 ) + throwException(qm, inOrder, fflib_ApexMocks.NEVER, '', methodCounts, verificationMode.CustomAssertMessage, expectedArguments, matchers, actualArguments); + } + + Integer i=0; + for ( ; i matchers, + fflib_QualifiedMethod qm, + fflib_MethodArgValues methodArg) + { + fflib_InvocationOnMock calledMethod = getNextMethodCall(); + while(calledMethod != null) + { + if(calledMethod.getMethod() == qm && + argumentsMatch(calledMethod.getMethodArgValues(), matchers, methodArg)) + { + //it's our method + if (matchers != null) + { + capture(matchers); + } + return true; + } + + calledMethod = getNextMethodCall(); + } + + return false; + } + + private Integer countInteractions( + List matchers, + fflib_QualifiedMethod qualifiedMethod, + fflib_MethodArgValues methodArg) + { + Integer interactionsCouter = 0; + + for (Integer i = idxMethodCall, len = fflib_MethodCountRecorder.getOrderedMethodCalls().size(); i matchers, + fflib_QualifiedMethod qualifiedMethod, + fflib_MethodArgValues methodArg) + { + Integer lastInteracionIndex = 0; + + //going all through the orderedMethodCalls to find all the interaction of the method + for (Integer i = idxMethodCall, len = fflib_MethodCountRecorder.getOrderedMethodCalls().size(); i matchers, + fflib_MethodArgValues methodArg) + { + //Check it was called with the right args. + if (matchers != null) + { + if(fflib_Match.matchesAllArgs(calledMethodArg, matchers)) + { + //Return now we've matched the method call + return true; + } + } + else if(calledMethodArg == methodArg) + { + //Return now we've matched the method call + return true; + } + + return false; + } + + private fflib_InvocationOnMock getNextMethodCall() + { + return getNextMethodCall(true); + } + + private fflib_InvocationOnMock getNextMethodCall(Boolean updateIdxMethodCall) + { + Integer idx = 0; + for (fflib_InvocationOnMock invocation : fflib_MethodCountRecorder.getOrderedMethodCalls()) + { + if (idx == idxMethodCall) + { + if(isForMockInstance(invocation)) + { + if(updateIdxMethodCall) + idxMethodCall++; + return invocation; + } + } + else + { + idx++; + } + } + + return null; + } + + private Boolean isForMockInstance(fflib_InvocationOnMock invocation) + { + for (Object mi : unorderedMockInstances) + { + if (mi === invocation.getMock()) + { + return true; + } + } + + return false; + } + + /* + * Used by the fflib_InOrder invocation verifier to find further interactions with a given mock instances. + * @param mockInstances The tracked mock instances - only methods called on these objects are counted as an invocation. + * @param idxLastMethodCalled The index of the last matched method, used to offset the search for invocations so we don't double count invocations. + * @return Whether or not there were further interactions. + */ + private Boolean hasNextInteraction(List mockInstances, Integer idxLastMethodCalled) + { + Integer idx = 0; + + for (fflib_InvocationOnMock methodCall : fflib_MethodCountRecorder.getOrderedMethodCalls()) + { + if (isForMockInstance(methodCall)) + { + idx++; + if (idx > idxLastMethodCalled) + { + return true; + } + } + } + + return false; + } + + /* + * Method that validate the verification mode used in the verify. + * Not all the methods from the fflib_VerificationMode are implemented for the different classes that extends the fflib_MethodVerifier. + * The error is thrown at run time, so this method is called in the method that actually performs the verify. + * @param verificationMode The verification mode that have to been verified. + * @throws Exception with message for the fflib_VerificationMode not implemented. + */ + protected override void validateMode(fflib_VerificationMode verificationMode) + { + if(notImplementedMethods.contains(verificationMode.Method)) + { + throw new fflib_ApexMocks.ApexMocksException( + 'The ' + verificationMode.Method.name() + ' method is not implemented for the fflib_InOrder class'); + } + } +} \ No newline at end of file diff --git a/sfdx-source/apex-mocks/fflib_InOrder.cls-meta.xml b/sfdx-source/apex-mocks/fflib_InOrder.cls-meta.xml new file mode 100644 index 00000000000..dd61d1f917e --- /dev/null +++ b/sfdx-source/apex-mocks/fflib_InOrder.cls-meta.xml @@ -0,0 +1,5 @@ + + + 52.0 + Active + diff --git a/sfdx-source/apex-mocks/fflib_Inheritor.cls b/sfdx-source/apex-mocks/fflib_Inheritor.cls new file mode 100644 index 00000000000..d718ecf67fa --- /dev/null +++ b/sfdx-source/apex-mocks/fflib_Inheritor.cls @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2016-2017 FinancialForce.com, inc. All rights reserved. + */ +@isTest +public class fflib_Inheritor implements IA, IB, IC +{ + public interface IA {String doA();} + public interface IB {String doB();} + public interface IC {String doC();} + + public String doA(){return 'Did A';} + public String doB(){return 'Did B';} + public String doC(){return 'Did C';} +} \ No newline at end of file diff --git a/sfdx-source/apex-mocks/fflib_Inheritor.cls-meta.xml b/sfdx-source/apex-mocks/fflib_Inheritor.cls-meta.xml new file mode 100644 index 00000000000..f539c3a316c --- /dev/null +++ b/sfdx-source/apex-mocks/fflib_Inheritor.cls-meta.xml @@ -0,0 +1,4 @@ + + + 52.0 + diff --git a/sfdx-source/apex-mocks/fflib_InvocationOnMock.cls b/sfdx-source/apex-mocks/fflib_InvocationOnMock.cls new file mode 100644 index 00000000000..9e13914fafc --- /dev/null +++ b/sfdx-source/apex-mocks/fflib_InvocationOnMock.cls @@ -0,0 +1,84 @@ +/* + Copyright (c) 2017 FinancialForce.com, inc. All rights reserved. + */ + +/** + * An invocation on a mock. + * A place holder for mock, the method that was called and the arguments that were passed. + * @group Core + */ +public with sharing class fflib_InvocationOnMock +{ + private fflib_QualifiedMethod qm; + private fflib_MethodArgValues methodArg; + private Object mockInstance; + + /** + * Constructor for the class. + * @param qm The fflib_QualifiedMethod instance to be stored. + * @param args The fflib_MethodArgValues instance to be stored. + * @param mockInstance The mock instance to be stored. + */ + public fflib_InvocationOnMock(fflib_QualifiedMethod qm, fflib_MethodArgValues args, Object mockInstance) + { + this.qm = qm; + this.methodArg = args; + this.mockInstance = mockInstance; + } + + /** + * Returns the argument at the given index. + * @param index The index of the wanted argument. + * @throws ApexMocksException in case the index is out of range. + * @return The argument at the given index. + */ + public Object getArgument(Integer index) + { + validateIndex(index); + return methodArg.argValues[index]; + } + + /** + * Returns the list of arguments passed to the method. + * @return The list of arguments. + */ + public List getArguments() + { + return methodArg.argValues; + } + + /** + * Returns fflib_MethodArgValues instance that represents the arguments passed to the method. + * @return The fflib_MethodArgValues instance that represents the arguments passed to the method. + */ + public fflib_MethodArgValues getMethodArgValues() + { + return methodArg; + } + + /** + * Returns the fflib_QualifiedMethod instance that represent the fully qualified method called within the invocation. + * @return The method stored in the invocation. + */ + public fflib_QualifiedMethod getMethod() + { + return qm; + } + + /** + * Returns the mock object on which the invocation occurs. + * @return The mock object on which the invocation occurs. + */ + public Object getMock() + { + return mockInstance; + } + + private void validateIndex(Integer index) + { + if(index < 0 || index >= methodArg.argValues.size()) + { + throw new fflib_ApexMocks.ApexMocksException('Invalid index, must be greater or equal to zero and less of ' + methodArg.argValues.size()+'.'); + } + } +} \ No newline at end of file diff --git a/sfdx-source/apex-mocks/fflib_InvocationOnMock.cls-meta.xml b/sfdx-source/apex-mocks/fflib_InvocationOnMock.cls-meta.xml new file mode 100644 index 00000000000..dd61d1f917e --- /dev/null +++ b/sfdx-source/apex-mocks/fflib_InvocationOnMock.cls-meta.xml @@ -0,0 +1,5 @@ + + + 52.0 + Active + diff --git a/sfdx-source/apex-mocks/fflib_Match.cls b/sfdx-source/apex-mocks/fflib_Match.cls new file mode 100644 index 00000000000..30a09202d3f --- /dev/null +++ b/sfdx-source/apex-mocks/fflib_Match.cls @@ -0,0 +1,1158 @@ +/** + * Copyright (c) 2014, FinancialForce.com, inc + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the FinancialForce.com, inc nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +public class fflib_Match +{ + private static List matchers = new List(); + + /** + * Matching + * True when comparing method arg values to matchers, false when comparing absolute arg values. + */ + public static Boolean Matching = false; + + /** + * Used internally by the mocking framework, you shouldn't need to call this method directly. + * Copies the registered matchers, and then switches matching mode off. + * @param expectedSize The expected number of matchers to be returned. If this does not match the actual value an expection is thrown. + * @return List The registered matchers, collected while in matching mode. + */ + public static List getAndClearMatchers(Integer expectedSize) + { + Matching = false; + + List retval = matchers.clone(); + matchers.clear(); + + if (retval.size() != expectedSize) + { + throw new fflib_ApexMocks.ApexMocksException('The number of matchers defined (' + retval.size() + ').' + + ' does not match the number expected (' + expectedSize + ')\n' + + 'If you are using matchers all arguments must be passed in as matchers.\n' + + 'For example myList.add(fflib_Match.anyInteger(), \'String\') should be defined as myList.add(fflib_Match.anyInteger(), fflib_Match.eq(\'String\')).'); + } + + return retval; + } + + /** + * Used internally by the mocking framework, you shouldn't need to call this method directly. + * Compares all supplied method arg values to the supplied target matchers. + * @param methodArg The arguments supplied when the method was called + * @param targetMatchers The matchers the arguments need to be compared with + * @throws fflib_ApexMocks.ApexMocksException Thrown when methodArgValues is null/empty, targetMatchers is null, or their sizes don't match + * @return Boolean True if all arg values satisfy all of the supplied matchers. + */ + public static Boolean matchesAllArgs(fflib_MethodArgValues methodArg, List targetMatchers) + { + validateArgs(methodArg, targetMatchers); + + Integer matchersSize = targetMatchers.size(); + for (Integer i=0; i targetMatchers) + { + if (methodArg == null) + { + throw new fflib_ApexMocks.ApexMocksException('MethodArgs cannot be null'); + } + + if (methodArg.argValues == null) + { + throw new fflib_ApexMocks.ApexMocksException('MethodArgs.argValues cannot be null'); + } + + if (targetMatchers == null) + { + throw new fflib_ApexMocks.ApexMocksException('Matchers cannot be null'); + } + + if (targetMatchers.size() != methodArg.argValues.size()) + { + throw new fflib_ApexMocks.ApexMocksException('MethodArgs and matchers must have the same count' + + ', MethodArgs: (' + methodArg.argValues.size() + ') ' + methodArg.argValues + + ', Matchers: (' + targetMatchers.size() + ') ' + targetMatchers); + } + } + + /** + * Registers a matcher which will be stubbed/verified against. + * @param matcher The matcher that needs to be compared + * @return Object Always returns null. This can then be cast into the correct arg type + * so that the right method is called on the mock objects. + */ + public static Object matches(fflib_IMatcher matcher) + { + Matching = true; + matchers.add(matcher); + + return null; + } + + /** + * COMBINED MATCHER + * The allOf, anyOf and noneOf methods are overloaded to provide fluent matcher calls for up to 4 matcher conditions. + * To connect 5 or more, the List version directly. + */ + + /** + * Registers a matcher which will check if the method is called with an arg that matches allOf + * @param o1 A dummy object returned by registering another matcher + * @param o2 A dummy object returned by registering another matcher + * @return Object A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. (You may need to cast down to your specific object type)public static Object allOf(Object o1, Object o2) + */ + public static Object allOf(Object o1, Object o2) + { + return allOf(new Object[]{ o1, o2 }); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches allOf + * @param o1 A dummy object returned by registering another matcher + * @param o2 A dummy object returned by registering another matcher + * @param o3 A dummy object returned by registering another matcher + * @return Object A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. (You may need to cast down to your specific object type) + */ + public static Object allOf(Object o1, Object o2, Object o3) + { + return allOf(new Object[]{ o1, o2, o3 }); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches allOf + * @param o1 A dummy object returned by registering another matcher + * @param o2 A dummy object returned by registering another matcher + * @param o3 A dummy object returned by registering another matcher + * @param o4 A dummy object returned by registering another matcher + * @return Object A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. (You may need to cast down to your specific object type) + */ + public static Object allOf(Object o1, Object o2, Object o3, Object o4) + { + return allOf(new Object[]{ o1, o2, o3, o4 }); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches allOf + * @param o A list of dummy objects returned by registering other matchers + * @return Object A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. (You may need to cast down to your specific object type) + */ + public static Object allOf(List o) + { + return combined(fflib_MatcherDefinitions.Connective.ALL, o); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches anyOf + * @param o1 A dummy object returned by registering another matcher + * @param o2 A dummy object returned by registering another matcher + * @return Object A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. (You may need to cast down to your specific object type) + */ + public static Object anyOf(Object o1, Object o2) + { + return anyOf(new Object[]{ o1, o2 }); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches anyOf + * @param o1 A dummy object returned by registering another matcher + * @param o2 A dummy object returned by registering another matcher + * @param o3 A dummy object returned by registering another matcher + * @return Object A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. (You may need to cast down to your specific object type) + */ + public static Object anyOf(Object o1, Object o2, Object o3) + { + return anyOf(new Object[]{ o1, o2, o3 }); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches anyOf + * @param o1 A dummy object returned by registering another matcher + * @param o2 A dummy object returned by registering another matcher + * @param o3 A dummy object returned by registering another matcher + * @param o4 A dummy object returned by registering another matcher + * @return Object A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. (You may need to cast down to your specific object type) + */ + public static Object anyOf(Object o1, Object o2, Object o3, Object o4) + { + return anyOf(new Object[]{ o1, o2, o3, o4 }); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches anyOf + * @param o A list of dummy objects returned by registering other matchers + * @return Object A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. (You may need to cast down to your specific object type) + */ + public static Object anyOf(List o) + { + return combined(fflib_MatcherDefinitions.Connective.AT_LEAST_ONE, o); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches isNot + * @param o1 A dummy object returned by registering another matcher + * @return Object A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. (You may need to cast down to your specific object type) + */ + public static Object isNot(Object o1) + { + return noneOf(new Object[]{ o1 }); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches noneOf + * @param o1 A dummy object returned by registering another matcher + * @param o2 A dummy object returned by registering another matcher + * @return Object A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. (You may need to cast down to your specific object type) + */ + public static Object noneOf(Object o1, Object o2) + { + return noneOf(new Object[]{ o1, o2 }); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches noneOf + * @param o1 A dummy object returned by registering another matcher + * @param o2 A dummy object returned by registering another matcher + * @param o3 A dummy object returned by registering another matcher + * @return Object A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. (You may need to cast down to your specific object type) + */ + public static Object noneOf(Object o1, Object o2, Object o3) + { + return noneOf(new Object[]{ o1, o2, o3 }); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches noneOf + * @param o1 A dummy object returned by registering another matcher + * @param o2 A dummy object returned by registering another matcher + * @param o3 A dummy object returned by registering another matcher + * @param o4 A dummy object returned by registering another matcher + * @return Object A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. (You may need to cast down to your specific object type) + */ + public static Object noneOf(Object o1, Object o2, Object o3, Object o4) + { + return noneOf(new Object[]{ o1, o2, o3, o4 }); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches noneOf + * @param o A list of dummy objects returned by registering other matchers + * @return Object A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. (You may need to cast down to your specific object type) + */ + public static Object noneOf(List o) + { + return combined(fflib_MatcherDefinitions.Connective.NONE, o); + } + + private static Object combined(fflib_MatcherDefinitions.Connective connectiveExpression, List o) + { + return matches(new fflib_MatcherDefinitions.Combined(connectiveExpression, (gatherMatchers(o)))); + } + + private static List gatherMatchers(Object[] ignoredMatcherObjects) + { + if (ignoredMatcherObjects == null || ignoredMatcherObjects.isEmpty()) + { + throw new fflib_ApexMocks.ApexMocksException('Must register matchers to combine'); + } + + //Each ignored matcher object represents a matcher that has been registered against fflib_Match.matchers, + //but is actually for the connective matchers. + List innerMatchers = new List(); + + Integer innerMatcherCount = ignoredMatcherObjects.size(); + while (innerMatchers.size() < innerMatcherCount) + { + if (matchers.isEmpty()) + { + throw new fflib_ApexMocks.ApexMocksException('Error reclaiming inner matchers for combined matcher. Wanted ' + + innerMatcherCount + ' matchers but only got ' + innerMatchers); + } + + fflib_IMatcher innerMatcher = matchers.remove(matchers.size()-1); + + //Add to the start of the list to preserve the order in which matchers were declared. + //Note. Apex throws list index out of bounds if inserting an element into an empty list at index 0S + if (!innerMatchers.isEmpty()) + { + innerMatchers.add(0, innerMatcher); + } + else + { + innerMatchers.add(innerMatcher); + } + } + + return innerMatchers; + } + + /** + * ALL OTHER MATCHER METHODS + */ + + /** + * Registers a matcher which will check if the method is called with an arg that matches eq + * @param toMatch The Object to be compared + * @return Object A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. (You may need to cast down to your specific object type) + */ + public static Object eq(Object toMatch) + { + return matches(new fflib_MatcherDefinitions.Eq(toMatch)); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches eqBoolean + * @param toMatch The Boolean to be compared + * @return Boolean A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static Boolean eqBoolean(Boolean toMatch) + { + return (Boolean)matches(new fflib_MatcherDefinitions.Eq(toMatch)); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches eqDate + * @param toMatch The Date to be compared + * @return Date A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static Date eqDate(Date toMatch) + { + return (Date)matches(new fflib_MatcherDefinitions.Eq(toMatch)); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches eqDatetime + * @param toMatch The Datetime to be compared + * @return Datetime A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static Datetime eqDatetime(Datetime toMatch) + { + return (Datetime)matches(new fflib_MatcherDefinitions.Eq(toMatch)); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches eqDecimal + * @param toMatch The Decimal to be compared + * @return Decimal A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static Decimal eqDecimal(Decimal toMatch) + { + return (Decimal)matches(new fflib_MatcherDefinitions.Eq(toMatch)); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches eqDouble + * @param toMatch The Double to be compared + * @return Double A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static Double eqDouble(Double toMatch) + { + return (Double)matches(new fflib_MatcherDefinitions.Eq(toMatch)); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches eqId + * @param toMatch The Id to be compared + * @return Id A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static Id eqId(Id toMatch) + { + return (Id)matches(new fflib_MatcherDefinitions.Eq(toMatch)); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches eqInteger + * @param toMatch The Integer to be compared + * @return Integer A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static Integer eqInteger(Integer toMatch) + { + return (Integer)matches(new fflib_MatcherDefinitions.Eq(toMatch)); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches eqList + * @param toMatch The List to be compared + * @return List A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static List eqList(List toMatch) + { + return (List)matches(new fflib_MatcherDefinitions.Eq(toMatch)); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches eqLong + * @param toMatch The Long to be compared + * @return Long A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static Long eqLong(Long toMatch) + { + return (Long)matches(new fflib_MatcherDefinitions.Eq(toMatch)); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches eqSObjectField + * @param toMatch The SObjectField to be compared + * @return SObjectField A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static SObjectField eqSObjectField(SObjectField toMatch) + { + return (SObjectField)matches(new fflib_MatcherDefinitions.Eq(toMatch)); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches eqSObjectType + * @param toMatch The SObjectType to be compared + * @return SObjectType A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static SObjectType eqSObjectType(SObjectType toMatch) + { + return (SObjectType)matches(new fflib_MatcherDefinitions.Eq(toMatch)); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches eqString + * @param toMatch The String to be compared + * @return String A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static String eqString(String toMatch) + { + return (String)matches(new fflib_MatcherDefinitions.Eq(toMatch)); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches refEq + * @param toMatch The Object to be compared + * @return Object A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. (You may need to cast down to your specific object type) + */ + public static Object refEq(Object toMatch) + { + return matches(new fflib_MatcherDefinitions.RefEq(toMatch)); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches anyBoolean + * @return Boolean A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static Boolean anyBoolean() + { + return (Boolean)matches(new fflib_MatcherDefinitions.AnyBoolean()); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches anyDate + * @return Date A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static Date anyDate() + { + return (Date)matches(new fflib_MatcherDefinitions.AnyDate()); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches anyDatetime + * @return Datetime A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static Datetime anyDatetime() + { + return (Datetime)matches(new fflib_MatcherDefinitions.AnyDatetime()); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches anyDecimal + * @return Decimal A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static Decimal anyDecimal() + { + return (Decimal)matches(new fflib_MatcherDefinitions.AnyDecimal()); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches anyDouble + * @return Double A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static Double anyDouble() + { + return (Double)matches(new fflib_MatcherDefinitions.AnyDouble()); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches anyFieldSet + * @return Schema.FieldSet A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static Schema.FieldSet anyFieldSet() + { + return (Schema.FieldSet)matches(new fflib_MatcherDefinitions.AnyFieldSet()); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches anyId + * @return Id A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static Id anyId() + { + return (Id)matches(new fflib_MatcherDefinitions.AnyId()); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches anyInteger + * @return Integer A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static Integer anyInteger() + { + return (Integer)matches(new fflib_MatcherDefinitions.AnyInteger()); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches anyList + * @return List A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static List anyList() + { + return (List)matches(new fflib_MatcherDefinitions.AnyList()); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches anyLong + * @return Long A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static Long anyLong() + { + return (Long)matches(new fflib_MatcherDefinitions.AnyLong()); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches anyObject + * @return Object A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. (You may need to cast down to your specific object type) + */ + public static Object anyObject() + { + return matches(new fflib_MatcherDefinitions.AnyObject()); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches anyString + * @return String A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static String anyString() + { + return (String)matches(new fflib_MatcherDefinitions.AnyString()); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches anySObject + * @return SObject A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static SObject anySObject() + { + return (SObject)matches(new fflib_MatcherDefinitions.AnySObject()); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches anySObjectField + * @return SObjectField A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static SObjectField anySObjectField() + { + return (SObjectField)matches(new fflib_MatcherDefinitions.AnySObjectField()); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches anySObjectType + * @return SObjectType A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static SObjectType anySObjectType() + { + return (SObjectType)matches(new fflib_MatcherDefinitions.AnySObjectType()); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches dateAfter (not inclusive) + * @param fromDate The Date to be compared + * @return Date A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static Date dateAfter(Date fromDate) + { + return dateAfter(fromDate, false); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches dateAfter + * @param fromDate The Date to be compared + * @param inclusive Whether or not a Date equal to fromDate should be considered a match + * @return Date A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static Date dateAfter(Date fromDate, Boolean inclusive) + { + return (Date)matches(new fflib_MatcherDefinitions.DatetimeAfter(fromDate, inclusive)); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches dateBefore (not inclusive) + * @param toDate The Date to be compared + * @return Date A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static Date dateBefore(Date toDate) + { + return dateBefore(toDate, false); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches dateBefore + * @param toDate The Date to be compared + * @param inclusive Whether or not a Date equal to toDate should be considered a match + * @return Date A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static Date dateBefore(Date toDate, Boolean inclusive) + { + return (Date)matches(new fflib_MatcherDefinitions.DatetimeBefore(toDate, inclusive)); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches dateBetween (not inclusive) + * @param fromDate The lower bound Date to be compared + * @param toDate The upper bound Date to be compared + * @return Date A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static Date dateBetween(Date fromDate, Date toDate) + { + return dateBetween(fromDate, false, toDate, false); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches dateBetween + * @param fromDate The lower bound Date to be compared + * @param inclusiveFrom Whether or not a Date equal to fromDate should be considered a match + * @param toDate The upper bound Date to be compared + * @param inclusiveTo Whether or not a Date equal to toDate should be considered a match + * @return Date A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static Date dateBetween(Date fromDate, Boolean inclusiveFrom, Date toDate, Boolean inclusiveTo) + { + return (Date)matches(new fflib_MatcherDefinitions.DatetimeBetween(fromDate, inclusiveFrom, toDate, inclusiveTo)); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches datetimeAfter (not inclusive) + * @param fromDate The Datetime to be compared + * @return Datetime A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static Datetime datetimeAfter(Datetime fromDate) + { + return datetimeAfter(fromDate, false); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches datetimeAfter + * @param fromDate The Datetime to be compared + * @param inclusive Whether or not a Datetime equal to fromDate should be considered a match + * @return Datetime A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static Datetime datetimeAfter(Datetime fromDate, Boolean inclusive) + { + return (Datetime)matches(new fflib_MatcherDefinitions.DatetimeAfter(fromDate, inclusive)); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches datetimeBefore (not inclusive) + * @param toDate The Datetime to be compared + * @return Datetime A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static Datetime datetimeBefore(Datetime toDate) + { + return datetimeBefore(toDate, false); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches datetimeBefore + * @param toDate The Datetime to be compared + * @param inclusive Whether or not a Datetime equal to toDate should be considered a match + * @return Datetime A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static Datetime datetimeBefore(Datetime toDate, Boolean inclusive) + { + return (Datetime)matches(new fflib_MatcherDefinitions.DatetimeBefore(toDate, inclusive)); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches datetimeBetween (not inclusive) + * @param fromDate The lower bound Datetime to be compared + * @param toDate The upper bound Datetime to be compared + * @return Datetime A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static Datetime datetimeBetween(Datetime fromDate, Datetime toDate) + { + return datetimeBetween(fromDate, false, toDate, false); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches datetimeBetween + * @param fromDate The lower bound Datetime to be compared + * @param inclusiveFrom Whether or not a Datetime equal to fromDate should be considered a match + * @param toDate The upper bound Datetime to be compared + * @param inclusiveTo Whether or not a Datetime equal to toDate should be considered a match + * @return Datetime A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static Datetime datetimeBetween(Datetime fromDate, Boolean inclusiveFrom, Datetime toDate, Boolean inclusiveTo) + { + return (Datetime)matches(new fflib_MatcherDefinitions.DatetimeBetween(fromDate, inclusiveFrom, toDate, inclusiveTo)); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches decimalBetween (not inclusive) + * @param lower The lower number to be compared + * @param upper The upper number to be compared + * @return Decimal A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static Decimal decimalBetween(Decimal lower, Decimal upper) + { + return decimalBetween(lower, false, upper, false); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches decimalBetween + * @param lower The lower number to be compared + * @param inclusiveLower Whether or not a number equal to the lower bound should be considered a match + * @param upper The upper number to be compared + * @param inclusiveUpper Whether or not a number equal to the upper bound should be considered a match + * @return Decimal A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static Decimal decimalBetween(Decimal lower, Boolean inclusiveLower, Decimal upper, Boolean inclusiveUpper) + { + return (Decimal)matches(new fflib_MatcherDefinitions.DecimalBetween(lower, inclusiveLower, upper, inclusiveUpper)); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches decimalLessThan (not inclusive) + * @param toMatch The number to be compared + * @return Decimal A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static Decimal decimalLessThan(Decimal toMatch) + { + return decimalLessThan(toMatch, false); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches decimalLessThan + * @param toMatch The number to be compared + * @param inclusive Whether or not a number equal to toMatch should be considered a match + * @return Decimal A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static Decimal decimalLessThan(Decimal toMatch, Boolean inclusive) + { + return (Decimal)matches(new fflib_MatcherDefinitions.DecimalLessThan(toMatch, inclusive)); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches decimalMoreThan (not inclusive) + * @param toMatch The number to be compared + * @return Decimal A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static Decimal decimalMoreThan(Decimal toMatch) + { + return decimalMoreThan(toMatch, false); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches decimalMoreThan + * @param toMatch The number to be compared + * @param inclusive Whether or not a number equal to toMatch should be considered a match + * @return Decimal A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static Decimal decimalMoreThan(Decimal toMatch, Boolean inclusive) + { + return (Decimal)matches(new fflib_MatcherDefinitions.DecimalMoreThan(toMatch, inclusive)); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches doubleBetween (not inclusive) + * @param lower The lower number to be compared + * @param upper The upper number to be compared + * @return Double A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static Double doubleBetween(Double lower, Double upper) + { + return doubleBetween(lower, false, upper, false); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches doubleBetween + * @param lower The lower number to be compared + * @param inclusiveLower Whether or not a number equal to the lower bound should be considered a match + * @param upper The upper number to be compared + * @param inclusiveUpper Whether or not a number equal to the upper bound should be considered a match + * @return Double A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static Double doubleBetween(Double lower, Boolean inclusiveLower, Double upper, Boolean inclusiveUpper) + { + return (Double)matches(new fflib_MatcherDefinitions.DecimalBetween(lower, inclusiveLower, upper, inclusiveUpper)); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches doubleLessThan (not inclusive) + * @param toMatch The number to be compared + * @return Double A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static Double doubleLessThan(Double toMatch) + { + return doubleLessThan(toMatch, false); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches doubleLessThan + * @param toMatch The number to be compared + * @param inclusive Whether or not a number equal to toMatch should be considered a match + * @return Double A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static Double doubleLessThan(Double toMatch, Boolean inclusive) + { + return (Double)matches(new fflib_MatcherDefinitions.DecimalLessThan(toMatch, inclusive)); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches doubleMoreThan (not inclusive) + * @param toMatch The number to be compared + * @return Double A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static Double doubleMoreThan(Double toMatch) + { + return doubleMoreThan(toMatch, false); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches doubleMoreThan + * @param toMatch The number to be compared + * @param inclusive Whether or not a number equal to toMatch should be considered a match + * @return Double A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static Double doubleMoreThan(Double toMatch, Boolean inclusive) + { + return (Double)matches(new fflib_MatcherDefinitions.DecimalMoreThan(toMatch, inclusive)); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches FieldSetEquivalentTo + * @param toMatch The fieldSet to be compared + * @return Schema.FieldSet A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static Schema.FieldSet fieldSetEquivalentTo(Schema.FieldSet toMatch) + { + return (Schema.FieldSet)matches(new fflib_MatcherDefinitions.FieldSetEquivalentTo(toMatch)); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches integerBetween (not inclusive) + * @param lower The lower number to be compared + * @param upper The upper number to be compared + * @return Integer A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static Integer integerBetween(Integer lower, Integer upper) + { + return integerBetween(lower, false, upper, false); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches integerBetween + * @param lower The lower number to be compared + * @param inclusiveLower Whether or not a number equal to the lower bound should be considered a match + * @param upper The upper number to be compared + * @param inclusiveUpper Whether or not a number equal to the upper bound should be considered a match + * @return Integer A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static Integer integerBetween(Integer lower, Boolean inclusiveLower, Integer upper, Boolean inclusiveUpper) + { + return (Integer)matches(new fflib_MatcherDefinitions.DecimalBetween(lower, inclusiveLower, upper, inclusiveUpper)); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches integerLessThan (not inclusive) + * @param toMatch The number to be compared + * @return Integer A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static Integer integerLessThan(Integer toMatch) + { + return integerLessThan(toMatch, false); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches integerLessThan + * @param toMatch The number to be compared + * @param inclusive Whether or not a number equal to toMatch should be considered a match + * @return Integer A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static Integer integerLessThan(Integer toMatch, Boolean inclusive) + { + return (Integer)matches(new fflib_MatcherDefinitions.DecimalLessThan(toMatch, inclusive)); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches integerMoreThan (not inclusive) + * @param toMatch The number to be compared + * @return Integer A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static Integer integerMoreThan(Integer toMatch) + { + return integerMoreThan(toMatch, false); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches integerMoreThan + * @param toMatch The number to be compared + * @param inclusive Whether or not a number equal to toMatch should be considered a match + * @return Integer A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static Integer integerMoreThan(Integer toMatch, Boolean inclusive) + { + return (Integer)matches(new fflib_MatcherDefinitions.DecimalMoreThan(toMatch, inclusive)); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches isNotNull + * @return Object A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. (You may need to cast down to your specific object type) + */ + public static Object isNotNull() + { + return matches(new fflib_MatcherDefinitions.IsNotNull()); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches isNull + * @return Object A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. (You may need to cast down to your specific object type) + */ + public static Object isNull() + { + return matches(new fflib_MatcherDefinitions.IsNull()); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches listContains + * @param toMatch The list to be compared + * @return Object A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. (You may need to cast down to your specific object type) + */ + public static Object listContains(Object toMatch) + { + return matches(new fflib_MatcherDefinitions.ListContains(toMatch)); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches listIsEmpty + * @return Object A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. (You may need to cast down to your specific object type) + */ + public static Object listIsEmpty() + { + return matches(new fflib_MatcherDefinitions.ListIsEmpty()); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches longBetween (not inclusive) + * @param lower The lower number to be compared + * @param upper The upper number to be compared + * @return Long A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static Long longBetween(Long lower, Long upper) + { + return longBetween(lower, false, upper, false); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches longBetween + * @param lower The lower number to be compared + * @param inclusiveLower Whether or not a number equal to the lower bound should be considered a match + * @param upper The upper number to be compared + * @param inclusiveUpper Whether or not a number equal to the upper bound should be considered a match + * @return Long A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static Long longBetween(Long lower, Boolean inclusiveLower, Long upper, Boolean inclusiveUpper) + { + return (Long)matches(new fflib_MatcherDefinitions.DecimalBetween(lower, inclusiveLower, upper, inclusiveUpper)); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches longLessThan (not inclusive) + * @param toMatch The number to be compared + * @return Long A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static Long longLessThan(Long toMatch) + { + return longLessThan(toMatch, false); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches longLessThan + * @param toMatch The number to be compared + * @param inclusive Whether or not a number equal to toMatch should be considered a match + * @return Long A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static Long longLessThan(Long toMatch, Boolean inclusive) + { + return (Long)matches(new fflib_MatcherDefinitions.DecimalLessThan(toMatch, inclusive)); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches longMoreThan (not inclusive) + * @param toMatch The number to be compared + * @return Long A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static Long longMoreThan(Long toMatch) + { + return longMoreThan(toMatch, false); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches longMoreThan + * @param toMatch The number to be compared + * @param inclusive Whether or not a number equal to toMatch should be considered a match + * @return Long A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static Long longMoreThan(Long toMatch, Boolean inclusive) + { + return (Long)matches(new fflib_MatcherDefinitions.DecimalMoreThan(toMatch, inclusive)); + } + + /** + * Registers a matcher which will check if the method is called with an SObject of specified SObjectType + * @param objectType The SObjectType to be compared + * @return SObject a dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked + */ + public static SObject sObjectOfType(Schema.SObjectType objectType) + { + return (SObject)matches(new fflib_MatcherDefinitions.SObjectOfType(objectType)); + } + /** + * Registers a matcher which will check if the method is called with an SObject + * @param toMatch A Map of SObjectFields to required values, to be compared to concrete SObject records + * @return SObject a dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked + */ + public static SObject sObjectWith(Map toMatch) + { + return (SObject)matches(new fflib_MatcherDefinitions.SObjectWith(toMatch)); + } + /** + * Registers a matcher which will check if the method is called with a list of SObject + * @param toMatch A list of Map of SObjectFields to required values, to be compared to concrete SObject records + * @return SObject a dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked + */ + public static SObject[] sObjectsWith(list> toMatch) + { + return (SObject[])matches(new fflib_MatcherDefinitions.SObjectsWith(toMatch,true)); + } + + /** + * Registers a matcher which will check if the method is called with a list of SObject + * @param toMatch A list of Map of SObjectFields to required values, to be compared to concrete SObject records. Comparison can be order dependent + * @return SObject a dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked + */ + public static SObject[] sObjectsWith(list> toMatch, Boolean matchInOrder) + { + return (SObject[])matches(new fflib_MatcherDefinitions.SObjectsWith(toMatch,matchInOrder)); + } + + /** + * Registers a matcher which will check if the method is called with an SObject + * @param toMatch The Id to be compared + * @return SObject a dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked + */ + public static SObject sObjectWithId(Id toMatch) + { + return (SObject)matches(new fflib_MatcherDefinitions.SObjectWithId(toMatch)); + } + /** + * Registers a matcher which will check if the method is called with an SObject + * @param toMatch The name to be compared + * @return SObject a dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked + */ + public static SObject sObjectWithName(String toMatch) + { + return (SObject)matches(new fflib_MatcherDefinitions.SObjectWithName(toMatch)); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches stringContains + * @param toMatch The substring to be compared + * @return String A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static String stringContains(String toMatch) + { + return (String)matches(new fflib_MatcherDefinitions.StringContains(toMatch)); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches stringEndsWith + * @param toMatch The substring to be compared + * @return String A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static String stringEndsWith(String toMatch) + { + return (String)matches(new fflib_MatcherDefinitions.StringEndsWith(toMatch)); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches stringIsBlank + * @return String A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static String stringIsBlank() + { + return (String)matches(new fflib_MatcherDefinitions.StringIsBlank()); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches stringIsNotBlank + * @return String A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static String stringIsNotBlank() + { + return (String)matches(new fflib_MatcherDefinitions.StringIsNotBlank()); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches stringMatches + * @param regEx The regex String to be compared + * @return String A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static String stringMatches(String regEx) + { + return (String)matches(new fflib_MatcherDefinitions.StringMatches(regEx)); + } + + /** + * Registers a matcher which will check if the method is called with an arg that matches stringStartsWith + * @param toMatch The substring to be compared + * @return String A dummy object of the correct type, so that when called inline as part of a method call, the correct method is invoked. + */ + public static String stringStartsWith(String toMatch) + { + return (String)matches(new fflib_MatcherDefinitions.StringStartsWith(toMatch)); + } +} \ No newline at end of file diff --git a/sfdx-source/apex-mocks/fflib_Match.cls-meta.xml b/sfdx-source/apex-mocks/fflib_Match.cls-meta.xml new file mode 100644 index 00000000000..08c4dbd5cfa --- /dev/null +++ b/sfdx-source/apex-mocks/fflib_Match.cls-meta.xml @@ -0,0 +1,4 @@ + + + 52.0 + diff --git a/sfdx-source/apex-mocks/fflib_MatcherDefinitions.cls b/sfdx-source/apex-mocks/fflib_MatcherDefinitions.cls new file mode 100644 index 00000000000..fa7a911ced4 --- /dev/null +++ b/sfdx-source/apex-mocks/fflib_MatcherDefinitions.cls @@ -0,0 +1,1390 @@ +/** + * Copyright (c) 2014-2017, FinancialForce.com, inc + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the FinancialForce.com, inc nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * Class providing Apex Mocks standard matcher implementations. + * You shouldn't need to reference the classes directly outside of the ApexMocks framework, instead use the equivalent helper method in fflib_Match + * to construct the matcher, register the matcher and return an object of the correct type to be called in your unit test. + * E.g. Don't construct Eq(Object toMatch), instead call fflib_Match.eq(Object toMatch). + */ +public with sharing class fflib_MatcherDefinitions +{ + /** + * Connective - Enum representing the possible operators for the Combined matcher. Possible values: ALL, AT_LEAST_ONE, NONE + */ + public Enum Connective + { + ALL, + AT_LEAST_ONE, + NONE + } + + /* + * COMBINED MATCHER + */ + + /** + * Combined matcher: compares the supplied argument matches one, all or none of the internal matchers + */ + public class Combined implements fflib_IMatcher + { + private Connective connectiveExpression; + private List internalMatchers; + + /** + * Combined constructor + * @param connectiveExpression Controls the combination mode, i.e. if we need to match all, any or none of the inner matchers + * @param internalMatchers An ordered list of the internal matchers to be combined + * @return fflib_MatcherDefinitions.Combined A new Combined instance + */ + public Combined(Connective connectiveExpression, List internalMatchers) + { + this.connectiveExpression = validate(connectiveExpression); + this.internalMatchers = validate(internalMatchers); + } + + public Boolean matches(Object arg) + { + for (fflib_IMatcher internalMatcher : internalMatchers) + { + if (internalMatcher.matches(arg)) + { + if (connectiveExpression == Connective.AT_LEAST_ONE) + { + //At least one match => success! + return true; + } + else if (connectiveExpression == Connective.NONE) + { + //At least one match => failure! + return false; + } + } + else if (connectiveExpression == Connective.ALL) + { + //At least one mismatch => failure! + return false; + } + } + + //We didn't return early. + //If matching any, must have been no matches => failure! + //If matching all, must have been all matches => success! + //If matching none, must have been all mismatches => success! + return connectiveExpression != Connective.AT_LEAST_ONE; + } + + private Connective validate(Connective connectiveExpression) + { + if (connectiveExpression == null) + { + throw new fflib_ApexMocks.ApexMocksException('Invalid connective expression: ' + connectiveExpression); + } + + return connectiveExpression; + } + + private List validate(List innerMatchers) + { + if (innerMatchers == null || innerMatchers.isEmpty()) + { + throw new fflib_ApexMocks.ApexMocksException('Invalid inner matchers: ' + innerMatchers); + } + + return innerMatchers; + } + + public override String toString() + { + List internalDescriptions = new List(); + for (fflib_IMatcher internalMatcher : internalMatchers) + { + internalDescriptions.add('' + internalMatcher); + } + String internalDescription = String.join(internalDescriptions, ', '); + + switch on connectiveExpression + { + when AT_LEAST_ONE + { + return '[any of: ' + internalDescription + ']'; + } + when ALL + { + return '[all of: ' + internalDescription + ']'; + } + when else + { + return '[none of: ' + internalDescription + ']'; + } + } + } + } + + /* + * OBJECT MATCHERS + */ + + /** + * Eq matcher: checks if the supplied argument is equal (==) to a specified object + */ + public class Eq implements fflib_IMatcher + { + private Object toMatch; + + /** + * Eq constructor + * @param toMatch The object to be compared + * @return fflib_MatcherDefinitions.Eq A new Eq instance + */ + public Eq(Object toMatch) + { + this.toMatch = validateNotNull(toMatch); + } + + public Boolean matches(Object arg) + { + return toMatch == arg; + } + + public override String toString() + { + return '[equals ' + stringify(toMatch) + ']'; + } + } + + /** + * RefEq matcher: checks if the supplied argument is a reference to the same object (===) as a specified object + */ + public class RefEq implements fflib_IMatcher + { + private Object toMatch; + + /** + * RefEq constructor + * @param toMatch The object to be compared + * @return fflib_MatcherDefinitions.RefEq A new RefEq instance + */ + public RefEq(Object toMatch) + { + this.toMatch = validateNotNull(toMatch); + } + + public Boolean matches(Object arg) + { + return toMatch === arg; + } + + public override String toString() + { + return '[reference equals ' + fflib_MatcherDefinitions.stringify(toMatch) + ']'; + } + } + + /* + * ANY MATCHERS + */ + + /** + * AnyBoolean matcher: checks if the supplied argument is an instance of a Boolean + */ + public class AnyBoolean implements fflib_IMatcher + { + public Boolean matches(Object arg) + { + return arg != null && arg instanceof Boolean; + } + + public override String toString() + { + return '[any Boolean]'; + } + } + + /** + * AnyDate matcher: checks if the supplied argument is an instance of a Date + */ + public class AnyDate implements fflib_IMatcher + { + public Boolean matches(Object arg) + { + return arg != null && arg instanceof Date; + } + + public override String toString() + { + return '[any Date]'; + } + } + + /** + * AnyDatetime matcher: checks if the supplied argument is an instance of a Datetime + */ + public class AnyDatetime implements fflib_IMatcher + { + public Boolean matches(Object arg) + { + return arg != null && arg instanceof Datetime; + } + + public override String toString() + { + return '[any DateTime]'; + } + } + + /** + * AnyDecimal matcher: checks if the supplied argument is an instance of a Decimal + */ + public class AnyDecimal implements fflib_IMatcher + { + public Boolean matches(Object arg) + { + return arg != null && arg instanceof Decimal; + } + + public override String toString() + { + return '[any Decimal]'; + } + } + + /** + * AnyDouble matcher: checks if the supplied argument is an instance of a Double + */ + public class AnyDouble implements fflib_IMatcher + { + public Boolean matches(Object arg) + { + return arg != null && arg instanceof Double; + } + + public override String toString() + { + return '[any Double]'; + } + } + + /** + * AnyFieldSet matcher: checks if the supplied argument is an instance of a FieldSet + */ + public class AnyFieldSet implements fflib_IMatcher + { + public Boolean matches(Object arg) + { + return arg != null && arg instanceof Schema.FieldSet; + } + + public override String toString() + { + return '[any FieldSet]'; + } + } + + /** + * AnyId matcher: checks if the supplied argument is an instance of an Id + */ + public class AnyId implements fflib_IMatcher + { + public Boolean matches(Object arg) + { + return arg != null && arg instanceof Id; + } + + public override String toString() + { + return '[any Id]'; + } + } + + /** + * AnyInteger matcher: checks if the supplied argument is an instance of an Integer + */ + public class AnyInteger implements fflib_IMatcher + { + public Boolean matches(Object arg) + { + return arg != null && arg instanceof Integer; + } + + public override String toString() + { + return '[any Integer]'; + } + } + + /** + * AnyList matcher: checks if the supplied argument is an instance of a List + */ + public class AnyList implements fflib_IMatcher + { + public Boolean matches(Object arg) + { + return arg != null && arg instanceof List; + } + + public override String toString() + { + return '[any list]'; + } + } + + /** + * AnyLong matcher: checks if the supplied argument is an instance of a Long + */ + public class AnyLong implements fflib_IMatcher + { + public Boolean matches(Object arg) + { + return arg != null && arg instanceof Long; + } + + public override String toString() + { + return '[any Long]'; + } + } + + /** + * AnyObject matcher: checks if the supplied argument is an instance of an Object + */ + public class AnyObject implements fflib_IMatcher + { + public Boolean matches(Object arg) + { + return arg != null; + } + + public override String toString() + { + return '[any Object]'; + } + } + + /** + * AnyString matcher: checks if the supplied argument is an instance of a String + */ + public class AnyString implements fflib_IMatcher + { + public Boolean matches(Object arg) + { + return arg != null && arg instanceof String; + } + + public override String toString() + { + return '[any String]'; + } + } + + /** + * AnySObject matcher: checks if the supplied argument is an instance of an SObject + */ + public class AnySObject implements fflib_IMatcher + { + public Boolean matches(Object arg) + { + return arg != null && arg instanceof SObject; + } + + public override String toString() + { + return '[any SObject]'; + } + } + + /** + * AnySObjectField matcher: checks if the supplied argument is an instance of an SObjectField + */ + public class AnySObjectField implements fflib_IMatcher + { + public Boolean matches(Object arg) + { + return arg != null && arg instanceof SObjectField; + } + + public override String toString() + { + return '[any SObjectField]'; + } + } + + /** + * AnySObjectType matcher: checks if the supplied argument is an instance of an SObjectType + */ + public class AnySObjectType implements fflib_IMatcher + { + public Boolean matches(Object arg) + { + return arg != null && arg instanceof SObjectType; + } + + public override String toString() + { + return '[any SObjectType]'; + } + } + + /* + * DATETIME MATCHERS + */ + + /** + * DatetimeAfter matcher: checks if the supplied argument is after a specified datetime + */ + public class DatetimeAfter implements fflib_IMatcher + { + private Datetime fromDatetime; + private Boolean inclusive; + + /** + * DatetimeAfter constructor + * @param fromDatetime The datetime to be compared + * @param inclusive Whether or not dates equal to the fromDatetime should be considered a match + * @return fflib_MatcherDefinitions.DatetimeAfter A new DatetimeAfter instance + */ + public DatetimeAfter(Datetime fromDatetime, Boolean inclusive) + { + this.fromDatetime = (Datetime)validateNotNull(fromDatetime); + this.inclusive = (Boolean)validateNotNull(inclusive); + } + + public Boolean matches(Object arg) + { + if (arg instanceof Datetime) + { + Datetime datetimeToCompare = (Datetime)arg; + return inclusive ? fromDatetime <= datetimeToCompare : fromDatetime < datetimeToCompare; + } + + return false; + } + + public override String toString() + { + if (inclusive) + { + return '[on or after ' + JSON.serialize(fromDateTime) + ']'; + } + else + { + return '[after ' + JSON.serialize(fromDateTime) + ']'; + } + } + } + + /** + * DatetimeBefore matcher: checks if the supplied argument is before a specified datetime + */ + public class DatetimeBefore implements fflib_IMatcher + { + private Datetime toDatetime; + private Boolean inclusive; + + /** + * DatetimeBefore constructor + * @param toDatetime The datetime to be compared + * @param inclusive Whether or not dates equal to the toDatetime should be considered a match + * @return fflib_MatcherDefinitions.DatetimeBefore A new DatetimeBefore instance + */ + public DatetimeBefore(Datetime toDatetime, Boolean inclusive) + { + this.toDatetime = (Datetime)validateNotNull(toDatetime); + this.inclusive = (Boolean)validateNotNull(inclusive); + } + + public Boolean matches(Object arg) + { + if (arg instanceof Datetime) + { + Datetime datetimeToCompare = (Datetime)arg; + return inclusive ? datetimeToCompare <= toDatetime : datetimeToCompare < toDatetime; + } + + return false; + } + + public override String toString() + { + if (inclusive) + { + return '[on or before ' + JSON.serialize(toDateTime) + ']'; + } + else + { + return '[before ' + JSON.serialize(toDateTime) + ']'; + } + } + } + + /** + * DatetimeBetween matcher: checks if the supplied argument is between two specified datetimes + */ + public class DatetimeBetween implements fflib_IMatcher + { + private Datetime fromDatetime; + private Boolean inclusiveFrom; + private Datetime toDatetime; + private Boolean inclusiveTo; + + /** + * DatetimeBetween constructor + * @param fromDatetime The lower bound datetime to be compared + * @param inclusiveFrom Whether or not dates equal to the fromDatetime should be considered a match + * @param toDatetime The upper bound dateetime to be compared + * @param inclusiveTo Whether or not dates equal to the toDatetime should be considered a match + * @return fflib_MatcherDefinitions.DatetimeBetween A new DatetimeBetween instance + */ + public DatetimeBetween(Datetime fromDatetime, Boolean inclusiveFrom, Datetime toDatetime, Boolean inclusiveTo) + { + this.fromDatetime = (Datetime)validateNotNull(fromDatetime); + this.inclusiveFrom = (Boolean)validateNotNull(inclusiveFrom); + this.toDatetime = (Datetime)validateNotNull(toDatetime); + this.inclusiveTo = (Boolean)validateNotNull(inclusiveTo); + } + + public Boolean matches(Object arg) + { + if (arg instanceof Datetime) + { + Datetime datetimeToCompare = (Datetime)arg; + if ((inclusiveFrom ? datetimeToCompare >= fromDatetime : datetimeToCompare > fromDatetime) + && (inclusiveTo ? datetimeToCompare <= toDatetime : datetimeToCompare < toDatetime)) + { + return true; + } + } + + return false; + } + + public override String toString() + { + return String.format('[{0} {1} and {2} {3}]', new List{ + inclusiveFrom ? 'on or after' : 'after', + JSON.serialize(fromDateTime), + inclusiveTo ? 'on or before' : 'before', + JSON.serialize(toDateTime) + }); + } + } + + /* + * DECIMAL (AND OTHER NUMBER) MATCHERS + */ + + /** + * DecimalBetween matcher: checks if the supplied argument is between two specified decimals + */ + public class DecimalBetween implements fflib_IMatcher + { + private Decimal lower; + private Boolean inclusiveLower; + private Decimal upper; + private Boolean inclusiveUpper; + + /** + * DecimalBetween constructor + * @param lower The lower bound number to be compared + * @param inclusiveLower Whether or not numbers equal to lower should be considered a match + * @param upper The upper bound number to be compared + * @param inclusiveUpper Whether or not numbers equal to upper should be considered a match + * @return fflib_MatcherDefinitions.DecimalBetween A new DecimalBetween instance + */ + public DecimalBetween(Decimal lower, Boolean inclusiveLower, Decimal upper, Boolean inclusiveUpper) + { + this.lower = (Decimal)validateNotNull(lower); + this.inclusiveLower = (Boolean)validateNotNull(inclusiveLower); + this.upper = (Decimal)validateNotNull(upper); + this.inclusiveUpper = (Boolean)validateNotNull(inclusiveUpper); + } + + public Boolean matches(Object arg) + { + if (arg != null && arg instanceof Decimal) + { + Decimal longArg = (Decimal)arg; + + if ((inclusiveLower ? longArg >= lower : longArg > lower) + && (inclusiveUpper ? longArg <= upper : longArg < upper)) + { + return true; + } + } + + return false; + } + + public override String toString() + { + return String.format('{0} {1} and {2} {3}', new List{ + inclusiveLower ? 'greater than or equal to' : 'greater than', + '' + lower, + inclusiveUpper ? 'less than or equal to' : 'less than', + '' + upper + }); + } + } + + /** + * DecimalLessThan matcher: checks if the supplied argument is less than a specified decimal + */ + public class DecimalLessThan implements fflib_IMatcher + { + private Decimal toMatch; + private Boolean inclusive; + + /** + * DecimalLessThan constructor + * @param toMatch The number to be compared against + * @param inclusive Whether or not numbers equal to toMatch should be considered a match + * @return fflib_MatcherDefinitions.DecimalLessThan A new DecimalLessThan instance + */ + public DecimalLessThan(Decimal toMatch, Boolean inclusive) + { + this.toMatch = (Decimal)validateNotNull(toMatch); + this.inclusive = (Boolean)validateNotNull(inclusive); + } + + public Boolean matches(Object arg) + { + if (arg != null && arg instanceof Decimal) + { + Decimal longArg = (Decimal)arg; + return inclusive ? longArg <= toMatch : longArg < toMatch; + } + + return false; + } + + public override String toString() + { + if (inclusive) + { + return '[less than or equal to ' + toMatch + ']'; + } + else + { + return '[less than ' + toMatch + ']'; + } + } + } + + /** + * DecimalMoreThan matcher: checks if the supplied argument is greater than a specified decimal + */ + public class DecimalMoreThan implements fflib_IMatcher + { + private Decimal toMatch; + private Boolean inclusive; + + /** + * DecimalMoreThan constructor + * @param toMatch The number to be compared against + * @param inclusive Whether or not numbers equal to toMatch should be considered a match + * @return fflib_MatcherDefinitions.DecimalMoreThan A new DecimalMoreThan instance + */ + public DecimalMoreThan(Decimal toMatch, Boolean inclusive) + { + this.toMatch = (Decimal)validateNotNull(toMatch); + this.inclusive = (Boolean)validateNotNull(inclusive); + } + + public Boolean matches(Object arg) + { + if (arg != null && arg instanceof Decimal) + { + Decimal longArg = (Decimal)arg; + return inclusive ? longArg >= toMatch : longArg > toMatch; + } + + return false; + } + + public override String toString() + { + if (inclusive) + { + return '[greater than or equal to ' + toMatch + ']'; + } + else + { + return '[greater than ' + toMatch + ']'; + } + } + } + + /** + * FIELDSET MATCHERS + */ + + /** + * FieldSetEquivalentTo matcher: checks the supplied argument is a field set with the same field set members as a specified field set + * This matcher is needed because equivalent FieldSets do not pass == checks, and we can't override equals/hashcode on FieldSets. + */ + public class FieldSetEquivalentTo implements fflib_IMatcher + { + private final Set toMatch; + + /* + * Dirty test-only constructor, allowing us to test this class even if there are no field sets defined in the current org. + */ + @TestVisible + public FieldSetEquivalentTo() + { + this.toMatch = null; + } + + public FieldSetEquivalentTo(Schema.FieldSet toMatch) + { + this.toMatch = new Set(((Schema.FieldSet)validateNotNull(toMatch)).getFields()); + } + + public Boolean matches(Object arg) + { + return (toMatch != null && arg != null && arg instanceof Schema.FieldSet) ? toMatch == new Set(((FieldSet)arg).getFields()) : false; + } + + public override String toString() + { + return '[FieldSet with fields ' + fflib_MatcherDefinitions.stringify(toMatch) + ']'; + } + } + + /* + * IS MATCHERS + */ + + /** + * IsNotNull matcher: checks the supplied argument is not null + */ + public class IsNotNull implements fflib_IMatcher + { + public Boolean matches(Object arg) + { + return arg != null; + } + + public override String toString() + { + return '[is not null]'; + } + } + + /** + * IsNull matcher: checks the supplied argument is null + */ + public class IsNull implements fflib_IMatcher + { + public Boolean matches(Object arg) + { + return arg == null; + } + + public override String toString() + { + return '[is null]'; + } + } + + /* + * LIST MATCHERS + */ + + /** + * ListContains matcher: checks if the supplied argument is equal (==) to any of the elements in a specified list + */ + public class ListContains implements fflib_IMatcher + { + private Object toMatch; + + /** + * ListContains constructor + * @param toMatch The list of objects to be compared + * @return fflib_MatcherDefinitions.ListContains A new ListContains instance + */ + public ListContains(Object toMatch) + { + this.toMatch = toMatch; + } + + public Boolean matches(Object arg) + { + if (arg != null && arg instanceof List) + { + for (Object o : (List)arg) + { + if (o == toMatch) + { + return true; + } + } + } + + return false; + } + + public override String toString() + { + return '[list containing ' + fflib_MatcherDefinitions.stringify(toMatch) + ']'; + } + } + + /** + * ListIsEmpty matcher: checks if the supplied argument is an empty list + */ + public class ListIsEmpty implements fflib_IMatcher + { + public Boolean matches(Object arg) + { + return arg != null + && arg instanceof List + && ((List)arg).isEmpty(); + } + + public override String toString() + { + return '[empty list]'; + } + } + + /* + * SOBJECT MATCHERS + */ + + /** + * SObjectOfType matcher: checks if the supplied argument has the specified SObjectType + */ + public class SObjectOfType implements fflib_IMatcher + { + private Schema.SObjectType objectType; + + /** + * SObjectOfType constructor + * @param objectType The SObjectType to be compared + * @return fflib_MatcherDefinitions.SObjectOfType A new SObjectOfType instance + */ + public SObjectOfType(Schema.SObjectType objectType) + { + this.objectType = (Schema.SObjectType)validateNotNull(objectType); + } + + public Boolean matches(Object arg) + { + if (arg != null && arg instanceof SObject) + { + SObject soArg = (SObject)arg; + return soArg.getSObjectType() == objectType; + } + + return false; + } + + public override String toString() + { + return '[SObject of type ' + objectType + ']'; + } + } + + /** + * SObjectWith matcher: compares the supplied argument against a Map, representing fields and their expected values. + * Note. this method silently catches exceptions getting values for the supplied fields from the arguments supplied in method calls. + * + * If your matcher is mysteriously failing for your SObject record, it may be getting silent 'SObject row was retrieved via SOQL without querying + * the requested field' exceptions, because you haven't queried all of the fields used in this matcher. + */ + public class SObjectWith implements fflib_IMatcher + { + private Map toMatch; + + /** + * SObjectWith constructor + * @param toMatch A map of fields to their values to be compared. We compare each of these fields against the supplied sobject's field values. + * @return fflib_MatcherDefinitions.SObjectWith A new SObjectWith instance + */ + public SObjectWith(Map toMatch) + { + this.toMatch = validate(toMatch); + } + + public Boolean matches(Object arg) + { + if (arg != null && arg instanceof SObject) + { + SObject soArg = (SObject)arg; + if (!sobjectMatches(soArg,this.toMatch)) + { + return false; + } + return true; + } + + return false; + } + + private Map validate(Map arg) + { + if (arg == null || arg.isEmpty()) + { + throw new fflib_ApexMocks.ApexMocksException('Arg cannot be null/empty: ' + arg); + } + + return arg; + } + + public override String toString() + { + return '[SObject with fields ' + fflib_MatcherDefinitions.stringify(toMatch) + ']'; + } + } + + /** + * SObjectsWith matcher: compares the supplied list argument against a list>, representing fields and their expected values. + * Each list element represents one Sobject in a list supplied to a mocked method that accepts list. + * Each list element that is a map is compared against the equivalent argument list element position + * + * Example: + * You use uow.registerNew(someListofAccounts). You mock uow in the testmethod. + * toMatch is new list { + * new map {Account.Name => 'foo'}, + * new map {Account.Name => 'bar'} + * } + * By default, matchers compare against argument elements in order, viz: + * The matcher will compare the first Account in the list passed to uow.registerNew to the first map of field values (i.e. Account[0].Name must be 'foo') + * The matcher then compares the second Account in the list passed to uow.registerNew to the second map of field values (i.e. Account[1].Name must be 'bar') + * + * Optional second argument matchInOrderr if false means that each argument element is compared against all matcher elements + * if everuy argument is matched exactly once and no matcher matches more than once, then the match is true + * + * If the arity of the list passed in the mocked method doesn't agree with the arity of the map of expected field values, false is returned + * + * Note. this method silently catches exceptions getting values for the supplied fields from the arguments supplied in method calls. + * + * If your matcher is mysteriously failing for your SObject record, it may be getting silent 'SObject row was retrieved via SOQL without querying + * the requested field' exceptions, because you haven't queried all of the fields used in this matcher. + */ + public class SObjectsWith implements fflib_IMatcher + { + private list> toMatch; + private Boolean matchInOrder { + get + { + return matchInOrder == null ? false : matchInOrder; + } + set; + } + + /** + * SObjectsWith constructor + * @param toMatch A list of maps of fields to their values to be compared. We compare each of these fields against the supplied list of sobject's field values. + * @return fflib_MatcherDefinitions.SObjectWith A new SObjectWith instance + */ + public SObjectsWith(list> toMatch, Boolean matchInOrder) + { + this.toMatch = validate(toMatch); + this.matchInOrder = matchInOrder; + } + public SObjectsWith(list> toMatch) + { + this.toMatch = validate(toMatch); + this.matchInOrder = true; + } + public Boolean matches(Object arg) + { + if (arg != null && arg instanceof list) + { + SObject[] sobjsArg = (SObject[])arg; + list> toMatches = new list>(); + + // Counters for matchInOrder = false; not relevant for matchInOrder = true + list argMatchedCounts = new list(); // # times matched by a matcher. anything other than 1 is match error + list matcherMatchedCounts = new list(); // for each map + // # args that match it. Anything other than 1 is match error + + + for (map mtchElm : toMatch) + { + toMatches.add(mtchElm); + matcherMatchedCounts.add(0); + } + + if (sobjsArg.size() != toMatches.size()) // arity of arguments to mocked method doesn't agree with arity of expected matches + { + return false; + } + + if (matchInOrder) + { + for (Integer i = 0; i < sobjsArg.size(); i++) + { // match in order (toMatch[i] must match arg[i]) + if (!sobjectMatches(sobjsArg[i],toMatches[i])) + { + return false; + } + } + return true; + } + else + { + // match in any order (but every arg must be matched only once) + for (Integer i = 0; i < sobjsArg.size(); i++) + { + argMatchedCounts.add(0); + // For each arg passed to mocked method, see if any match in the list of match field maps. + // Loop within loop so not hugely efficient but there are no IDs to rely on. + // Avoid unit test methods that build huge lists of expected results + + for (Integer m = 0; m < toMatches.size(); m++) + { + if (sobjectMatches(sobjsArg[i],toMatches[m])) + { + argMatchedCounts[i] ++; + matcherMatchedCounts[m] ++; + } + } + } + // Check to see that every arg was matched only once + // Check to see that every matcher matched only once + // Anything else is a match fail + + for (Integer i=0; i < argMatchedCounts.size(); i++) + { + if (argMatchedCounts[i] != 1 || matcherMatchedCounts[i] != 1) + { + return false; + } + } + return true; + } + } + return false; + } + + private list> validate(list> arg) + { + if (arg == null || arg.isEmpty() ) + { + throw new fflib_ApexMocks.ApexMocksException('Arg cannot be null/empty/other than list of map: ' + arg); + } + + return arg; + } + + public override String toString() + { + if (matchInOrder) + { + return '[ordered SObjects with ' + fflib_MatcherDefinitions.stringify(toMatch) + ']'; + } + else + { + return '[unordered SObjects with ' + fflib_MatcherDefinitions.stringify(toMatch) + ']'; + } + } + + + } + + /** + * helper for the sObjectWith, sObjectsWith matchers + * Compares to see if the field values in toMatch exist in the sobj + **/ + private static Boolean sObjectMatches(Sobject sobj, map toMatch) + { + for (Schema.SObjectField f : toMatch.keySet()) + { + Object valueToMatch = toMatch.get(f); + + try + { + if (sobj.get(f) != valueToMatch) + { + return false; + } + } + catch (Exception e) + { + //If we fail to get the value for a field it's either: + // - 'SObject row was retrieved via SOQL without querying the requested field' as a mismatch + // - System.SObjectException - Account.Id does not belong to SObject type Opportunity + //Don't care too much, just treat this as a mismatch. + return false; + } + } + return true; // map of expected fieldvals found in sobj arg + } + + /** + * SObjectWithId matcher: checks if the supplied argument has the specified Id + */ + public class SObjectWithId implements fflib_IMatcher + { + private Id toMatch; + + /** + * SObjectWithId constructor + * @param toMatch The Id to be compared + * @return fflib_MatcherDefinitions.SObjectWithId A new SObjectWithId instance + */ + public SObjectWithId(Id toMatch) + { + this.toMatch = (Id)validateNotNull(toMatch); + } + + public Boolean matches(Object arg) + { + if (arg != null && arg instanceof SObject) + { + SObject soArg = (SObject)arg; + return soArg.Id == toMatch; + } + + return false; + } + + public override String toString() + { + return '[SObject with Id "' + toMatch + '"]'; + } + } + + /** + * SObjectWithName matcher: checks if the supplied argument has the specified Name + */ + public class SObjectWithName implements fflib_IMatcher + { + private String toMatch; + + /** + * SObjectWithName constructor + * @param toMatch The name to be compared + * @return fflib_MatcherDefinitions.SObjectWithName A new SObjectWithName instance + */ + public SObjectWithName(String toMatch) + { + this.toMatch = (String)validateNotNull(toMatch); + } + + public Boolean matches(Object arg) + { + if (arg != null && arg instanceof SObject) + { + SObject soArg = (SObject)arg; + Schema.DescribeSObjectResult describe = soArg.getSObjectType().getDescribe(); + for (Schema.SObjectField f : describe.fields.getMap().values()) + { + if (f.getDescribe().isNameField()) + { + return soArg.get(f) == toMatch; + } + } + } + + return false; + } + + public override String toString() + { + return '[SObject with Name "' + toMatch + '"]'; + } + } + + /* + * STRING MATCHERS + */ + + /** + * StringContains matcher: checks if the supplied argument contains the specified substring + */ + public class StringContains implements fflib_IMatcher + { + private String toMatch; + + /** + * StringContains constructor + * @param toMatch The substring to be compared + * @return fflib_MatcherDefinitions.StringContains A new StringContains instance + */ + public StringContains(String toMatch) + { + this.toMatch = (String)validateNotNull(toMatch); + } + + public Boolean matches(Object arg) + { + return arg != null && arg instanceof String ? ((String)arg).contains(toMatch) : false; + } + + public override String toString() + { + return '[contains "' + toMatch + '"]'; + } + } + + /** + * StringEndsWith matcher: checks if the supplied argument ends with the specified substring + */ + public class StringEndsWith implements fflib_IMatcher + { + private String toMatch; + + /** + * StringEndsWith constructor + * @param toMatch The substring to be compared + * @return fflib_MatcherDefinitions.StringEndsWith A new StringEndsWith instance + */ + public StringEndsWith(String toMatch) + { + this.toMatch = (String)validateNotNull(toMatch); + } + + public Boolean matches(Object arg) + { + return arg != null && arg instanceof String ? ((String)arg).endsWith(toMatch) : false; + } + + public override String toString() + { + return '[ends with "' + toMatch + '"]'; + } + } + + /** + * StringIsBlank matcher: checks if the supplied argument is a blank String + */ + public class StringIsBlank implements fflib_IMatcher + { + public Boolean matches(Object arg) + { + return arg == null || (arg instanceof String ? String.isBlank((String)arg) : false); + } + + public override String toString() + { + return '[blank String]'; + } + } + + /** + * StringIsNotBlank matcher: checks if the supplied argument is a non-blank string + */ + public class StringIsNotBlank implements fflib_IMatcher + { + public Boolean matches(Object arg) + { + return (arg != NULL && arg instanceof String) ? String.isNotBlank((String)arg) : false; + } + + public override String toString() + { + return '[non-blank String]'; + } + } + + /** + * StringMatches matcher: checks if the supplied argument matches the specified regex expression + */ + public class StringMatches implements fflib_IMatcher + { + private Pattern pat; + private final String regEx; + + /** + * StringMatches constructor + * @param toMatch The substring to be compared + * @return fflib_MatcherDefinitions.StringMatches A new StringMatches instance + */ + public StringMatches(String regEx) + { + this.regEx = regEx; + this.pat = Pattern.compile((String)validateNotNull(regEx)); + } + + public Boolean matches(Object arg) + { + return arg != null && arg instanceof String ? pat.matcher((String)arg).matches() : false; + } + + public override String toString() + { + return '[matches regex "' + regEx + '"]'; + } + } + + /** + * StringStartsWith matcher: checks if the supplied argument starts with the specified substring + */ + public class StringStartsWith implements fflib_IMatcher + { + private String toMatch; + + /** + * StringStartsWith constructor + * @param toMatch The substring to be compared + * @return fflib_MatcherDefinitions.StringStartsWith A new StringStartsWith instance + */ + public StringStartsWith(String toMatch) + { + this.toMatch = (String)validateNotNull(toMatch); + } + + public Boolean matches(Object arg) + { + return arg != null && arg instanceof String ? ((String)arg).startsWith(toMatch) : false; + } + + public override String toString() + { + return '[starts with "' + toMatch + '"]'; + } + } + + /* + * Helpers + */ + + private static Object validateNotNull(Object arg) + { + if (arg == null) + { + throw new fflib_ApexMocks.ApexMocksException('Arg cannot be null: ' + arg); + } + + return arg; + } + + public static String stringify(Object value) + { + try + { + return JSON.serialize(value, false); + } + catch (Exception error) + { + return '' + value; + } + } +} diff --git a/sfdx-source/apex-mocks/fflib_MatcherDefinitions.cls-meta.xml b/sfdx-source/apex-mocks/fflib_MatcherDefinitions.cls-meta.xml new file mode 100644 index 00000000000..08c4dbd5cfa --- /dev/null +++ b/sfdx-source/apex-mocks/fflib_MatcherDefinitions.cls-meta.xml @@ -0,0 +1,4 @@ + + + 52.0 + diff --git a/sfdx-source/apex-mocks/fflib_MatchersReturnValue.cls b/sfdx-source/apex-mocks/fflib_MatchersReturnValue.cls new file mode 100644 index 00000000000..b6984c2678b --- /dev/null +++ b/sfdx-source/apex-mocks/fflib_MatchersReturnValue.cls @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2014-2016, FinancialForce.com, inc + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the FinancialForce.com, inc nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +public with sharing class fflib_MatchersReturnValue +{ + public List matchers; + public fflib_MethodReturnValue returnValue; + + public fflib_MatchersReturnValue(List matchers, fflib_MethodReturnValue returnValue) + { + this.matchers = matchers; + this.returnValue = returnValue; + } +} \ No newline at end of file diff --git a/sfdx-source/apex-mocks/fflib_MatchersReturnValue.cls-meta.xml b/sfdx-source/apex-mocks/fflib_MatchersReturnValue.cls-meta.xml new file mode 100644 index 00000000000..08c4dbd5cfa --- /dev/null +++ b/sfdx-source/apex-mocks/fflib_MatchersReturnValue.cls-meta.xml @@ -0,0 +1,4 @@ + + + 52.0 + diff --git a/sfdx-source/apex-mocks/fflib_MethodArgValues.cls b/sfdx-source/apex-mocks/fflib_MethodArgValues.cls new file mode 100644 index 00000000000..58857b885de --- /dev/null +++ b/sfdx-source/apex-mocks/fflib_MethodArgValues.cls @@ -0,0 +1,70 @@ +/** + * Copyright (c) 2014-2016, FinancialForce.com, inc + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the FinancialForce.com, inc nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +public with sharing class fflib_MethodArgValues +{ + public List argValues; + + /** + * Wrapper object which encapsulates the argument values + * supplied during a given method call. + * @param argValues The + * @return fflib_MethodArgValues The method argument wrapper object + */ + public fflib_MethodArgValues(List argValues) + { + this.argValues = argValues; + } + + /** + * Standard equals override. + * @param other The object whose equality we are verifying + * @return Boolean True if meaningfully equivalent, false otherwise. + */ + public Boolean equals(Object other) + { + if (this === other) + { + return true; + } + + fflib_MethodArgValues that = other instanceof fflib_MethodArgValues ? (fflib_MethodArgValues)other : null; + return that != null && this.argValues == that.argValues; + } + + /** + * Standard hashCode override. + * @return Integer The generated hashCode + */ + public Integer hashCode() + { + Integer prime = 31; + Integer result = 1; + + result = prime * result + ((argValues == null) ? 0 : argValues.hashCode()); + + return result; + } +} \ No newline at end of file diff --git a/sfdx-source/apex-mocks/fflib_MethodArgValues.cls-meta.xml b/sfdx-source/apex-mocks/fflib_MethodArgValues.cls-meta.xml new file mode 100644 index 00000000000..08c4dbd5cfa --- /dev/null +++ b/sfdx-source/apex-mocks/fflib_MethodArgValues.cls-meta.xml @@ -0,0 +1,4 @@ + + + 52.0 + diff --git a/sfdx-source/apex-mocks/fflib_MethodCountRecorder.cls b/sfdx-source/apex-mocks/fflib_MethodCountRecorder.cls new file mode 100644 index 00000000000..7bec4e5bc76 --- /dev/null +++ b/sfdx-source/apex-mocks/fflib_MethodCountRecorder.cls @@ -0,0 +1,61 @@ +/* + Copyright (c) 2014-2017 FinancialForce.com, inc. All rights reserved. + */ + +/** + * @group Core + */ +public with sharing class fflib_MethodCountRecorder +{ + /* + * Map of method arguments by type name. + * + * Key: qualifiedMethod + * Object: list of method arguments. + * + * Object: map of count by method call argument. + */ + private static Map> methodArgumentsByTypeName = + new Map>(); + + private static List orderedMethodCalls = + new List(); + + /** + * Getter for the list of the methods ordered calls. + * @return The list of methods called in order. + */ + public static List getOrderedMethodCalls() + { + return orderedMethodCalls; + } + + /** + * Getter for the map of the method's calls with the related arguments. + * @return The map of methods called with the arguments. + */ + public static Map> getMethodArgumentsByTypeName() + { + return methodArgumentsByTypeName; + } + + /** + * Record a method was called on a mock object. + * @param invocation The object holding all the data of the invocation, like the method and arguments and the mock instance. + */ + public void recordMethod(fflib_InvocationOnMock invocation) + { + List methodArgs = + methodArgumentsByTypeName.get(invocation.getMethod()); + + if (methodArgs == null) + { + methodArgs = new List(); + methodArgumentsByTypeName.put(invocation.getMethod(), methodArgs); + } + + methodArgs.add(invocation.getMethodArgValues()); + + orderedMethodCalls.add(invocation); + } +} \ No newline at end of file diff --git a/sfdx-source/apex-mocks/fflib_MethodCountRecorder.cls-meta.xml b/sfdx-source/apex-mocks/fflib_MethodCountRecorder.cls-meta.xml new file mode 100644 index 00000000000..08c4dbd5cfa --- /dev/null +++ b/sfdx-source/apex-mocks/fflib_MethodCountRecorder.cls-meta.xml @@ -0,0 +1,4 @@ + + + 52.0 + diff --git a/sfdx-source/apex-mocks/fflib_MethodReturnValue.cls b/sfdx-source/apex-mocks/fflib_MethodReturnValue.cls new file mode 100644 index 00000000000..7f834006add --- /dev/null +++ b/sfdx-source/apex-mocks/fflib_MethodReturnValue.cls @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2014-2017, FinancialForce.com, inc + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the FinancialForce.com, inc nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @group Core + * Class defining a method return value. + */ +@isTest +public with sharing class fflib_MethodReturnValue +{ + private StandardAnswer basicAnswer = new StandardAnswer(); + + /** + * Instance of the implementation of the Answer interface that implements the answer, + * if an answer isn't explicitly set the standard answer will be used, which just returns the stubbed return value. + */ + public fflib_Answer Answer { get; set; } + + /** + * Setup a stubbed return value. + * @param value The value to return from the stubbed method call. + * @return The fflib_MethodReturnValue instance to allow you to chain the methods. + */ + public fflib_MethodReturnValue thenReturn(Object value) + { + thenAnswer(this.basicAnswer.setValue(value)); + return this; + } + + /** + * Setup a stubbed exception. + * @param e The exception to throw from the stubbed method call. + * @return The fflib_MethodReturnValue instance to allow you to chain the methods. + */ + public fflib_MethodReturnValue thenThrow(Exception e) + { + thenAnswer(this.basicAnswer.setValue(e)); + return this; + } + + /** + * Setup a stubbed answer. + * @param answer The answer to run from the stubbed method call. + */ + public void thenAnswer(fflib_Answer answer) + { + this.Answer = answer; + } + + /** + * Setup a list of stubbed return values. + * @param values The values to return from the stubbed method call in consecutive calls. + * @return The fflib_MethodReturnValue instance to allow you to chain the methods. + */ + public fflib_MethodReturnValue thenReturnMulti(List values) + { + thenAnswer(this.basicAnswer.setValues(values)); + return this; + } + + /** + * Setup a list stubbed exceptions. + * @param es The exceptions to throw from the stubbed method call in consecutive calls. + * @return The fflib_MethodReturnValue instance to allow you to chain the methods. + */ + public fflib_MethodReturnValue thenThrowMulti(List es) + { + thenAnswer(this.basicAnswer.setValues(es)); + return this; + } + + /** + * @group Core + * Inner class to handle all the stubs that do not use the thenAnswer method directly. + * For internal use only. + */ + public class StandardAnswer implements fflib_Answer + { + private Integer whichStubReturnIndex = 0; + /* + * It stores the return values for the method stubbed. + * The values would be stored and then returned as part of the standard answer invocation. + */ + private List ReturnValues = new List(); + + /** + * Setter of a single return value. + * @param value The value to be set as return value for the StandardAnswer object. + * @return The StandardAnswer instance. + */ + public StandardAnswer setValue(Object value) + { + ReturnValues.add(value); + return this; + } + + /** + * Setter of the list of return values. + * @param value The value to be set as return value for the StandardAnswer object. + * @return the StandardAnswer instance. + */ + public StandardAnswer setValues(List values) + { + if(values == null || values.size() == 0) + { + throw new fflib_ApexMocks.ApexMocksException( + 'The stubbing is not correct, no return values have been set.'); + } + + ReturnValues.addAll(values); + return this; + } + + /** + * Standard basic implementation for the fflib_Answer answer method, to be used as default answering. + * @param invocation The invocation to answer for. + * @return The ReturnValue for the method stubbed. + */ + public Object answer(fflib_InvocationOnMock invocation) + { + if(ReturnValues == null || ReturnValues.size() == 0) + { + throw new fflib_ApexMocks.ApexMocksException( + 'The stubbing is not correct, no return values have been set.'); + } + + Integer returnValuesSize = ReturnValues.size()-1; + + if(whichStubReturnIndex < returnValuesSize) + { + return ReturnValues[whichStubReturnIndex++]; + } + else + { + return ReturnValues[returnValuesSize]; + } + } + } +} \ No newline at end of file diff --git a/sfdx-source/apex-mocks/fflib_MethodReturnValue.cls-meta.xml b/sfdx-source/apex-mocks/fflib_MethodReturnValue.cls-meta.xml new file mode 100644 index 00000000000..08c4dbd5cfa --- /dev/null +++ b/sfdx-source/apex-mocks/fflib_MethodReturnValue.cls-meta.xml @@ -0,0 +1,4 @@ + + + 52.0 + diff --git a/sfdx-source/apex-mocks/fflib_MethodReturnValueRecorder.cls b/sfdx-source/apex-mocks/fflib_MethodReturnValueRecorder.cls new file mode 100644 index 00000000000..fd9f37d5117 --- /dev/null +++ b/sfdx-source/apex-mocks/fflib_MethodReturnValueRecorder.cls @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2014, FinancialForce.com, inc + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the FinancialForce.com, inc nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @group Core + */ +public with sharing class fflib_MethodReturnValueRecorder +{ + public Boolean Stubbing { get; set; } + + public List DoThrowWhenExceptions { get; set; } + + /** + * Map of matchers by method. + * + * Key: qualifiedMethod + * Object: map of method return values by method. + */ + + private Map> matcherReturnValuesByMethod; + + public fflib_MethodReturnValue MethodReturnValue { get; private set; } + + public fflib_MethodReturnValueRecorder() + { + matcherReturnValuesByMethod = new Map>(); + + MethodReturnValue = null; + } + + /** + * Prepare a stubbed method return value. + * @param invocation The object holding all the data of the invocation, like the method and arguments and the mock instance. + * @return The MethodReturnValue instance. + */ + public fflib_MethodReturnValue prepareMethodReturnValue(fflib_InvocationOnMock invocation) + { + MethodReturnValue = new fflib_MethodReturnValue(); + + List matcherReturnValues = matcherReturnValuesByMethod.get(invocation.getMethod()); + if (matcherReturnValues == null) + { + matcherReturnValues = new List(); + matcherReturnValuesByMethod.put(invocation.getMethod(), matcherReturnValues); + } + + List argValues = invocation.getMethodArgValues().argValues; + + //Register explicit arg values as 'equals' matchers, to preserve old behaviour + if (!fflib_Match.Matching) + { + for (Object arg : argValues) + { + if (arg == null) + fflib_Match.isNull(); + else + fflib_Match.eq(arg); + } + } + + List matchers = fflib_Match.getAndClearMatchers(argValues.size()); + matcherReturnValues.add(new fflib_MatchersReturnValue(matchers, MethodReturnValue)); + + return MethodReturnValue; + } + + /** + * Get the method return value for the given method call. + * @param invocation The object holding all the data of the invocation, like the method and arguments and the mock instance. + * @return The MethodReturnValue instance. + */ + public fflib_MethodReturnValue getMethodReturnValue(fflib_InvocationOnMock invocation) + { + List matchersForMethods = matcherReturnValuesByMethod.get(invocation.getMethod()); + if (matchersForMethods != null) + { + for (Integer i = matchersForMethods.size() - 1; i >= 0; i--) + { + fflib_MatchersReturnValue matchersReturnValue = (fflib_MatchersReturnValue)matchersForMethods.get(i); + if (fflib_Match.matchesAllArgs(invocation.getMethodArgValues(), matchersReturnValue.matchers)) + { + return matchersReturnValue.ReturnValue; + } + } + } + + return null; + } + + /** + * Prepare a stubbed exceptions for a void method. + * @param exps The list of exception to throw. + */ + public void prepareDoThrowWhenExceptions(List exps) + { + DoThrowWhenExceptions = exps; + } +} \ No newline at end of file diff --git a/sfdx-source/apex-mocks/fflib_MethodReturnValueRecorder.cls-meta.xml b/sfdx-source/apex-mocks/fflib_MethodReturnValueRecorder.cls-meta.xml new file mode 100644 index 00000000000..08c4dbd5cfa --- /dev/null +++ b/sfdx-source/apex-mocks/fflib_MethodReturnValueRecorder.cls-meta.xml @@ -0,0 +1,4 @@ + + + 52.0 + diff --git a/sfdx-source/apex-mocks/fflib_MethodVerifier.cls b/sfdx-source/apex-mocks/fflib_MethodVerifier.cls new file mode 100644 index 00000000000..38aca8e0f22 --- /dev/null +++ b/sfdx-source/apex-mocks/fflib_MethodVerifier.cls @@ -0,0 +1,154 @@ +/* + Copyright (c) 2017 FinancialForce.com, inc. All rights reserved. + */ + +/** + * This class implements the actual verification. + * @group Core + */ +public abstract class fflib_MethodVerifier +{ + /** + * Verify a method was called on a mock object. + * @param mockInvocation The object holding all the data of the invocation, like the method and arguments and the mock instance. + * @param verificationMode The verification mode that holds the setting about how the verification should be performed. + */ + public void verifyMethodCall(fflib_InvocationOnMock mockInvocation, fflib_VerificationMode verificationMode) + { + validateMode(verificationMode); + + verify(mockinvocation.getMethod(), mockinvocation.getMethodArgValues(), verificationMode); + } + + /* + * Method that actually performs the verify + * @param qm The method to be verified. + * @param methodArg The arguments of the method that needs to be verified. + * @param verificationMode The verification mode that holds the setting about how the verification should be performed. + */ + protected abstract void verify( + fflib_QualifiedMethod qm, + fflib_MethodArgValues methodArg, + fflib_VerificationMode verificationMode); + + /* + * Method that validates the verification mode used in the verify. + * Not all the methods from the fflib_VerificationMode are implemented for the different classes that extends the fflib_MethodVerifier. + * The error is thrown at run time, so this method is called in the method that actually performs the verify. + * @param verificationMode The verification mode that has to have been verified. + * @throws Exception with message for the fflib_VerificationMode not implemented. + */ + protected abstract void validateMode(fflib_VerificationMode verificationMode); + + /* + * Method that performs the argument capturing. + * Captures argument values during verification. + * @param matchers The list of matcher with which a method is verified. + */ + protected void capture(List matchers) + { + for(fflib_IMatcher matcher : matchers) + { + if( matcher instanceof fflib_ArgumentCaptor.AnyObject ) + { + ((fflib_ArgumentCaptor.AnyObject)matcher).storeArgument(); + } + } + } + + protected void throwException( + fflib_QualifiedMethod qm, + String inOrder, + Integer expectedCount, + String qualifier, + Integer methodCount, + String customAssertMessage, + fflib_MethodArgValues expectedArguments, + List expectedMatchers, + List actualArguments) + { + String template = 'EXPECTED COUNT: {0}{1}{2}' // qualified expected count (e.g. "3 or fewer times in order") + + '\nACTUAL COUNT: {3}' // actual count + + '\nMETHOD: {4}' // method signature + + '{5}'; // custom assert message + + String expectedDescription = ''; + String actualDescription = ''; + + if (qm.hasArguments()) + { + template += '\n---' // separator + + '\nACTUAL ARGS: {6}' // actual args + + '\n---' // separator + + '\nEXPECTED ARGS: {7}'; // matcher descriptions + + if (expectedMatchers == null) + { + expectedDescription = describe(expectedArguments); + } + else + { + expectedDescription = describe(expectedMatchers); + } + actualDescription = describe(actualArguments); + } + + String message = String.format(template, new List{ + '' + expectedCount, + String.isBlank(qualifier) ? '' : ('' + qualifier), + inOrder, + '' + methodCount, + '' + qm, + String.isBlank(customAssertMessage) ? '' : ('\n' + customAssertMessage), + actualDescription, + expectedDescription + }); + + throw new fflib_ApexMocks.ApexMocksException(message); + } + + private static String describe(List matchers) + { + List descriptions = new List(); + for (fflib_IMatcher matcher : matchers) + { + descriptions.add('' + matcher); + } + + return String.join(descriptions, ', '); + } + + private static String describe(List valuesFromAllInvocations) + { + List descriptions = new List(); + if (valuesFromAllInvocations != null) + { + for (fflib_MethodArgValues valuesFromOneInvocation : valuesFromAllInvocations) + { + descriptions.add(describe(valuesFromOneInvocation)); + } + } + + return '(' + String.join(descriptions, '), (') + ')'; + } + + private static String describe(fflib_MethodArgValues values) + { + List descriptions = new List(); + for (Object value : values.argValues) + { + try + { + // Attempt to JSON serialize - that way it doesn't truncate SObject fields etc. + // Bear in mind that something are not JSON serializable, e.g. things with circular references. + descriptions.add(JSON.serialize(value)); + } + catch (Exception error) + { + descriptions.add('' + value); + } + } + + return String.join(descriptions, ', '); + } +} \ No newline at end of file diff --git a/sfdx-source/apex-mocks/fflib_MethodVerifier.cls-meta.xml b/sfdx-source/apex-mocks/fflib_MethodVerifier.cls-meta.xml new file mode 100644 index 00000000000..dd61d1f917e --- /dev/null +++ b/sfdx-source/apex-mocks/fflib_MethodVerifier.cls-meta.xml @@ -0,0 +1,5 @@ + + + 52.0 + Active + diff --git a/sfdx-source/apex-mocks/fflib_QualifiedMethod.cls b/sfdx-source/apex-mocks/fflib_QualifiedMethod.cls new file mode 100644 index 00000000000..521106992d0 --- /dev/null +++ b/sfdx-source/apex-mocks/fflib_QualifiedMethod.cls @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2016-2017 FinancialForce.com, inc. All rights reserved. + */ +public with sharing class fflib_QualifiedMethod +{ + public final Object mockInstance; + public final String typeName; + public final String methodName; + public final List methodArgTypes; + + public fflib_QualifiedMethod(String typeName, String methodName, List methodArgTypes) + { + this(typeName, methodName, methodArgTypes, null); + } + + public fflib_QualifiedMethod(String typeName, String methodName, List methodArgTypes, Object mockInstance) + { + this.mockInstance = mockInstance; + this.typeName = typeName; + this.methodName = methodName; + this.methodArgTypes = methodArgTypes; + } + + /** + * Standard equals override. + * @param other The object whose equality we are verifying + * @return Boolean True if meaningfully equivalent, false otherwise. + */ + public Boolean equals(Object other) + { + if (this === other) + { + return true; + } + + fflib_QualifiedMethod that = other instanceof fflib_QualifiedMethod ? (fflib_QualifiedMethod)other : null; + + return that != null + && (this.mockInstance === that.mockInstance || !fflib_ApexMocksConfig.HasIndependentMocks) + && this.typeName == that.typeName + && this.methodName == that.methodName + && this.methodArgTypes == that.methodArgTypes; + } + + /** + * Standard hashCode override. + * @return Integer The generated hashCode + */ + public Integer hashCode() + { + Integer prime = 31; + Integer result = 1; + + if (fflib_ApexMocksConfig.HasIndependentMocks) + { + result = prime * result + ((mockInstance == null) ? 0 : mockInstance.hashCode()); + } + result = prime * result + ((methodArgTypes == null) ? 0 : methodArgTypes.hashCode()); + result = prime * result + ((methodName == null) ? 0 : methodName.hashCode()); + result = prime * result + ((typeName == null) ? 0 : typeName.hashCode()); + + return result; + } + + /** + * Standard toString override. + * @return String The human friendly description of the method. + */ + public override String toString() + { + return typeName + '.' + methodName + methodArgTypes; + } + + /** + * Predicate describing whether the qualified method accepts arguments or not. + * @return True if the method accepts arguments. + */ + public Boolean hasArguments() + { + return this.methodArgTypes != null && !this.methodArgTypes.isEmpty(); + } +} \ No newline at end of file diff --git a/sfdx-source/apex-mocks/fflib_QualifiedMethod.cls-meta.xml b/sfdx-source/apex-mocks/fflib_QualifiedMethod.cls-meta.xml new file mode 100644 index 00000000000..08c4dbd5cfa --- /dev/null +++ b/sfdx-source/apex-mocks/fflib_QualifiedMethod.cls-meta.xml @@ -0,0 +1,4 @@ + + + 52.0 + diff --git a/sfdx-source/apex-mocks/fflib_QualifiedMethodAndArgValues.cls b/sfdx-source/apex-mocks/fflib_QualifiedMethodAndArgValues.cls new file mode 100644 index 00000000000..fb25c2e4c3a --- /dev/null +++ b/sfdx-source/apex-mocks/fflib_QualifiedMethodAndArgValues.cls @@ -0,0 +1,40 @@ +/* + Copyright (c) 2016-2017 FinancialForce.com, inc. All rights reserved. + */ + +/** + * @group Core + */ +public with sharing class fflib_QualifiedMethodAndArgValues +{ + private final fflib_QualifiedMethod qm; + private final fflib_MethodArgValues args; + private final Object mockInstance; + + public fflib_QualifiedMethodAndArgValues(fflib_QualifiedMethod qm, fflib_MethodArgValues args, Object mockInstance) + { + this.qm = qm; + this.args = args; + this.mockInstance = mockInstance; + } + + public fflib_QualifiedMethod getQualifiedMethod() + { + return qm; + } + + public fflib_MethodArgValues getMethodArgValues() + { + return args; + } + + public Object getMockInstance() + { + return mockInstance; + } + + public override String toString() + { + return qm + ' with args: [' + String.join(args.argValues, '],[') + ']'; + } +} \ No newline at end of file diff --git a/sfdx-source/apex-mocks/fflib_QualifiedMethodAndArgValues.cls-meta.xml b/sfdx-source/apex-mocks/fflib_QualifiedMethodAndArgValues.cls-meta.xml new file mode 100644 index 00000000000..dd61d1f917e --- /dev/null +++ b/sfdx-source/apex-mocks/fflib_QualifiedMethodAndArgValues.cls-meta.xml @@ -0,0 +1,5 @@ + + + 52.0 + Active + diff --git a/sfdx-source/apex-mocks/fflib_System.cls b/sfdx-source/apex-mocks/fflib_System.cls new file mode 100644 index 00000000000..476b81f44c8 --- /dev/null +++ b/sfdx-source/apex-mocks/fflib_System.cls @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2017 FinancialForce.com, inc. All rights reserved. + */ + +/** + * @group Core + * Contains counterparts for helper methods in the native System class. + */ + +public class fflib_System +{ + /** + * Verifies that the supplied argument is meaningfully equivalent to the expected argument, as defined by its matcher. + * See fflib_SystemTest for examples of usage. + * @param ignoredRetval Dummy value, returned on registering an fflib_IMatcher. + * @param value The object instance upon which we are checking equality. + */ + public static void assertEquals(Object ignoredRetval, Object value) + { + assertEquals(ignoredRetval, value, null); + } + + /** + * Verifies that the supplied argument is meaningfully equivalent to the expected argument, as defined by its matcher. + * See fflib_SystemTest for examples of usage. + * @param ignoredRetval Dummy value, returned on registering an fflib_IMatcher. + * @param value The object instance upon which we are checking equality. + * @param customAssertMessage Provides context or additional information for the assertion. + */ + public static void assertEquals(Object ignoredRetval, Object value, String customAssertMessage) + { + fflib_IMatcher matcher = null; + try + { + List matchers = fflib_Match.getAndClearMatchers(1); + matcher = matchers[0]; + } + catch (fflib_ApexMocks.ApexMocksException e) + { + throw new fflib_ApexMocks.ApexMocksException('fflib_System.assertEquals expects you to register exactly 1 fflib_IMatcher (typically through the helpers in fflib_Match).'); + } + + if (!matcher.matches(value)) + { + throw new fflib_ApexMocks.ApexMocksException(String.format('Expected : {0}, Actual: {1}{2}', new String[]{ + String.valueOf(matcher), + String.valueOf(value), + String.isBlank(customAssertMessage) ? '' : (' -- ' + customAssertMessage) + })); + } + } +} \ No newline at end of file diff --git a/sfdx-source/apex-mocks/fflib_System.cls-meta.xml b/sfdx-source/apex-mocks/fflib_System.cls-meta.xml new file mode 100644 index 00000000000..dd61d1f917e --- /dev/null +++ b/sfdx-source/apex-mocks/fflib_System.cls-meta.xml @@ -0,0 +1,5 @@ + + + 52.0 + Active + diff --git a/sfdx-source/apex-mocks/fflib_VerificationMode.cls b/sfdx-source/apex-mocks/fflib_VerificationMode.cls new file mode 100644 index 00000000000..1bf014b381b --- /dev/null +++ b/sfdx-source/apex-mocks/fflib_VerificationMode.cls @@ -0,0 +1,139 @@ +/* + Copyright (c) 2017 FinancialForce.com, inc. All rights reserved. + */ + +/** + * This class implements the verification modes with Mockito syntax style. + * It can be used in the classic verify and in the ordered verify. + * @group Core + */ +public with sharing class fflib_VerificationMode +{ + public Integer VerifyMin {get; set;} + public Integer VerifyMax {get; set;} + public String CustomAssertMessage { get; set; } + + public enum ModeName {times, atLeast, atMost, between, atLeastOnce, calls} + + public ModeName Method; + + public fflib_VerificationMode() + { + VerifyMin = 1; + VerifyMax = null; + CustomAssertMessage = null; + Method = null; + } + + /** + * Sets how many times the method is expected to be called. + * For InOrder verification we copy Mockito behavior which is as follows; + *
    + *
  • Consume the specified number of matching invocations, ignoring non-matching invocations in between
  • + *
  • Fail an assert if the very next invocation matches, but additional matches can still exist so long as at least one non-matching invocation exists before them
  • + *
+ * For example if you had a(); a(); b(); a(); + * then inOrder.verify(myMock, 2)).a(); or inOrder.verify(myMock, 3)).a(); would pass but not inOrder.verify(myMock, 1)).a(); + * @param times The number of times you expect the method to have been called. + * @return The fflib_VerificationMode object instance with the proper settings. + */ + public fflib_VerificationMode times(Integer times) + { + this.Method = ModeName.times; + this.VerifyMin = this.VerifyMax = times; + return this; + } + + /** + * Sets a custom assert message for the verify. + * @param customAssertMessage The custom message for the assert in case the assert is false. The custom message is queued to the default message. + * @return The fflib_VerificationMode object instance with the proper settings. + */ + public fflib_VerificationMode description(String customAssertMessage) + { + this.CustomAssertMessage = customAssertMessage; + return this; + } + + /** + * Sets the minimum number of times the method is expected to be called. + * With the InOrder verification it performs a greedy verification, which means it would consume all the instances of the method verified. + * @param atLeastTimes The minimum number of times you expect the method to have been called. + * @return The fflib_VerificationMode object instance with the proper settings. + */ + public fflib_VerificationMode atLeast(Integer atLeastTimes) + { + this.Method = ModeName.atLeast; + this.VerifyMin = atLeastTimes; + + return this; + } + + /** + * Sets the maximum number of times the method is expected to be called. Not available in the InOrder verification. + * @param atMostTimes The maximum number of times the method is expected to be called. + * @return The fflib_VerificationMode object instance with the proper settings. + */ + public fflib_VerificationMode atMost(Integer atMostTimes) + { + this.Method = ModeName.atMost; + this.VerifyMax = atMostTimes; + + return this; + } + + /** + * Sets that the method is called at least once. + * With the InOrder verification it performs a greedy verification, which means it would consume all the instances of the method verified. + * @return The fflib_VerificationMode object instance with the proper settings. + */ + public fflib_VerificationMode atLeastOnce() + { + this.Method = ModeName.atLeastOnce; + this.VerifyMin = 1; + + return this; + } + + /** + * Sets the range of how many times the method is expected to be called. Not available in the InOrder verification. + * @param atLeastTimes The minimum number of times you expect the method to have been called. + * @param atMostTimes The maximum number of times the method is expected to be called. + * @return The fflib_VerificationMode object instance with the proper settings. + */ + public fflib_VerificationMode between(Integer atLeastTimes, Integer atMostTimes) + { + this.Method = ModeName.between; + this.VerifyMin = atLeastTimes; + this.VerifyMax = atMostTimes; + + return this; + } + + /** + * Sets that the method is not expected to be called. + * @return The fflib_VerificationMode object instance with the proper settings. + */ + public fflib_VerificationMode never() + { + this.VerifyMin = fflib_ApexMocks.NEVER; + this.VerifyMax = fflib_ApexMocks.NEVER; + + return this; + } + + /** + * Sets how many times the method is expected to be called for an InOrder verifier. Available Only with the InOrder verification. + * A verification mode using calls will not fail if the method is called more times than expected. + * @param callingTimes The number of times you expect the method to have been called in the InOrder verifying (no greedy verify). + * @return The fflib_VerificationMode object instance with the proper settings. + */ + public fflib_VerificationMode calls(Integer callingTimes) + { + this.Method = ModeName.calls; + this.VerifyMin = callingTimes; + this.VerifyMax = null; + + return this; + } +} \ No newline at end of file diff --git a/sfdx-source/apex-mocks/fflib_VerificationMode.cls-meta.xml b/sfdx-source/apex-mocks/fflib_VerificationMode.cls-meta.xml new file mode 100644 index 00000000000..dd61d1f917e --- /dev/null +++ b/sfdx-source/apex-mocks/fflib_VerificationMode.cls-meta.xml @@ -0,0 +1,5 @@ + + + 52.0 + Active + diff --git a/sfdx-source/apex-mocks/tests/fflib_AnswerTest.cls b/sfdx-source/apex-mocks/tests/fflib_AnswerTest.cls new file mode 100644 index 00000000000..63463099e04 --- /dev/null +++ b/sfdx-source/apex-mocks/tests/fflib_AnswerTest.cls @@ -0,0 +1,438 @@ +/* + Copyright (c) 2017 FinancialForce.com, inc. All rights reserved. + */ + +/** + * @nodoc + */ +@isTest +private class fflib_AnswerTest +{ + + private static fflib_InvocationOnMock actualInvocation = null; + + @isTest + static void thatAnswersWithException() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + mocks.startStubbing(); + mocks.when(mockList.get2(0, 'Hi hi Hello Hi hi')).thenAnswer(new fflib_AnswerTest.ExceptionForAnswer()); + mocks.stopStubbing(); + + // When + try + { + mockList.get2(0, 'Hi hi Hello Hi hi'); + System.assert(false, 'an exception is expected to be thrown on the answer execution'); + } + catch(fflib_ApexMocks.ApexMocksException ansExpt) + { + String expectedMessage = 'an error occurs on the execution of the answer'; + // Then + System.assertEquals(expectedMessage, ansExpt.getMessage(), 'the message from the answer is not as expected'); + } + } + + @isTest + static void thatStoresMethodIntoInvocationOnMock() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + mocks.startStubbing(); + mocks.when(mockList.get2(0, 'Hi hi Hello Hi hi')).thenAnswer(new fflib_AnswerTest.BasicAnswer()); + mocks.stopStubbing(); + + // When + mockList.get2(0, 'Hi hi Hello Hi hi'); + + // Then + + Object methodCalled = actualInvocation.getMethod(); + System.assert(methodCalled instanceof fflib_QualifiedMethod, 'the object returned is not a method as expected'); + + String expectedMethodSignature = fflib_MyList.getStubClassName() + '.get2(Integer, String)'; + System.assertEquals(expectedMethodSignature, ((fflib_QualifiedMethod)methodCalled).toString(), ' the method is no the one expected'); + } + + @isTest + static void thatAnswerOnlyForTheMethodStubbedWithAnswer() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + mocks.startStubbing(); + mocks.when(mockList.get(3)).thenReturn('ted'); + mocks.when(mockList.get2(0, 'Hi hi Hello Hi hi')).thenAnswer(new fflib_AnswerTest.BasicAnswer()); + mocks.stopStubbing(); + + // When + mockList.add('one'); + String noAnswered = mockList.get(3); + mockList.get2(0, 'Hi hi Hello Hi hi'); + + // Then + Object methodCalled = actualInvocation.getMethod(); + System.assert(methodCalled instanceof fflib_QualifiedMethod, 'the object returned is not a method as expected'); + + String expectedMethodSignature = fflib_MyList.getStubClassName() + '.get2(Integer, String)'; + System.assertEquals(expectedMethodSignature, ((fflib_QualifiedMethod)methodCalled).toString(), ' the method is no the one expected'); + + System.assertEquals('ted', noAnswered, 'the get method should have returned the stubbed string'); + } + + @isTest + static void thatMultipleAnswersAreHandled() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + mocks.startStubbing(); + mocks.when(mockList.get(3)).thenAnswer(new fflib_AnswerTest.FirstAnswer()); + mocks.when(mockList.get2(0, 'Hi hi Hello Hi hi')).thenAnswer(new fflib_AnswerTest.SecondAnswer()); + mocks.stopStubbing(); + + // When + mockList.add('one'); + String answer1 = mockList.get(3); + String answer2 = mockList.get2(0, 'Hi hi Hello Hi hi'); + + System.assertEquals('this is the first answer', answer1, 'the answer wasnt the one expected'); + System.assertEquals('and this is the second one', answer2, 'the answer wasnt the one expected'); + } + + @isTest + static void thatStoresMockInstanceIntoInvocationOnMock() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + mocks.startStubbing(); + mocks.when(mockList.get2(0, 'Hi hi Hello Hi hi')).thenAnswer(new fflib_AnswerTest.BasicAnswer()); + mocks.stopStubbing(); + + // When + String mockCalled = mockList.get2(0, 'Hi hi Hello Hi hi'); + + // Then + System.assert(actualInvocation.getMock() instanceof fflib_MyList.IList, 'the object returned is not a mock instance as expected'); + System.assertEquals(mockList, actualInvocation.getMock(), 'the mock returned should be the mockList used in the stubbing'); + } + + @isTest + static void thatMethodsParametersAreAccessible() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + mocks.startStubbing(); + mocks.when(mockList.get2(0, 'Hi hi Hello Hi hi')).thenAnswer(new fflib_AnswerTest.ProcessArgumentAnswer()); + mocks.stopStubbing(); + + // When + String actualValue = mockList.get2(0, 'Hi hi Hello Hi hi'); + + // Then + System.assertEquals('Bye hi Hello Bye hi', actualValue, 'the answer is not correct'); + } + + @isTest + static void thatAnswerOnlyForTheStubbedParameter() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + mocks.startStubbing(); + mocks.when(mockList.get2(0, 'Hi hi Hello Hi hi')).thenAnswer(new fflib_AnswerTest.ProcessArgumentAnswer()); + mocks.stopStubbing(); + + // When + String actualValue1 = mockList.get2(0, 'some string for my method'); + String actualValue2 = mockList.get2(0, 'Hi hi Hello Hi hi'); + String actualValue3 = mockList.get2(0, 'another string for the same method'); + + // Then + System.assertEquals('Bye hi Hello Bye hi', actualValue2, 'the answer is not correct'); + System.assertEquals(null, actualValue1, 'the answer is not correct'); + System.assertEquals(null, actualValue3, 'the answer is not correct'); + } + + @isTest + static void thatMethodsParametersAreAccessibleWhenCalledWithMatchers() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + mocks.startStubbing(); + mocks.when(mockList.get2(fflib_Match.anyInteger(), fflib_Match.anyString())).thenAnswer(new fflib_AnswerTest.ProcessArgumentAnswer()); + mocks.stopStubbing(); + + // When + String actualValue = mockList.get2(0, 'Hi hi Hello Hi hi'); + + // Then + System.assertEquals('Bye hi Hello Bye hi', actualValue, 'the answer is not correct'); + } + + @isTest + static void thatExceptionIsThrownWhenAccessOutOfIndexArgument() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + mocks.startStubbing(); + mocks.when(mockList.get2(0, 'Hi hi Hello Hi hi')).thenAnswer(new fflib_AnswerTest.ExceptionForArgumentsOutOfBound()); + mocks.stopStubbing(); + + // When + String actualValue = mockList.get2(0, 'Hi hi Hello Hi hi'); + } + + @isTest + static void thatExceptionIsThrownWhenAccessNegativeIndexArgument() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + mocks.startStubbing(); + mocks.when(mockList.get2(0, 'Hi hi Hello Hi hi')).thenAnswer(new fflib_AnswerTest.ExceptionForNegativeArgumentIndex()); + mocks.stopStubbing(); + + // When + String actualValue = mockList.get2(0, 'Hi hi Hello Hi hi'); + } + + @isTest + static void thatArgumentListEmptyForMethodWithNoArgument() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + mocks.startStubbing(); + mocks.when(mockList.isEmpty()).thenAnswer(new fflib_AnswerTest.ArgumentListEmptyForMethodWithNoArgument()); + mocks.stopStubbing(); + + // When + Boolean actualValue = mockList.isEmpty(); + } + + @isTest + static void thatAnswerToVoidMethod() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + mocks.startStubbing(); + ((fflib_MyList) mocks.doAnswer(new fflib_AnswerTest.BasicAnswer(), mockList)).addMore('Hi hi Hello Hi hi'); + mocks.stopStubbing(); + + // When + mockList.addMore('Hi hi Hello Hi hi'); + + // Then + Object methodCalled = actualInvocation.getMethod(); + System.assert(methodCalled instanceof fflib_QualifiedMethod, 'the object returned is not a method as expected'); + + String expectedMethodSignature = fflib_MyList.getStubClassName() + '.addMore(String)'; + System.assertEquals(expectedMethodSignature, ((fflib_QualifiedMethod)methodCalled).toString(), 'Unexpected method name: ' + methodCalled); + } + + @isTest + static void thatAnswerToVoidAndNotVoidMethods() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + mocks.startStubbing(); + ((fflib_MyList) mocks.doAnswer(new fflib_AnswerTest.FirstAnswer(), mockList)).get(3); + ((fflib_MyList) mocks.doAnswer(new fflib_AnswerTest.BasicAnswer(), mockList)).addMore('Hi hi Hello Hi hi'); + ((fflib_MyList) mocks.doAnswer(new fflib_AnswerTest.SecondAnswer(), mockList)).get2(4, 'Hi hi Hello Hi hi'); + mocks.stopStubbing(); + + // When + String answer1 = mockList.get(3); + String answer2 = mockList.get2(4, 'Hi hi Hello Hi hi'); + mockList.addMore('Hi hi Hello Hi hi'); + + // Then + Object methodCalled = actualInvocation.getMethod(); + System.assert(methodCalled instanceof fflib_QualifiedMethod, 'the object returned is not a method as expected'); + + String expectedMethodSignature = fflib_MyList.getStubClassName() + '.addMore(String)'; + System.assertEquals(expectedMethodSignature, ((fflib_QualifiedMethod)methodCalled).toString(), + 'the last method called should be the addMore, so should be the last to set the actualInvocation variable.'); + + System.assertEquals('this is the first answer', answer1, 'the answer was not the one expected'); + System.assertEquals('and this is the second one', answer2, 'the answer was not the one expected'); + } + + @isTest + static void thatAnswerToDifferentVoidMethods() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + fflib_AnswerTest.FirstAnswer answer1 = new fflib_AnswerTest.FirstAnswer(); + fflib_AnswerTest.SecondAnswer answer2 = new fflib_AnswerTest.SecondAnswer(); + + System.assertEquals(null, answer1.getMessage(), 'the answer message should be null at this stage'); + System.assertEquals(null, answer2.getMessage(), 'the answer message should be null at this stage'); + + mocks.startStubbing(); + ((fflib_MyList) mocks.doAnswer(answer1, mockList)).addMore('Hi hi Hello Hi hi'); + ((fflib_MyList) mocks.doAnswer(answer2, mockList)).add('Hello'); + mocks.stopStubbing(); + + // When + mockList.addMore('Hi hi Hello Hi hi'); + mockList.add('Hello'); + + // Then + System.assertEquals('this is the first answer', answer1.getMessage(), 'the answer was not the one expected'); + System.assertEquals('and this is the second one', answer2.getMessage(), 'the answer was not the one expected'); + } + + //Answers + + public class BasicAnswer implements fflib_Answer + { + public Object answer(fflib_InvocationOnMock invocation) + { + actualInvocation = invocation; + return null; + } + } + + public class ExceptionForAnswer implements fflib_Answer + { + public Object answer(fflib_InvocationOnMock invocation) + { + actualInvocation = invocation; + + throw new fflib_ApexMocks.ApexMocksException('an error occurs on the execution of the answer'); + } + } + + public class ExceptionForArgumentsOutOfBound implements fflib_Answer + { + public Object answer(fflib_InvocationOnMock invocation) + { + actualInvocation = invocation; + + try + { + Object noExistingObj = invocation.getArgument(2); + System.assert(false, 'an exception was expected because the argument in the method are only 2'); + } + catch(fflib_ApexMocks.ApexMocksException exp) + { + String expectedMessage = 'Invalid index, must be greater or equal to zero and less of 2.'; + String actualMessage = exp.getMessage(); + System.assertEquals(expectedMessage, actualMessage, 'the message return by the exception is not as expected'); + } + return null; + } + } + + public class ExceptionForNegativeArgumentIndex implements fflib_Answer + { + public Object answer(fflib_InvocationOnMock invocation) + { + actualInvocation = invocation; + + try + { + Object noExistingObj = invocation.getArgument(-1); + System.assert(false, 'an exception was expected because the argument index cannot be negative'); + } + catch(fflib_ApexMocks.ApexMocksException exp) + { + String expectedMessage = 'Invalid index, must be greater or equal to zero and less of 2.'; + String actualMessage = exp.getMessage(); + System.assertEquals(expectedMessage, actualMessage, 'the message return by the exception is not as expected'); + } + return null; + } + } + + public class ArgumentListEmptyForMethodWithNoArgument implements fflib_Answer + { + public Object answer(fflib_InvocationOnMock invocation) + { + actualInvocation = invocation; + + List emptyList = invocation.getArguments(); + + System.assertEquals(0, emptyList.size(), 'the argument list from a method without arguments should be empty'); + + return null; + } + } + + public class FirstAnswer implements fflib_Answer + { + private String answerMessage; + + public String getMessage() + { + return this.answerMessage; + } + + public Object answer(fflib_InvocationOnMock invocation) + { + actualInvocation = invocation; + + this.answerMessage = 'this is the first answer'; + + return answerMessage; + } + } + + public class SecondAnswer implements fflib_Answer + { + private String answerMessage; + + public String getMessage() + { + return this.answerMessage; + } + + public Object answer(fflib_InvocationOnMock invocation) + { + actualInvocation = invocation; + + this.answerMessage = 'and this is the second one'; + + return answerMessage; + } + } + + public class ProcessArgumentAnswer implements fflib_Answer + { + public Object answer(fflib_InvocationOnMock invocation) + { + actualInvocation = invocation; + + String argument = (String) invocation.getArgument(1); + System.assertNotEquals(null, argument, ' the argument should have some value'); + argument = argument.replace('Hi', 'Bye'); + return argument; + } + } +} \ No newline at end of file diff --git a/sfdx-source/apex-mocks/tests/fflib_AnswerTest.cls-meta.xml b/sfdx-source/apex-mocks/tests/fflib_AnswerTest.cls-meta.xml new file mode 100644 index 00000000000..dd61d1f917e --- /dev/null +++ b/sfdx-source/apex-mocks/tests/fflib_AnswerTest.cls-meta.xml @@ -0,0 +1,5 @@ + + + 52.0 + Active + diff --git a/sfdx-source/apex-mocks/tests/fflib_AnyOrderTest.cls b/sfdx-source/apex-mocks/tests/fflib_AnyOrderTest.cls new file mode 100644 index 00000000000..5651b31f48f --- /dev/null +++ b/sfdx-source/apex-mocks/tests/fflib_AnyOrderTest.cls @@ -0,0 +1,1195 @@ +/* + Copyright (c) 2017 FinancialForce.com, inc. All rights reserved. + */ + +/** + * @nodoc + */ +@isTest +private class fflib_AnyOrderTest +{ + /* + * replicating the apex mocks tests with the new syntax + */ + + @isTest + private static void whenVerifyMultipleCallsWithMatchersShouldReturnCorrectMethodCallCounts() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('bob'); + mockList.add('fred'); + + // Then + ((fflib_MyList.IList) mocks.verify(mockList, mocks.times(2))).add(fflib_Match.anyString()); + ((fflib_MyList.IList) mocks.verify(mockList)).add('fred'); + ((fflib_MyList.IList) mocks.verify(mockList)).add(fflib_Match.stringContains('fred')); + } + + @isTest + private static void whenVerifyWithCombinedMatchersShouldReturnCorrectMethodCallCounts() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('bob'); + mockList.add('fred'); + + // Then + ((fflib_MyList.IList) mocks.verify(mockList, mocks.never())).add( + (String)fflib_Match.allOf(fflib_Match.eq('bob'), fflib_Match.stringContains('re')) + ); + + ((fflib_MyList.IList) mocks.verify(mockList)).add( + (String)fflib_Match.allOf(fflib_Match.eq('fred'), fflib_Match.stringContains('re')) + ); + + ((fflib_MyList.IList) mocks.verify(mockList, mocks.times(2))).add( + (String)fflib_Match.anyOf(fflib_Match.eq('bob'), fflib_Match.eq('fred')) + ); + + ((fflib_MyList.IList) mocks.verify(mockList)).add( + (String)fflib_Match.anyOf(fflib_Match.eq('bob'), fflib_Match.eq('jack')) + ); + + ((fflib_MyList.IList) mocks.verify(mockList, mocks.times(2))).add( + (String)fflib_Match.noneOf(fflib_Match.eq('jack'), fflib_Match.eq('tim')) + ); + + ((fflib_MyList.IList) mocks.verify(mockList, mocks.times(2))).add( + (String)fflib_Match.noneOf( + fflib_Match.anyOf(fflib_Match.eq('jack'), fflib_Match.eq('jill')), + fflib_Match.allOf(fflib_Match.eq('tim'), fflib_Match.stringContains('i')) + ) + ); + + ((fflib_MyList.IList) mocks.verify(mockList, mocks.times(2))).add( + (String)fflib_Match.isNot(fflib_Match.eq('jack')) + ); + } + + @isTest + private static void whenVerifyCustomMatchersCanBeUsed() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.get(1); + mockList.get(2); + mockList.get(3); + mockList.get(4); + mockList.get(5); + + // Then + ((fflib_MyList.IList) mocks.verify(mockList, mocks.times(3))).get((Integer)fflib_Match.matches(new isOdd())); + ((fflib_MyList.IList) mocks.verify(mockList, mocks.times(2))).get((Integer)fflib_Match.matches(new isEven())); + } + + @isTest + private static void verifyMultipleMethodCallsWithSameSingleArgument() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('bob'); + mockList.add('bob'); + + // Then + ((fflib_MyList.IList) mocks.verify(mockList, mocks.times(2))).add('bob'); + } + + @isTest + private static void verifyMethodNotCalled() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.get(0); + + // Then + ((fflib_MyList.IList) mocks.verify(mockList, mocks.never())).add('bob'); + ((fflib_MyList.IList) mocks.verify(mockList)).get(0); + } + + @isTest + private static void verifySingleMethodCallWithMultipleArguments() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.set(0, 'bob'); + + // Then + ((fflib_MyList.IList) mocks.verify(mockList)).set(0, 'bob'); + ((fflib_MyList.IList) mocks.verify(mockList, mocks.never())).set(0, 'fred'); + } + + @isTest + private static void verifyMethodCallWhenNoCallsBeenMadeForType() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // Then + ((fflib_MyList.IList) mocks.verify(mockList, mocks.never())).add('bob'); + } + + @isTest + private static void whenVerifyMethodNeverCalledMatchersAreReset() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('bob'); + + // Then + ((fflib_MyList.IList) mocks.verify(mockList, mocks.never())).get(fflib_Match.anyInteger()); + ((fflib_MyList.IList) mocks.verify(mockList)).add(fflib_Match.anyString()); + } + + /* + * times + */ + + @isTest + private static void verifyTimesMethodHasBeenCalled() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('bob'); + mockList.add('bob'); + mockList.add('bob'); + + // Then + ((fflib_MyList.IList) mocks.verify(mockList, mocks.times(3))).add('bob'); + } + + @isTest + private static void verifyTimesMethodHasBeenCalledWithMatchers() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('bob1'); + mockList.add('bob2'); + mockList.add('bob3'); + + // Then + ((fflib_MyList.IList) mocks.verify(mockList, mocks.times(3))).add(fflib_Match.anyString()); + } + + @isTest + private static void thatVerifyTimesMethodFailsWhenCalledLessTimes() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('bob'); + mockList.add('bob'); + mockList.add('bob'); + + // Then + try + { + ((fflib_MyList.IList) mocks.verify(mockList, mocks.times(4))).add('bob'); + + System.assert(false, 'an exception was expected'); + } + catch(Exception exc) + { + assertFailMessage(exc.getMessage(), 4, 3); + } + } + + @isTest + private static void thatVerifyTimesMethodFailsWhenCalledMoreTimes() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('bob'); + mockList.add('bob'); + mockList.add('bob'); + + // Then + try + { + ((fflib_MyList.IList) mocks.verify(mockList, mocks.times(2))).add('bob'); + + System.assert(false, 'an exception was expected'); + } + catch(Exception exc) + { + assertFailMessage(exc.getMessage(), 2, 3); + } + } + + @isTest + private static void thatVerifyTimesMethodFailsWhenCalledLessTimesWithMatchers() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('bob'); + mockList.add('bob'); + mockList.add('bob'); + + // Then + try + { + ((fflib_MyList.IList) mocks.verify(mockList, mocks.times(4))).add(fflib_Match.anyString()); + + System.assert(false, 'an exception was expected'); + } + catch(Exception exc) + { + assertFailMessage(exc.getMessage(), 4, 3); + } + } + + @isTest + private static void thatVerifyTimesMethodFailsWhenCalledMoreTimesWithMatchers() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('bob'); + mockList.add('bob'); + mockList.add('bob'); + + // Then + try + { + ((fflib_MyList.IList) mocks.verify(mockList, mocks.times(2))).add(fflib_Match.anyString()); + + System.assert(false, 'an exception was expected'); + } + catch(Exception exc) + { + assertFailMessage(exc.getMessage(), 2, 3); + } + } + + /* + * description + */ + + @isTest + private static void thatCustomMessageIsAdded() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('bob'); + mockList.add('bob'); + mockList.add('bob'); + + String customAssertMessage = 'Custom message to explain the reason of the verification'; + + // Then + try + { + ((fflib_MyList.IList) mocks.verify(mockList, mocks.times(2).description(customAssertMessage))).add(fflib_Match.anyString()); + + System.assert(false, 'an exception was expected'); + } + catch(Exception exc) + { + System.assertEquals('EXPECTED COUNT: 2' + + '\nACTUAL COUNT: 3' + + '\nMETHOD: fflib_MyList__sfdc_ApexStub.add(String)' + + '\nCustom message to explain the reason of the verification' + + '\n---' + + '\nACTUAL ARGS: ("bob"), ("bob"), ("bob")' + + '\n---' + + '\nEXPECTED ARGS: [any String]', + exc.getMessage(), + 'Unexpected verify fail message'); + } + } + + /* + * atLeast + */ + + @isTest + private static void thatVerifiesAtLeastNumberOfTimes() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('bob'); + mockList.add('fred'); + mockList.add('bob'); + mockList.add('bob'); + + // Then + ((fflib_MyList.IList) mocks.verify(mockList, mocks.atLeast(2))).add('bob'); + } + + @isTest + private static void thatVerifiesAtLeastNumberOfTimesWhenIsCalledMoreTimes() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('bob'); + mockList.add('fred'); + mockList.add('bob'); + mockList.add('fred'); + mockList.add('bob'); + mockList.add('fred'); + + // Then + ((fflib_MyList.IList) mocks.verify(mockList, mocks.atLeast(2))).add('bob'); + } + + @isTest + private static void thatThrownExceptionIfCalledLessThanAtLeastNumberOfTimes() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('bob'); + mockList.add('fred'); + mockList.add('bob'); + + // Then + try + { + ((fflib_MyList.IList) mocks.verify(mockList, mocks.atLeast(3))).add('bob'); + + System.assert(false, 'an exception was expected because we are asserting that the method is called 3 times when instead is called only twice'); + } + catch(fflib_ApexMocks.ApexMocksException ex) + { + System.assertEquals('EXPECTED COUNT: 3 or more times' + + '\nACTUAL COUNT: 2' + + '\nMETHOD: fflib_MyList__sfdc_ApexStub.add(String)' + + '\n---' + + '\nACTUAL ARGS: ("bob"), ("fred"), ("bob")' + + '\n---' + + '\nEXPECTED ARGS: "bob"', + ex.getMessage(), + 'Unexpected verify fail message'); + } + } + + @isTest + private static void thatVerifiesAtLeastNumberOfTimesWithMatchers() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('bob'); + mockList.add('fred'); + + // Then + ((fflib_MyList.IList) mocks.verify(mockList, mocks.atLeast(2))).add(fflib_Match.anyString()); + } + + @isTest + private static void thatVerifiesAtLeastNumberOfTimesWhenIsCalledMoreTimesWithMatchers() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('bob'); + mockList.add('fred'); + mockList.add('fred'); + mockList.add('fred'); + + // Then + ((fflib_MyList.IList) mocks.verify(mockList, mocks.atLeast(2))).add(fflib_Match.anyString()); + } + + @isTest + private static void thatThrownExceptionIfCalledLessThanAtLeastNumberOfTimesWithMatchers() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('bob'); + mockList.add('fred'); + + // Then + try + { + ((fflib_MyList.IList) mocks.verify(mockList, mocks.atLeast(3))).add(fflib_Match.anyString()); + + System.assert(false, 'an exception was expected because we are asserting that the method is called 3 times when instead is called only twice'); + } + catch(fflib_ApexMocks.ApexMocksException ex) + { + System.assertEquals('EXPECTED COUNT: 3 or more times' + + '\nACTUAL COUNT: 2' + + '\nMETHOD: fflib_MyList__sfdc_ApexStub.add(String)' + + '\n---' + + '\nACTUAL ARGS: ("bob"), ("fred")' + + '\n---' + + '\nEXPECTED ARGS: [any String]', + ex.getMessage(), + 'Unexpected verify fail message'); + } + } + + /* + * atMost + */ + + @isTest + private static void thatVerifiesAtMostNumberOfTimes() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('bob'); + mockList.add('fred'); + mockList.add('bob'); + mockList.add('bob'); + mockList.add('fred'); + + // Then + ((fflib_MyList.IList) mocks.verify(mockList, mocks.atMost(5))).add('bob'); + } + + @isTest + private static void thatVerifiesAtMostSameNumberOfTimes() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('bob'); + mockList.add('fred'); + mockList.add('bob'); + mockList.add('bob'); + mockList.add('fred'); + + // Then + ((fflib_MyList.IList) mocks.verify(mockList, mocks.atMost(3))).add('bob'); + } + + @isTest + private static void thatThrownExceptionIfCalledMoreThanAtMostNumberOfTimes() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('bob'); + mockList.add('fred'); + mockList.add('fred'); + mockList.add('bob'); + mockList.add('bob'); + mockList.add('bob'); + mockList.add('fred'); + + // Then + try + { + ((fflib_MyList.IList) mocks.verify(mockList, mocks.atMost(3))).add('bob'); + + System.assert(false, 'an exception was expected because we are asserting that the method is called 3 times when instead is called four times'); + } + catch(fflib_ApexMocks.ApexMocksException ex) + { + System.assertEquals('EXPECTED COUNT: 3 or fewer times' + + '\nACTUAL COUNT: 4' + + '\nMETHOD: fflib_MyList__sfdc_ApexStub.add(String)' + + '\n---' + + '\nACTUAL ARGS: ("bob"), ("fred"), ("fred"), ("bob"), ("bob"), ("bob"), ("fred")' + + '\n---' + + '\nEXPECTED ARGS: "bob"', + ex.getMessage(), + 'Unexpected verify fail message'); + } + } + + @isTest + private static void thatVerifiesAtMostNumberOfTimesWithMatchers() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('bob'); + mockList.add('fred'); + mockList.add('fred'); + + // Then + ((fflib_MyList.IList) mocks.verify(mockList, mocks.atMost(5))).add(fflib_Match.anyString()); + } + + @isTest + private static void thatVerifiesAtMostSameNumberOfTimesWithMatchers() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('bob'); + mockList.add('fred'); + mockList.add('fred'); + + // Then + ((fflib_MyList.IList) mocks.verify(mockList, mocks.atMost(3))).add(fflib_Match.anyString()); + } + + @isTest + private static void thatThrownExceptionIfCalledMoreThanAtMostNumberOfTimesWithMatchers() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('bob'); + mockList.add('fred'); + mockList.add('fred'); + mockList.add('fred'); + + // Then + try + { + ((fflib_MyList.IList) mocks.verify(mockList, mocks.atMost(3))).add(fflib_Match.anyString()); + + System.assert(false, 'an exception was expected because we are asserting that the method is called 3 times when instead is called four times'); + } + catch(fflib_ApexMocks.ApexMocksException ex) + { + System.assertEquals('EXPECTED COUNT: 3 or fewer times' + + '\nACTUAL COUNT: 4' + + '\nMETHOD: fflib_MyList__sfdc_ApexStub.add(String)' + + '\n---' + + '\nACTUAL ARGS: ("bob"), ("fred"), ("fred"), ("fred")' + + '\n---' + + '\nEXPECTED ARGS: [any String]' + , ex.getMessage(), + 'Unexpected verify fail message'); + } + } + + /* + * atLeastOnce + */ + + @isTest + private static void thatVerifiesAtLeastOnceNumberOfTimes() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('bob'); + mockList.add('fred'); + mockList.add('fred'); + + // Then + ((fflib_MyList.IList) mocks.verify(mockList, mocks.atLeastOnce())).add('bob'); + } + + @isTest + private static void thatVerifiesAtLeastOnceNumberOfTimesWhenIsCalledMoreTimes() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('bob'); + mockList.add('fred'); + mockList.add('bob'); + mockList.add('fred'); + + // Then + ((fflib_MyList.IList) mocks.verify(mockList, mocks.atLeastOnce())).add('bob'); + } + + @isTest + private static void thatThrownExceptionIfCalledLessThanAtLeastOnceNumberOfTimes() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('rob'); + mockList.add('fred'); + + // Then + try + { + ((fflib_MyList.IList) mocks.verify(mockList, mocks.atLeastOnce())).add('bob'); + + System.assert(false, 'an exception was expected because we are asserting that the method is called at least once when instead is never called'); + } + catch(fflib_ApexMocks.ApexMocksException ex) + { + System.assertEquals('EXPECTED COUNT: 1 or more times' + + '\nACTUAL COUNT: 0' + + '\nMETHOD: fflib_MyList__sfdc_ApexStub.add(String)' + + '\n---' + + '\nACTUAL ARGS: ("rob"), ("fred")' + + '\n---' + + '\nEXPECTED ARGS: "bob"', + ex.getmessage(), + 'Unexpected verify fail message'); + } + } + + @isTest + private static void thatVerifiesAtLeastOnceNumberOfTimesWithMatchers() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('bob'); + mockList.add('fred', 'fred', 'fred', 'fred'); + + // Then + ((fflib_MyList.IList) mocks.verify(mockList, mocks.atLeastOnce())).add(fflib_Match.anyString()); + } + + @isTest + private static void thatVerifiesAtLeastOnceNumberOfTimesWhenIsCalledMoreTimesWithMatchers() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('bob'); + mockList.add('fred', 'fred', 'fred', 'fred'); + mockList.add('bob'); + mockList.add('fred', 'fred', 'fred', 'fred'); + mockList.add('bob'); + mockList.add('fred', 'fred', 'fred', 'fred'); + + // Then + ((fflib_MyList.IList) mocks.verify(mockList, mocks.atLeastOnce())).add(fflib_Match.anyString()); + } + + @isTest + private static void thatThrownExceptionIfCalledLessThanAtLeastOnceNumberOfTimesWithMatchers() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('fred', 'fred', 'fred', 'fred'); + mockList.add('fred', 'fred', 'fred', 'fred'); + mockList.add('fred', 'fred', 'fred', 'fred'); + + // Then + try + { + ((fflib_MyList.IList) mocks.verify(mockList, mocks.atLeastOnce())).add(fflib_Match.anyString()); + + System.assert(false, 'an exception was expected because we are asserting that the method is called at lest once when instead is never called'); + } + catch(fflib_ApexMocks.ApexMocksException ex) + { + System.assertEquals('EXPECTED COUNT: 1 or more times' + + '\nACTUAL COUNT: 0' + + '\nMETHOD: fflib_MyList__sfdc_ApexStub.add(String)' + + '\n---' + + '\nACTUAL ARGS: ()' + + '\n---' + + '\nEXPECTED ARGS: [any String]', + ex.getMessage(), + 'Unexpected verify fail message'); + } + } + + /* + * between + */ + + @isTest + private static void thatVerifiesBetweenNumberOfTimes() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('bob'); + mockList.add('fred'); + mockList.add('bob'); + mockList.add('fred'); + mockList.add('bob'); + mockList.add('fred'); + mockList.add('bob'); + mockList.add('fred'); + + // Then + ((fflib_MyList.IList) mocks.verify(mockList, mocks.between(3, 5))).add('bob'); + } + + @isTest + private static void thatBetweenThrownExceptionIfCalledLessThanAtLeastNumberOfTimes() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('bob'); + mockList.add('fred'); + mockList.add('bob'); + + // Then + try + { + ((fflib_MyList.IList) mocks.verify(mockList, mocks.between(3, 5))).add('bob'); + + System.assert(false, 'an exception was expected because we are asserting that the method is called at least 3 times when instead is called only twice'); + } + catch(fflib_ApexMocks.ApexMocksException ex) + { + System.assertEquals('EXPECTED COUNT: 3 or more times' + + '\nACTUAL COUNT: 2' + + '\nMETHOD: fflib_MyList__sfdc_ApexStub.add(String)' + + '\n---' + + '\nACTUAL ARGS: ("bob"), ("fred"), ("bob")' + + '\n---' + + '\nEXPECTED ARGS: "bob"', + ex.getMessage() + ); + } + } + + @isTest + private static void thatVerifiesBetweenNumberOfTimesWithMatchers() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('bob'); + mockList.add('fred'); + mockList.add('fred'); + mockList.add('bob'); + + // Then + ((fflib_MyList.IList) mocks.verify(mockList, mocks.between(3, 5))).add(fflib_Match.anyString()); + } + + @isTest + private static void thatBetweenThrownExceptionIfCalledLessThanAtLeastNumberOfTimesWithMatchers() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('bob'); + mockList.add('fred'); + + // Then + try + { + ((fflib_MyList.IList) mocks.verify(mockList, mocks.between(3, 5))).add(fflib_Match.anyString()); + + System.assert(false, 'an exception was expected because we are asserting that the method is called 3 times when instead is called only twice'); + } + catch(fflib_ApexMocks.ApexMocksException ex) + { + System.assertEquals('EXPECTED COUNT: 3 or more times' + + '\nACTUAL COUNT: 2' + + '\nMETHOD: fflib_MyList__sfdc_ApexStub.add(String)' + + '\n---' + + '\nACTUAL ARGS: ("bob"), ("fred")' + + '\n---' + + '\nEXPECTED ARGS: [any String]', + ex.getMessage() + ); + } + } + + @isTest + private static void thatBetweenThrownExceptionIfCalledMoreThanAtMostNumberOfTimes() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('bob'); + mockList.add('fred'); + mockList.add('fred'); + mockList.add('bob'); + mockList.add('bob'); + mockList.add('bob'); + mockList.add('bob'); + mockList.add('bob'); + mockList.add('fred'); + + // Then + try + { + ((fflib_MyList.IList) mocks.verify(mockList, mocks.between(3, 5))).add('bob'); + + System.assert(false, 'an exception was expected because we are asserting that the method is called at most 5 times when instead is called six times'); + } + catch(fflib_ApexMocks.ApexMocksException ex) + { + System.assertEquals('EXPECTED COUNT: 5 or fewer times' + + '\nACTUAL COUNT: 6' + + '\nMETHOD: fflib_MyList__sfdc_ApexStub.add(String)' + + '\n---' + + '\nACTUAL ARGS: ("bob"), ("fred"), ("fred"), ("bob"), ("bob"), ("bob"), ("bob"), ("bob"), ("fred")' + + '\n---' + + '\nEXPECTED ARGS: "bob"', + ex.getMessage() + ); + } + } + + @isTest + private static void thatBetweenThrownExceptionIfCalledMoreThanAtMostNumberOfTimesWithMatchers() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('bob'); + mockList.add('fred'); + mockList.add('bob'); + mockList.add('fred'); + mockList.add('fred'); + mockList.add('fred'); + + // Then + try + { + ((fflib_MyList.IList) mocks.verify(mockList, mocks.between(3, 5))).add(fflib_Match.anyString()); + + System.assert(false, 'an exception was expected because we are asserting that the method is called 5 times when instead is called six times'); + } + catch(fflib_ApexMocks.ApexMocksException ex) + { + System.assertEquals('EXPECTED COUNT: 5 or fewer times' + + '\nACTUAL COUNT: 6' + + '\nMETHOD: fflib_MyList__sfdc_ApexStub.add(String)' + + '\n---' + + '\nACTUAL ARGS: ("bob"), ("fred"), ("bob"), ("fred"), ("fred"), ("fred")' + + '\n---' + + '\nEXPECTED ARGS: [any String]', + ex.getMessage(), + 'Unexpected verify fail message'); + } + } + + /* + * never + */ + + @isTest + private static void verifyNeverMethodHasNotBeenCalled() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('bob1'); + mockList.add('bob2'); + mockList.add('bob3'); + + // Then + ((fflib_MyList.IList) mocks.verify(mockList, mocks.never())).add('bob'); + } + + @isTest + private static void verifyNeverMethodHasBeenNotCalledWithMatchers() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('fred', 'fred', 'fred', 'fred'); + mockList.add('fred', 'fred', 'fred', 'fred'); + mockList.add('fred', 'fred', 'fred', 'fred'); + + // Then + ((fflib_MyList.IList) mocks.verify(mockList, mocks.never())).add(fflib_Match.anyString()); + } + + @isTest + private static void thatVerifyNeverFailsWhenCalledMoreTimes() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('bob'); + mockList.add('bob'); + + // Then + try + { + ((fflib_MyList.IList) mocks.verify(mockList, mocks.never())).add('bob'); + + System.assert(false, 'an exception was expected'); + } + catch(Exception exc) + { + assertFailMessage(exc.getMessage(), 0, 2); + } + } + + @isTest + private static void thatVerifyNeverFailsWhenCalledMoreTimesWithMatchers() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('bob'); + mockList.add('bob'); + mockList.add('bob'); + + // Then + try + { + ((fflib_MyList.IList) mocks.verify(mockList, mocks.never())).add(fflib_Match.anyString()); + + System.assert(false, 'an exception was expected'); + } + catch(Exception exc) + { + assertFailMessage(exc.getMessage(), 0, 3); + } + } + + + + /* + * atLeastOnce + */ + + @isTest + private static void thatVerifiesAtLeastOnce() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('bob'); + mockList.add('fred'); + mockList.add('bob'); + mockList.add('bob'); + + // Then + ((fflib_MyList.IList) mocks.verify(mockList, mocks.atLeastOnce())).add('bob'); + } + + @isTest + private static void thatVerifiesAtLeastOnceWhenIsCalledMoreTimes() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('bob'); + mockList.add('fred'); + mockList.add('bob'); + mockList.add('fred'); + mockList.add('bob'); + mockList.add('fred'); + + // Then + ((fflib_MyList.IList) mocks.verify(mockList, mocks.atLeastOnce())).add('bob'); + } + + @isTest + private static void thatThrownExceptionIfCalledLessThanAtLeastOnce() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('bob'); + mockList.add('fred'); + mockList.add('bob'); + + // Then + try + { + ((fflib_MyList.IList) mocks.verify(mockList, mocks.atLeastOnce())).add('rob'); + + System.assert(false, 'an exception was expected because we are asserting that the method is called one times when instead is not called'); + } + catch(fflib_ApexMocks.ApexMocksException ex) + { + System.assertEquals('EXPECTED COUNT: 1 or more times' + + '\nACTUAL COUNT: 0' + + '\nMETHOD: fflib_MyList__sfdc_ApexStub.add(String)' + + '\n---' + + '\nACTUAL ARGS: ("bob"), ("fred"), ("bob")' + + '\n---' + + '\nEXPECTED ARGS: "rob"', + ex.getMessage(), + 'Unexpected verify fail message'); + } + } + + @isTest + private static void thatVerifiesAtLeastOnceWithMatchers() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('bob'); + mockList.add('fred'); + + // Then + ((fflib_MyList.IList) mocks.verify(mockList, mocks.atLeastOnce())).add(fflib_Match.anyString()); + } + + @isTest + private static void thatVerifiesAtLeastOnceWhenIsCalledMoreTimesWithMatchers() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('bob'); + mockList.add('fred'); + mockList.add('fred'); + mockList.add('fred'); + + // Then + ((fflib_MyList.IList) mocks.verify(mockList, mocks.atLeastOnce())).add(fflib_Match.anyString()); + } + + @isTest + private static void thatThrownExceptionIfCalledLessThanAtLeastOnceWithMatchers() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('bob'); + mockList.add('fred'); + + // Then + try + { + ((fflib_MyList.IList) mocks.verify(mockList, mocks.atLeastOnce())).add(fflib_Match.stringStartsWith('rob')); + + System.assert(false, 'an exception was expected because we are asserting that the method is called once when instead is not called'); + } + catch(fflib_ApexMocks.ApexMocksException ex) + { + System.assertEquals('EXPECTED COUNT: 1 or more times' + + '\nACTUAL COUNT: 0' + + '\nMETHOD: fflib_MyList__sfdc_ApexStub.add(String)' + + '\n---' + + '\nACTUAL ARGS: ("bob"), ("fred")' + + '\n---' + + '\nEXPECTED ARGS: [starts with "rob"]', + ex.getMessage(), + 'Unexpected verify fail message'); + } + } + + /* + * HELPER METHODS + */ + + private static void assertFailMessage(String exceptionMessage, Integer expectedInvocations, Integer actualsInvocations) + { + System.assert( + exceptionMessage.startsWith('EXPECTED COUNT: ' + expectedInvocations + '\nACTUAL COUNT: ' + actualsInvocations), + 'Unexpected verify fail message: ' + exceptionMessage + ); + } + + /* + * HELPER CLASSES + */ + + private class isOdd implements fflib_IMatcher + { + public Boolean matches(Object arg) + { + return arg instanceof Integer ? Math.mod((Integer)arg, 2) == 1: false; + } + } + + private class isEven implements fflib_IMatcher + { + public Boolean matches(Object arg) + { + return arg instanceof Integer ? Math.mod((Integer)arg, 2) == 0: false; + } + } +} \ No newline at end of file diff --git a/sfdx-source/apex-mocks/tests/fflib_AnyOrderTest.cls-meta.xml b/sfdx-source/apex-mocks/tests/fflib_AnyOrderTest.cls-meta.xml new file mode 100644 index 00000000000..dd61d1f917e --- /dev/null +++ b/sfdx-source/apex-mocks/tests/fflib_AnyOrderTest.cls-meta.xml @@ -0,0 +1,5 @@ + + + 52.0 + Active + diff --git a/sfdx-source/apex-mocks/tests/fflib_ApexMocksTest.cls b/sfdx-source/apex-mocks/tests/fflib_ApexMocksTest.cls new file mode 100644 index 00000000000..c90b847fe52 --- /dev/null +++ b/sfdx-source/apex-mocks/tests/fflib_ApexMocksTest.cls @@ -0,0 +1,1627 @@ +/* + * Copyright (c) 2014-2017 FinancialForce.com, inc. All rights reserved. + */ +@isTest +private class fflib_ApexMocksTest +{ + private static final fflib_ApexMocks MY_MOCKS = new fflib_ApexMocks(); + private static final fflib_MyList MY_MOCK_LIST = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + + @isTest + static void whenStubMultipleCallsWithMatchersShouldReturnExpectedValues() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + mocks.startStubbing(); + mocks.when(mockList.get2(fflib_Match.anyInteger(), fflib_Match.anyString())).thenReturn('any'); + mocks.when(mockList.get2(fflib_Match.anyInteger(), fflib_Match.stringContains('Hello'))).thenReturn('hello'); + mocks.stopStubbing(); + + // When + String actualValue = mockList.get2(0, 'Hi hi Hello Hi hi'); + + // Then + System.assertEquals('hello', actualValue); + } + + @isTest + static void whenVerifyMultipleCallsWithMatchersShouldReturnCorrectMethodCallCounts() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('bob'); + mockList.add('fred'); + + // Then + ((fflib_MyList.IList) mocks.verify(mockList, 2)).add(fflib_Match.anyString()); + ((fflib_MyList.IList) mocks.verify(mockList)).add('fred'); + ((fflib_MyList.IList) mocks.verify(mockList)).add(fflib_Match.stringContains('fred')); + } + + @isTest + static void whenStubExceptionWithMatchersShouldThrowException() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + mocks.startStubbing(); + ((fflib_MyList.IList) mocks.doThrowWhen(new MyException('Matcher Exception'), mockList)).add(fflib_Match.stringContains('Hello')); + mocks.stopStubbing(); + + // When + mockList.add('Hi'); + + try + { + mockList.add('Hi Hello Hi'); + System.assert(false, 'Expected exception'); + } + catch (MyException e) + { + //Then + System.assertEquals('Matcher Exception', e.getMessage()); + } + } + + @isTest + static void whenVerifyWithCombinedMatchersShouldReturnCorrectMethodCallCounts() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('bob'); + mockList.add('fred'); + + // Then + ((fflib_MyList.IList) mocks.verify(mockList, 0)).add( + (String)fflib_Match.allOf(fflib_Match.eq('bob'), fflib_Match.stringContains('re')) + ); + + ((fflib_MyList.IList) mocks.verify(mockList)).add( + (String)fflib_Match.allOf(fflib_Match.eq('fred'), fflib_Match.stringContains('re')) + ); + + ((fflib_MyList.IList) mocks.verify(mockList, 2)).add( + (String)fflib_Match.anyOf(fflib_Match.eq('bob'), fflib_Match.eq('fred')) + ); + + ((fflib_MyList.IList) mocks.verify(mockList, 1)).add( + (String)fflib_Match.anyOf(fflib_Match.eq('bob'), fflib_Match.eq('jack')) + ); + + ((fflib_MyList.IList) mocks.verify(mockList, 2)).add( + (String)fflib_Match.noneOf(fflib_Match.eq('jack'), fflib_Match.eq('tim')) + ); + + ((fflib_MyList.IList) mocks.verify(mockList, 2)).add( + (String)fflib_Match.noneOf( + fflib_Match.anyOf(fflib_Match.eq('jack'), fflib_Match.eq('jill')), + fflib_Match.allOf(fflib_Match.eq('tim'), fflib_Match.stringContains('i')) + ) + ); + + ((fflib_MyList.IList) mocks.verify(mockList, 2)).add( + (String)fflib_Match.isNot(fflib_Match.eq('jack')) + ); + } + + @isTest + static void whenStubCustomMatchersCanBeUsed() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + mocks.startStubbing(); + mocks.when(mockList.get((Integer)fflib_Match.matches(new isOdd()))).thenReturn('Odd'); + mocks.when(mockList.get((Integer)fflib_Match.matches(new isEven()))).thenReturn('Even'); + mocks.stopStubbing(); + + // When + String s1 = mockList.get(1); + String s2 = mockList.get(2); + String s3 = mockList.get(3); + String s4 = mockList.get(4); + String s5 = mockList.get(5); + + // Then + System.assertEquals('Odd', s1); + System.assertEquals('Even', s2); + System.assertEquals('Odd', s3); + System.assertEquals('Even', s4); + System.assertEquals('Odd', s5); + } + + @isTest + static void whenVerifyCustomMatchersCanBeUsed() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.get(1); + mockList.get(2); + mockList.get(3); + mockList.get(4); + mockList.get(5); + + // Then + ((fflib_MyList.IList) mocks.verify(mockList, 3)).get((Integer)fflib_Match.matches(new isOdd())); + ((fflib_MyList.IList) mocks.verify(mockList, 2)).get((Integer)fflib_Match.matches(new isEven())); + } + + @isTest + static void whenStubWithMatcherAndNonMatcherArgumentsShouldThrowException() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + String expectedError = 'The number of matchers defined (1).' + + ' does not match the number expected (2)\n' + + 'If you are using matchers all arguments must be passed in as matchers.\n' + + 'For example myList.add(fflib_Match.anyInteger(), \'String\') should be defined as myList.add(fflib_Match.anyInteger(), fflib_Match.eq(\'String\')).'; + + // Then + try + { + mocks.startStubbing(); + mocks.when(mockList.get2(fflib_Match.anyInteger(), 'String literal')).thenReturn('fail'); + System.assert(false, 'Expected exception'); + } + catch (fflib_ApexMocks.ApexMocksException e) + { + System.assertEquals(expectedError, e.getMessage()); + } + } + + @isTest + static void whenVerifyWithMatcherAndNonMatcherArgumentsShouldThrowException() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + String expectedError = 'The number of matchers defined (1).' + + ' does not match the number expected (2)\n' + + 'If you are using matchers all arguments must be passed in as matchers.\n' + + 'For example myList.add(fflib_Match.anyInteger(), \'String\') should be defined as myList.add(fflib_Match.anyInteger(), fflib_Match.eq(\'String\')).'; + + mockList.get2(1, 'String literal'); + + // Then + try + { + ((fflib_MyList.IList) mocks.verify(mockList)).get2(fflib_Match.anyInteger(), 'String literal'); + System.assert(false, 'Expected exception'); + } + catch (fflib_ApexMocks.ApexMocksException e) + { + System.assertEquals(expectedError, e.getMessage()); + } + } + + @isTest + static void whenStubSameMethodWithMatchersAndNonMatchersShouldStubInOrder() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + mocks.startStubbing(); + + mocks.when(mockList.get2(1, 'Non-matcher first')).thenReturn('Bad'); //Set the return value using the non-matcher arguments + mocks.when(mockList.get2(fflib_Match.eqInteger(1), fflib_Match.stringContains('Non-matcher first'))).thenReturn('Good'); //Override the return value using matcher arguments + + mocks.when(mockList.get2(fflib_Match.eqInteger(1), fflib_Match.stringContains('Matcher first'))).thenReturn('Bad'); //Set the return value using the matcher arguments + mocks.when(mockList.get2(1, 'Matcher first')).thenReturn('Good'); //Override the return value using non-matcher arguments + + mocks.stopStubbing(); + + // When/Thens + System.assertEquals('Good', mockList.get2(1, 'Non-matcher first')); + System.assertEquals('Good', mockList.get2(1, 'Matcher first')); + } + + @isTest + static void whenStubExceptionSameMethodWithMatchersAndNonMatchersShouldStubInOrder() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + mocks.startStubbing(); + + ((fflib_MyList.IList)mocks.doThrowWhen(new fflib_ApexMocks.ApexMocksException('Bad'), mockList)).add('Non-matcher first'); //Set the exception value using the non-matcher arguments + ((fflib_MyList.IList)mocks.doThrowWhen(new fflib_ApexMocks.ApexMocksException('Good'), mockList)).add(fflib_Match.stringContains('Non-matcher first')); //Override the exception value using matcher arguments + + ((fflib_MyList.IList)mocks.doThrowWhen(new fflib_ApexMocks.ApexMocksException('Bad'), mockList)).add(fflib_Match.stringContains('Matcher first')); //Set the exception value using the matcher arguments + ((fflib_MyList.IList)mocks.doThrowWhen(new fflib_ApexMocks.ApexMocksException('Good'), mockList)).add('Matcher first'); //Override the exception value using non-matcher arguments + + mocks.stopStubbing(); + + // When/Thens + try + { + mockList.add('Non-matcher first'); + System.assert(false, 'Expected exception'); + } + catch (fflib_ApexMocks.ApexMocksException e) + { + System.assertEquals('Good', e.getMessage()); + } + + try + { + mockList.add('Matcher first'); + System.assert(false, 'Expected exception'); + } + catch (fflib_ApexMocks.ApexMocksException e) + { + System.assertEquals('Good', e.getMessage()); + } + } + + @isTest + static void whenStubSingleCallWithSingleArgumentShouldReturnStubbedValue() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + mocks.startStubbing(); + mocks.when(mockList.get(0)).thenReturn('bob'); + mocks.stopStubbing(); + + // When + String actualValue = mockList.get(0); + + // Then + System.assertEquals('bob', actualValue); + } + + @isTest + static void whenStubSingleCallWithNullReturnValueItShouldReturnNull() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + mocks.startStubbing(); + mocks.when(mockList.get(0)).thenReturn(null); + mocks.stopStubbing(); + + // When + String actualValue = mockList.get(0); + + // Then + System.assertEquals(null, actualValue); + } + + @isTest + static void whenStubMultipleCallsWithSingleArgumentShouldReturnStubbedValues() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + mocks.startStubbing(); + mocks.when(mockList.get(0)).thenReturn('bob'); + mocks.when(mockList.get(1)).thenReturn('fred'); + mocks.stopStubbing(); + + // When + String actualValueArg0 = mockList.get(0); + String actualValueArg1 = mockList.get(1); + String actualValueArg2 = mockList.get(2); + + // Then + System.assertEquals('bob', actualValueArg0); + System.assertEquals('fred', actualValueArg1); + System.assertEquals(null, actualValueArg2); + } + + @isTest + static void whenStubSameCallWithDifferentArgumentValueShouldReturnLastStubbedValue() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + mocks.startStubbing(); + mocks.when(mockList.get(0)).thenReturn('bob1'); + mocks.when(mockList.get(0)).thenReturn('bob2'); + mocks.stopStubbing(); + + // When + String actualValue = mockList.get(0); + + // Then + System.assertEquals('bob2', actualValue); + } + + @isTest + static void whenStubCallWithNoArgumentsShouldReturnStubbedValue() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + mocks.startStubbing(); + mocks.when(mockList.isEmpty()).thenReturn(false); + mocks.stopStubbing(); + + // When + Boolean actualValue = mockList.isEmpty(); + + // Then + System.assertEquals(false, actualValue); + } + + @isTest + static void verifySingleMethodCallWithNoArguments() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.isEmpty(); + + // Then + ((fflib_MyList.IList) mocks.verify(mockList)).isEmpty(); + } + + @isTest + static void verifySingleMethodCallWithSingleArgument() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('bob'); + + // Then + ((fflib_MyList.IList) mocks.verify(mockList)).add('bob'); + } + + @isTest + static void verifyMultipleMethodCallsWithSameSingleArgument() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('bob'); + mockList.add('bob'); + + // Then + ((fflib_MyList.IList) mocks.verify(mockList, 2)).add('bob'); + } + + @isTest + static void verifyMultipleMethodCallsWithDifferentSingleArgument() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('bob'); + mockList.add('fred'); + + // Then + ((fflib_MyList.IList) mocks.verify(mockList)).add('bob'); + ((fflib_MyList.IList) mocks.verify(mockList)).add('fred'); + } + + @isTest + static void verifyMethodCallsWithSameNameButDifferentArgumentTypes() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('bob'); + mockList.add(new String[] {'bob'}); + mockList.add((String)null); + mockList.add((String[])null); + + // Then + ((fflib_MyList.IList) mocks.verify(mockList)).add('bob'); + ((fflib_MyList.IList) mocks.verify(mockList)).add(new String[] {'bob'}); + ((fflib_MyList.IList) mocks.verify(mockList)).add((String)null); + ((fflib_MyList.IList) mocks.verify(mockList)).add((String[])null); + } + + @isTest + static void verifyMethodNotCalled() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.get(0); + + // Then + ((fflib_MyList.IList) mocks.verify(mockList, fflib_ApexMocks.NEVER)).add('bob'); + ((fflib_MyList.IList) mocks.verify(mockList)).get(0); + } + + @isTest + static void stubAndVerifyMethodCallsWithNoArguments() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + mocks.startStubbing(); + mocks.when(mockList.isEmpty()).thenReturn(false); + mocks.stopStubbing(); + + mockList.clear(); + + // When + Boolean actualValue = mockList.isEmpty(); + + // Then + System.assertEquals(false, actualValue); + ((fflib_MyList.IList) mocks.verify(mockList)).clear(); + } + + @isTest + static void whenStubExceptionTheExceptionShouldBeThrown() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + mocks.startStubbing(); + mocks.when(mockList.get(0)).thenThrow(new MyException('Stubbed exception.')); + mocks.stopStubbing(); + + // When + try + { + mockList.get(0); + System.assert(false, 'Stubbed exception should have been thrown.'); + } + catch(Exception e) + { + // Then + System.assert(e instanceof MyException); + System.assertEquals('Stubbed exception.', e.getMessage()); + } + } + + @isTest + static void whenStubVoidMethodWithExceptionThenExceptionShouldBeThrown() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + mocks.startStubbing(); + ((fflib_MyList.IList) mocks.doThrowWhen(new MyException('Stubbed exception.'), mockList)).clear(); + mocks.stopStubbing(); + + // When + try + { + mockList.clear(); + System.assert(false, 'Stubbed exception should have been thrown.'); + } + catch(Exception e) + { + // Then + System.assert(e instanceof MyException); + System.assertEquals('Stubbed exception.', e.getMessage()); + } + } + + @isTest + static void whenStubMultipleVoidMethodsWithExceptionsThenExceptionsShouldBeThrown() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + mocks.startStubbing(); + ((fflib_MyList.IList) mocks.doThrowWhen(new MyException('clear stubbed exception.'), mockList)).clear(); + ((fflib_MyList.IList) mocks.doThrowWhen(new MyException('add stubbed exception.'), mockList)).add('bob'); + mocks.stopStubbing(); + + // When + try + { + mockList.clear(); + System.assert(false, 'Stubbed exception should have been thrown.'); + } + catch(Exception e) + { + // Then + System.assert(e instanceof MyException); + System.assertEquals('clear stubbed exception.', e.getMessage()); + } + + // When + try + { + mockList.add('bob'); + System.assert(false, 'Stubbed exception should have been thrown.'); + } + catch(Exception e) + { + // Then + System.assert(e instanceof MyException); + System.assertEquals('add stubbed exception.', e.getMessage()); + } + } + + @isTest + static void whenStubVoidMethodWithExceptionAndCallMethodTwiceThenExceptionShouldBeThrownTwice() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + mocks.startStubbing(); + ((fflib_MyList.IList) mocks.doThrowWhen(new MyException('clear stubbed exception.'), mockList)).clear(); + mocks.stopStubbing(); + + // When + try + { + mockList.clear(); + System.assert(false, 'Stubbed exception should have been thrown.'); + } + catch(Exception e) + { + // Then + System.assert(e instanceof MyException); + System.assertEquals('clear stubbed exception.', e.getMessage()); + } + + // When + try + { + mockList.clear(); + System.assert(false, 'Stubbed exception should have been thrown.'); + } + catch(Exception e) + { + // Then + System.assert(e instanceof MyException); + System.assertEquals('clear stubbed exception.', e.getMessage()); + } + } + + @isTest + static void verifyMethodCallWhenNoCallsBeenMadeForType() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // Then + ((fflib_MyList.IList) mocks.verify(mockList, fflib_ApexMocks.NEVER)).add('bob'); + } + + @isTest + static void verifySingleMethodCallWithMultipleArguments() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.set(0, 'bob'); + + // Then + ((fflib_MyList.IList) mocks.verify(mockList)).set(0, 'bob'); + ((fflib_MyList.IList) mocks.verify(mockList, fflib_ApexMocks.NEVER)).set(0, 'fred'); + } + + @isTest + static void whenStubMultipleCallsWithMultipleArgumentShouldReturnStubbedValues() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + mocks.startStubbing(); + mocks.when(mockList.get2(0, 'zero')).thenReturn('bob'); + mocks.when(mockList.get2(1, 'one')).thenReturn('fred'); + mocks.when(mockList.get2(0, 'two')).thenReturn('bob'); + mocks.when(mockList.get2(1, 'three')).thenReturn('bub'); + mocks.stopStubbing(); + + // When + String actualValueArg0 = mockList.get2(0, 'zero'); + String actualValueArg1 = mockList.get2(1, 'one'); + String actualValueArg2 = mockList.get2(0, 'two'); + String actualValueArg3 = mockList.get2(1, 'three'); + String actualValueArg4 = mockList.get2(0, 'three'); + + // Then + System.assertEquals('bob', actualValueArg0); + System.assertEquals('fred', actualValueArg1); + System.assertEquals('bob', actualValueArg2); + System.assertEquals('bub', actualValueArg3); + System.assertEquals(null, actualValueArg4); + } + + @isTest + static void whenStubNullConcreteArgValueCorrectValueIsReturned() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + String expected = 'hello'; + + mocks.startStubbing(); + mocks.when(mockList.get(null)).thenReturn(expected); + mocks.stopStubbing(); + + // When + String actual = mockList.get(null); + + // Then + System.assertEquals(expected, actual); + } + + @isTest + static void whenSetDoThrowWhenExceptionsValuesAreSet() + { + //Given + MyException e = new MyException('Test'); + fflib_ApexMocks mocks = new fflib_ApexMocks(); + + List expsList = new List{e}; + + //When + mocks.DoThrowWhenExceptions = expsList; + + //Then + System.assert(expsList === mocks.DoThrowWhenExceptions); + } + + @isTest + static void whenVerifyMethodNeverCalledMatchersAreReset() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('bob'); + + // Then + ((fflib_MyList.IList) mocks.verify(mockList, fflib_ApexMocks.NEVER)).get(fflib_Match.anyInteger()); + ((fflib_MyList.IList) mocks.verify(mockList)).add(fflib_Match.anyString()); + } + + @isTest + static void whenMockIsGeneratedCanVerify() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList.IList mockList = new fflib_Mocks.Mockfflib_MyList(mocks); + + // When + mockList.add('bob'); + + // Then + ((fflib_MyList.IList) mocks.verify(mockList, fflib_ApexMocks.NEVER)).get(fflib_Match.anyInteger()); + ((fflib_MyList.IList) mocks.verify(mockList)).add('bob'); + } + + @isTest + static void whenMockIsGeneratedCanStubVerify() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList.IList mockList = new fflib_Mocks.Mockfflib_MyList(mocks); + + // When + mocks.startStubbing(); + mocks.when(mockList.get(1)).thenReturn('One'); + mocks.when(mockList.get(fflib_Match.integerMoreThan(2))).thenReturn('>Two'); + mocks.stopStubbing(); + + // Then + System.assertEquals(null, mockList.get(0)); + System.assertEquals('One', mockList.get(1)); + System.assertEquals(null, mockList.get(2)); + System.assertEquals('>Two', mockList.get(3)); + } + + @isTest + static void thatMultipleInstancesCanBeMockedIndependently() + { + fflib_ApexMocksConfig.HasIndependentMocks = true; + + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList first = (fflib_MyList)mocks.mock(fflib_MyList.class); + fflib_MyList second = (fflib_MyList)mocks.mock(fflib_MyList.class); + + mocks.startStubbing(); + mocks.when(first.get(0)).thenReturn('First'); + mocks.when(second.get(0)).thenReturn('Second'); + mocks.stopStubbing(); + + // When + String actual = first.get(0); + + // Then + System.assertEquals('First', actual, 'Should have returned stubbed value'); + ((fflib_MyList)mocks.verify(first)).get(0); + ((fflib_MyList)mocks.verify(second, mocks.never())).get(0); + } + + @isTest + static void thatMultipleInstancesCanBeMockedDependently() + { + fflib_ApexMocksConfig.HasIndependentMocks = false; + + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList first = (fflib_MyList)mocks.mock(fflib_MyList.class); + fflib_MyList second = (fflib_MyList)mocks.mock(fflib_MyList.class); + + mocks.startStubbing(); + mocks.when(first.get(0)).thenReturn('First'); + mocks.when(second.get(0)).thenReturn('Second'); + mocks.stopStubbing(); + + // When + String actual = first.get(0); + + // Then + System.assertEquals('Second', actual, 'Should have returned stubbed value'); + ((fflib_MyList)mocks.verify(first)).get(0); + ((fflib_MyList)mocks.verify(second)).get(0); + } + + static void thatStubbingCanBeChainedFirstExceptionThenValue() + { + // Given + // When + MY_MOCKS.startStubbing(); + MY_MOCKS.when(MY_MOCK_LIST.get(1)).thenThrow(new MyException('Stubbed exception.')).thenReturn('One'); + MY_MOCKS.stopStubbing(); + + // Then + assertExceptionMessage('Stubbed exception.'); + assertReturnedValue('One'); + } + + @isTest + static void thatStubbingCanBeChainedFirstValueThenException() + { + // Given + // When + MY_MOCKS.startStubbing(); + MY_MOCKS.when(MY_MOCK_LIST.get(1)).thenReturn('One').thenThrow(new MyException('Stubbed exception.')); + MY_MOCKS.stopStubbing(); + + // Then + assertReturnedValue('One'); + assertExceptionMessage('Stubbed exception.'); + } + + @isTest + static void thatStubbingMultipleMethodsCanBeChainedFirstExceptionThenValue() + { + // Given + // When + MY_MOCKS.startStubbing(); + MY_MOCKS.when(MY_MOCK_LIST.get(1)).thenThrow(new MyException('Stubbed exception.')).thenReturn('One'); + MY_MOCKS.when(MY_MOCK_LIST.get2(2, 'Hello.')).thenThrow(new MyException('Stubbed exception2.')).thenReturn('One2'); + MY_MOCKS.stopStubbing(); + + // Then + assertExceptionMessage('Stubbed exception.'); + assertReturnedValue('One'); + assertExceptionMessageForGet2('Stubbed exception2.'); + assertReturnedValueForGet2('One2'); + } + + @isTest + static void thatStubbingMultipleMethodsCanBeChainedFirstValueThenException() + { + // Given + // When + MY_MOCKS.startStubbing(); + MY_MOCKS.when(MY_MOCK_LIST.get(1)).thenReturn('One').thenThrow(new MyException('Stubbed exception.')); + MY_MOCKS.when(MY_MOCK_LIST.get2(2, 'Hello.')).thenReturn('One2').thenThrow(new MyException('Stubbed exception2.')); + MY_MOCKS.stopStubbing(); + + // Then + assertReturnedValue('One'); + assertExceptionMessage('Stubbed exception.'); + assertReturnedValueForGet2('One2'); + assertExceptionMessageForGet2('Stubbed exception2.'); + } + + @isTest + static void thatStubbingReturnsDifferentValuesForDifferentCalls() + { + // Given + // When + MY_MOCKS.startStubbing(); + MY_MOCKS.when(MY_MOCK_LIST.get(1)).thenReturnMulti(new List{'One', 'Two', 'Three'}); + MY_MOCKS.stopStubbing(); + + // Then + assertReturnedValue('One'); + assertReturnedValue('Two'); + assertReturnedValue('Three'); + } + + @isTest + static void thatStubbingReturnsDifferentValuesForDifferentCallsAndRepeatLastValuesForFurtherCalls() + { + // Given + // When + MY_MOCKS.startStubbing(); + MY_MOCKS.when(MY_MOCK_LIST.get(1)).thenReturnMulti(new List{'One', 'Two', 'Three'}); + MY_MOCKS.stopStubbing(); + + // Then + assertReturnedValue('One'); + assertReturnedValue('Two'); + assertReturnedValue('Three'); + + assertReturnedValue('Three'); + assertReturnedValue('Three'); + } + + @isTest + static void thatStubbingThrowsDifferentExceptionsForDifferentCalls() + { + // Given + MyException first = new MyException('first.'); + MyException second = new MyException('second.'); + MyException third = new MyException('third.'); + + // When + MY_MOCKS.startStubbing(); + MY_MOCKS.when(MY_MOCK_LIST.get(1)).thenThrowMulti(new List{first, second, third}); + MY_MOCKS.stopStubbing(); + + // Then + assertExceptionMessage('first.'); + assertExceptionMessage('second.'); + assertExceptionMessage('third.'); + } + + @isTest + static void thatStubbingThrowsDifferentExceptionsForDifferentCallsAndRepeatLastExceptionForFurtherCalls() + { + // Given + MyException first = new MyException('first.'); + MyException second = new MyException('second.'); + MyException third = new MyException('third.'); + + // When + MY_MOCKS.startStubbing(); + MY_MOCKS.when(MY_MOCK_LIST.get(1)).thenThrowMulti(new List{first, second, third}); + MY_MOCKS.stopStubbing(); + + // Then + assertExceptionMessage('first.'); + assertExceptionMessage('second.'); + assertExceptionMessage('third.'); + + assertExceptionMessage('third.'); + assertExceptionMessage('third.'); + } + + @isTest + static void thatStubbingThrowsAndReturnsDifferentExceptionsAndValuesForDifferentCalls() + { + // Given + MyException first = new MyException('first.'); + MyException second = new MyException('second.'); + MyException third = new MyException('third.'); + + // When + MY_MOCKS.startStubbing(); + MY_MOCKS.when(MY_MOCK_LIST.get(1)). + thenThrowMulti(new List{first, second, third}). + thenReturnMulti(new List{'One', 'Two', 'Three'}); + MY_MOCKS.stopStubbing(); + + // Then + assertExceptionMessage('first.'); + assertExceptionMessage('second.'); + assertExceptionMessage('third.'); + + assertReturnedValue('One'); + assertReturnedValue('Two'); + assertReturnedValue('Three'); + + assertReturnedValue('Three'); + assertReturnedValue('Three'); + } + + @isTest + static void thatStubbingReturnsAndThrowsDifferentValuesAndExceptionsForDifferentCalls() + { + // Given + MyException first = new MyException('first.'); + MyException second = new MyException('second.'); + MyException third = new MyException('third.'); + + // When + MY_MOCKS.startStubbing(); + MY_MOCKS.when(MY_MOCK_LIST.get(1)). + thenReturnMulti(new List{'One', 'Two', 'Three'}). + thenThrowMulti(new List{first, second, third}); + MY_MOCKS.stopStubbing(); + + // Then + assertReturnedValue('One'); + assertReturnedValue('Two'); + assertReturnedValue('Three'); + + assertExceptionMessage('first.'); + assertExceptionMessage('second.'); + assertExceptionMessage('third.'); + + assertExceptionMessage('third.'); + assertExceptionMessage('third.'); + } + + @isTest + static void thatStubbingMultipleTimesOverridePreviousThenReturnWithSingleValue() + { + // Given + // When + MY_MOCKS.startStubbing(); + MY_MOCKS.when(MY_MOCK_LIST.get(1)).thenReturn('One'); + MY_MOCKS.when(MY_MOCK_LIST.get(1)).thenReturn('Two'); + MY_MOCKS.stopStubbing(); + + // Then + assertReturnedValue('Two'); + assertReturnedValue('Two'); + } + + @isTest + static void thatStubbingMultipleTimesOverridePreviousThenReturnMultiWithSingleValue() + { + // Given + // When + MY_MOCKS.startStubbing(); + MY_MOCKS.when(MY_MOCK_LIST.get(1)).thenReturnMulti(new List{'One', 'Two', 'Three'}); + MY_MOCKS.when(MY_MOCK_LIST.get(1)).thenReturn('Two'); + MY_MOCKS.stopStubbing(); + + // Then + assertReturnedValue('Two'); + assertReturnedValue('Two'); + } + + @isTest + static void thatStubbingMultipleTimesOverridePreviousThenReturnMultiWithMultiValue() + { + // Given + // When + MY_MOCKS.startStubbing(); + MY_MOCKS.when(MY_MOCK_LIST.get(1)).thenReturnMulti(new List{'One', 'Two', 'Three'}); + MY_MOCKS.when(MY_MOCK_LIST.get(1)).thenReturnMulti(new List{'Four', 'Five', 'Six'}); + MY_MOCKS.stopStubbing(); + + // Then + assertReturnedValue('Four'); + assertReturnedValue('Five'); + assertReturnedValue('Six'); + + assertReturnedValue('Six'); + assertReturnedValue('Six'); + } + + @isTest + static void thatStubbingMultipleTimesOverridePreviousThenReturnWithMultiValues() + { + // Given + // When + MY_MOCKS.startStubbing(); + MY_MOCKS.when(MY_MOCK_LIST.get(1)).thenReturn('Two'); + MY_MOCKS.when(MY_MOCK_LIST.get(1)).thenReturnMulti(new List{'One', 'Two', 'Three'}); + MY_MOCKS.stopStubbing(); + + // Then + assertReturnedValue('One'); + assertReturnedValue('Two'); + assertReturnedValue('Three'); + + assertReturnedValue('Three'); + assertReturnedValue('Three'); + } + + @isTest + static void thatStubbingMultipleTimesOverridePreviousThenReturnMultiWithSingleException() + { + // Given + MyException first = new MyException('first.'); + + // When + MY_MOCKS.startStubbing(); + MY_MOCKS.when(MY_MOCK_LIST.get(1)).thenReturnMulti(new List{'One', 'Two', 'Three'}); + MY_MOCKS.when(MY_MOCK_LIST.get(1)).thenThrow(first); + MY_MOCKS.stopStubbing(); + + // Then + assertExceptionMessage('first.'); + assertExceptionMessage('first.'); + } + + @isTest + static void thatStubbingMultipleTimesOverridePreviousThenReturnMultiWithMultiExceptions() + { + // Given + MyException first = new MyException('first.'); + MyException second = new MyException('second.'); + MyException third = new MyException('third.'); + + // When + MY_MOCKS.startStubbing(); + MY_MOCKS.when(MY_MOCK_LIST.get(1)).thenReturnMulti(new List{'One', 'Two', 'Three'}); + MY_MOCKS.when(MY_MOCK_LIST.get(1)).thenThrowMulti(new List{first, second, third}); + MY_MOCKS.stopStubbing(); + + // Then + assertExceptionMessage('first.'); + assertExceptionMessage('second.'); + assertExceptionMessage('third.'); + + assertExceptionMessage('third.'); + assertExceptionMessage('third.'); + } + + @isTest + static void thatStubbingMultipleTimesOverridePreviousThenReturnWithMultiExceptions() + { + // Given + MyException first = new MyException('first.'); + MyException second = new MyException('second.'); + MyException third = new MyException('third.'); + + // When + MY_MOCKS.startStubbing(); + MY_MOCKS.when(MY_MOCK_LIST.get(1)).thenReturn('Two'); + MY_MOCKS.when(MY_MOCK_LIST.get(1)).thenThrowMulti(new List{first, second, third}); + MY_MOCKS.stopStubbing(); + + // Then + assertExceptionMessage('first.'); + assertExceptionMessage('second.'); + assertExceptionMessage('third.'); + + assertExceptionMessage('third.'); + assertExceptionMessage('third.'); + } + + @isTest + static void thatStubbingMultipleTimesOverridePreviousThenReturnWithSingleException() + { + // Given + MyException first = new MyException('first.'); + + // When + MY_MOCKS.startStubbing(); + MY_MOCKS.when(MY_MOCK_LIST.get(1)).thenReturn('Two'); + MY_MOCKS.when(MY_MOCK_LIST.get(1)).thenThrow(first); + MY_MOCKS.stopStubbing(); + + // Then + assertExceptionMessage('first.'); + assertExceptionMessage('first.'); + } + + @isTest + static void thatStubbingMultipleTimesOverridePreviousThenThrowWithSingleValue() + { + // Given + MyException first = new MyException('first.'); + + // When + MY_MOCKS.startStubbing(); + MY_MOCKS.when(MY_MOCK_LIST.get(1)).thenThrow(first); + MY_MOCKS.when(MY_MOCK_LIST.get(1)).thenReturn('Two'); + MY_MOCKS.stopStubbing(); + + // Then + assertReturnedValue('Two'); + assertReturnedValue('Two'); + } + + @isTest + static void thatStubbingMultipleTimesOverridePreviousThenThrowMultiWithSingleValue() + { + // Given + MyException first = new MyException('first.'); + MyException second = new MyException('second.'); + MyException third = new MyException('third.'); + + // When + MY_MOCKS.startStubbing(); + MY_MOCKS.when(MY_MOCK_LIST.get(1)).thenThrowMulti(new List{first, second, third}); + MY_MOCKS.when(MY_MOCK_LIST.get(1)).thenReturn('Two'); + MY_MOCKS.stopStubbing(); + + // Then + assertReturnedValue('Two'); + assertReturnedValue('Two'); + } + + @isTest + static void thatStubbingMultipleTimesOverridePreviousThenThrowMultiWithMultiValue() + { + // Given + MyException first = new MyException('first.'); + MyException second = new MyException('second.'); + MyException third = new MyException('third.'); + + // When + MY_MOCKS.startStubbing(); + MY_MOCKS.when(MY_MOCK_LIST.get(1)).thenThrowMulti(new List{first, second, third}); + MY_MOCKS.when(MY_MOCK_LIST.get(1)).thenReturnMulti(new List{'Four', 'Five', 'Six'}); + MY_MOCKS.stopStubbing(); + + // Then + assertReturnedValue('Four'); + assertReturnedValue('Five'); + assertReturnedValue('Six'); + + assertReturnedValue('Six'); + assertReturnedValue('Six'); + } + + @isTest + static void thatStubbingMultipleTimesOverridePreviousThenThrowWithMultiValues() + { + // Given + MyException first = new MyException('first.'); + + // When + MY_MOCKS.startStubbing(); + MY_MOCKS.when(MY_MOCK_LIST.get(1)).thenThrow(first); + MY_MOCKS.when(MY_MOCK_LIST.get(1)).thenReturnMulti(new List{'One', 'Two', 'Three'}); + MY_MOCKS.stopStubbing(); + + // Then + assertReturnedValue('One'); + assertReturnedValue('Two'); + assertReturnedValue('Three'); + + assertReturnedValue('Three'); + assertReturnedValue('Three'); + } + + @isTest + static void thatStubbingMultipleTimesOverridePreviousThenThrowMultiWithSingleException() + { + // Given + MyException first = new MyException('first.'); + MyException second = new MyException('second.'); + MyException third = new MyException('third.'); + MyException fourth = new MyException('fourth.'); + + // When + MY_MOCKS.startStubbing(); + MY_MOCKS.when(MY_MOCK_LIST.get(1)).thenThrowMulti(new List{first, second, third}); + MY_MOCKS.when(MY_MOCK_LIST.get(1)).thenThrow(fourth); + MY_MOCKS.stopStubbing(); + + // Then + assertExceptionMessage('fourth.'); + assertExceptionMessage('fourth.'); + } + + @isTest + static void thatStubbingMultipleTimesOverridePreviousThenThrowMultiWithMultiExceptions() + { + // Given + MyException first = new MyException('first.'); + MyException second = new MyException('second.'); + MyException third = new MyException('third.'); + + MyException fourth = new MyException('fourth.'); + MyException fifth = new MyException('fifth.'); + MyException sixth = new MyException('sixth.'); + + // When + MY_MOCKS.startStubbing(); + MY_MOCKS.when(MY_MOCK_LIST.get(1)).thenThrowMulti(new List{first, second, third}); + MY_MOCKS.when(MY_MOCK_LIST.get(1)).thenThrowMulti(new List{fourth, fifth, sixth}); + MY_MOCKS.stopStubbing(); + + // Then + assertExceptionMessage('fourth.'); + assertExceptionMessage('fifth.'); + assertExceptionMessage('sixth.'); + + assertExceptionMessage('sixth.'); + assertExceptionMessage('sixth.'); + } + + @isTest + static void thatStubbingMultipleTimesOverridePreviousThenThrowWithMultiExceptions() + { + // Given + MyException beforeFirst = new MyException('before first.'); + + MyException first = new MyException('first.'); + MyException second = new MyException('second.'); + MyException third = new MyException('third.'); + + // When + MY_MOCKS.startStubbing(); + MY_MOCKS.when(MY_MOCK_LIST.get(1)).thenThrow(beforeFirst); + MY_MOCKS.when(MY_MOCK_LIST.get(1)).thenThrowMulti(new List{first, second, third}); + MY_MOCKS.stopStubbing(); + + // Then + assertExceptionMessage('first.'); + assertExceptionMessage('second.'); + assertExceptionMessage('third.'); + + assertExceptionMessage('third.'); + assertExceptionMessage('third.'); + } + + @isTest + static void thatStubbingMultipleTimesOverridePreviousThenThrowWithSingleException() + { + // Given + MyException beforeFirst = new MyException('before first.'); + MyException first = new MyException('first.'); + + // When + MY_MOCKS.startStubbing(); + MY_MOCKS.when(MY_MOCK_LIST.get(1)).thenThrow(beforeFirst); + MY_MOCKS.when(MY_MOCK_LIST.get(1)).thenThrow(first); + MY_MOCKS.stopStubbing(); + + // Then + assertExceptionMessage('first.'); + assertExceptionMessage('first.'); + } + + @isTest + static void thatVoidMethodThrowsMultipleExceptions() + { + // Given + MyException beforeFirst = new MyException('before first.'); + + MyException first = new MyException('first.'); + MyException second = new MyException('second.'); + MyException third = new MyException('third.'); + + // When + MY_MOCKS.startStubbing(); + ((fflib_MyList.IList) MY_MOCKS.doThrowWhen(new List{first, second, third}, MY_MOCK_LIST)).add('Hello'); + MY_MOCKS.stopStubbing(); + + // Then + assertExceptionMessageOnVoidMethod('first.'); + assertExceptionMessageOnVoidMethod('second.'); + assertExceptionMessageOnVoidMethod('third.'); + + assertExceptionMessageOnVoidMethod('third.'); + assertExceptionMessageOnVoidMethod('third.'); + } + + @isTest + static void thatMultipleVoidMethodsThrowsMultipleExceptions() + { + // Given + MyException first = new MyException('first.'); + MyException second = new MyException('second.'); + MyException third = new MyException('third.'); + + MyException first2 = new MyException('first2.'); + MyException second2 = new MyException('second2.'); + MyException third2 = new MyException('third2.'); + + // When + MY_MOCKS.startStubbing(); + ((fflib_MyList.IList) MY_MOCKS.doThrowWhen(new List{first2, second2, third2}, MY_MOCK_LIST)).addMore('Hello'); + ((fflib_MyList.IList) MY_MOCKS.doThrowWhen(new List{first, second, third}, MY_MOCK_LIST)).add('Hello'); + MY_MOCKS.stopStubbing(); + + // Then + assertExceptionMessageOnVoidMethod('first.'); + assertExceptionMessageOnVoidMethod('second.'); + assertExceptionMessageOnVoidMethod('third.'); + + assertExceptionMessageOnVoidMethod('third.'); + assertExceptionMessageOnVoidMethod('third.'); + + + assertExceptionMessageOnAddMoreVoidMethod('first2.'); + assertExceptionMessageOnAddMoreVoidMethod('second2.'); + assertExceptionMessageOnAddMoreVoidMethod('third2.'); + + assertExceptionMessageOnAddMoreVoidMethod('third2.'); + assertExceptionMessageOnAddMoreVoidMethod('third2.'); + } + + @isTest + static void thatStubbingMutipleTimesVoidMethodThrowsMultipleExceptionsOverride() + { + // Given + MyException beforeFirst = new MyException('before first.'); + + MyException first = new MyException('first.'); + MyException second = new MyException('second.'); + MyException third = new MyException('third.'); + + MyException fourth = new MyException('fourth.'); + MyException fifth = new MyException('fifth.'); + MyException sixth = new MyException('sixth.'); + + // When + MY_MOCKS.startStubbing(); + ((fflib_MyList.IList) MY_MOCKS.doThrowWhen(new List{first, second, third}, MY_MOCK_LIST)).add('Hello'); + ((fflib_MyList.IList) MY_MOCKS.doThrowWhen(new List{fourth, fifth, sixth}, MY_MOCK_LIST)).add('Hello'); + MY_MOCKS.stopStubbing(); + + // Then + assertExceptionMessageOnVoidMethod('fourth.'); + assertExceptionMessageOnVoidMethod('fifth.'); + assertExceptionMessageOnVoidMethod('sixth.'); + + assertExceptionMessageOnVoidMethod('sixth.'); + assertExceptionMessageOnVoidMethod('sixth.'); + } + + @isTest + static void thatStubbingMutipleTimesVoidMethodThrowsMultipleExceptionsOverrideWithSingleException() + { + // Given + MyException beforeFirst = new MyException('before first.'); + + MyException first = new MyException('first.'); + MyException second = new MyException('second.'); + MyException third = new MyException('third.'); + + MyException fourth = new MyException('fourth.'); + MyException fifth = new MyException('fifth.'); + MyException sixth = new MyException('sixth.'); + + // When + MY_MOCKS.startStubbing(); + ((fflib_MyList.IList) MY_MOCKS.doThrowWhen(new List{first, second, third}, MY_MOCK_LIST)).add('Hello'); + ((fflib_MyList.IList) MY_MOCKS.doThrowWhen(fourth, MY_MOCK_LIST)).add('Hello'); + MY_MOCKS.stopStubbing(); + + // Then + assertExceptionMessageOnVoidMethod('fourth.'); + assertExceptionMessageOnVoidMethod('fourth.'); + } + + @isTest + static void thatExceptionIsthrownWhenStubbingIsNotDone() + { + MY_MOCKS.startStubbing(); + MY_MOCKS.when(MY_MOCK_LIST.get(1)); + MY_MOCKS.stopStubbing(); + + try + { + MY_MOCK_LIST.get(1); + + System.assert(false, 'an exception was expected'); + } + catch(fflib_ApexMocks.ApexMocksException myex) + { + System.assertEquals( + 'The stubbing is not correct, no return values have been set.', + myex.getMessage(), 'the message reported by the exception is not correct'); + } + } + + @isTest + static void thatExceptionIsthrownWhenReturnMultiPassEmptyList() + { + try + { + MY_MOCKS.startStubbing(); + MY_MOCKS.when(MY_MOCK_LIST.get(1)).thenReturnMulti(new List()); + MY_MOCKS.stopStubbing(); + System.assert(false, 'an exception was expected'); + } + catch(fflib_ApexMocks.ApexMocksException myex) + { + System.assertEquals( + 'The stubbing is not correct, no return values have been set.', + myex.getMessage(), 'the message reported by the exception is not correct'); + } + } + + @isTest + static void thatExceptionIsthrownWhenReturnMultiPassNullList() + { + try + { + MY_MOCKS.startStubbing(); + MY_MOCKS.when(MY_MOCK_LIST.get(1)).thenReturnMulti(null); + MY_MOCKS.stopStubbing(); + System.assert(false, 'an exception was expected'); + } + catch(fflib_ApexMocks.ApexMocksException myex) + { + System.assertEquals( + 'The stubbing is not correct, no return values have been set.', + myex.getMessage(), 'the message reported by the exception is not correct'); + } + } + + @isTest + static void thatExceptionIsthrownWhenThrowMultiPassEmptyList() + { + try + { + MY_MOCKS.startStubbing(); + MY_MOCKS.when(MY_MOCK_LIST.get(1)).thenThrowMulti(new List()); + MY_MOCKS.stopStubbing(); + System.assert(false, 'an exception was expected'); + } + catch(fflib_ApexMocks.ApexMocksException myex) + { + System.assertEquals( + 'The stubbing is not correct, no return values have been set.', + myex.getMessage(), 'the message reported by the exception is not correct'); + } + } + + @isTest + static void thatExceptionIsthrownWhenThrowMultiPassNullList() + { + try + { + MY_MOCKS.startStubbing(); + MY_MOCKS.when(MY_MOCK_LIST.get(1)).thenThrowMulti(null); + MY_MOCKS.stopStubbing(); + + System.assert(false, 'an exception was expected'); + } + catch(fflib_ApexMocks.ApexMocksException myex) + { + System.assertEquals( + 'The stubbing is not correct, no return values have been set.', + myex.getMessage(), 'the message reported by the exception is not correct'); + } + } + + @isTest + static void thatNullCanBeUsedAsReturnValue() + { + MY_MOCKS.startStubbing(); + MY_MOCKS.when(MY_MOCK_LIST.get(1)).thenReturn(null); + MY_MOCKS.stopStubbing(); + + + System.assertEquals(null, MY_MOCK_LIST.get(1), 'it should be possible stub using the null value'); + } + + @isTest + static void thatNullCanBeUsedAsExceptionvalue() + { + MY_MOCKS.startStubbing(); + MY_MOCKS.when(MY_MOCK_LIST.get(1)).thenThrow(null); + MY_MOCKS.stopStubbing(); + + System.assertEquals(null, MY_MOCK_LIST.get(1), 'it should be possible stub using the null value'); + } + + + + private static void assertExceptionMessage(String expectedMessage) + { + try + { + MY_MOCK_LIST.get(1); + System.assert(false, 'an exception was expected'); + } + catch(MyException myex) + { + System.assertEquals(expectedMessage, myex.getMessage(), 'the message reported by the exception is not correct'); + } + } + + private static void assertExceptionMessageForGet2(String expectedMessage) + { + try + { + MY_MOCK_LIST.get2(2, 'Hello.'); + System.assert(false, 'an exception was expected'); + } + catch(MyException myex) + { + System.assertEquals(expectedMessage, myex.getMessage(), 'the message reported by the exception is not correct'); + } + } + + private static void assertExceptionMessageOnVoidMethod(String expectedMessage) + { + try + { + MY_MOCK_LIST.add('Hello'); + System.assert(false, 'an exception was expected'); + } + catch(MyException myex) + { + System.assertEquals(expectedMessage, myex.getMessage(), 'the message reported by the exception is not correct'); + } + } + + private static void assertExceptionMessageOnAddMoreVoidMethod(String expectedMessage) + { + try + { + MY_MOCK_LIST.addMore('Hello'); + System.assert(false, 'an exception was expected'); + } + catch(MyException myex) + { + System.assertEquals(expectedMessage, myex.getMessage(), 'the message reported by the exception is not correct'); + } + } + + private static void assertReturnedValue(String expectedValue) + { + System.assertEquals(expectedValue, MY_MOCK_LIST.get(1), 'the method did not returned the expected value'); + } + + private static void assertReturnedValueForGet2(String expectedValue) + { + System.assertEquals(expectedValue, MY_MOCK_LIST.get2(2, 'Hello.'), 'the method did not returned the expected value'); + } + + @isTest + static void thatToStringReturnsSimpleStringValue() + { + System.assertEquals('fflib_ApexMocks', '' + new fflib_ApexMocks()); + } + + private class MyException extends Exception + { + } + + private class isOdd implements fflib_IMatcher + { + public Boolean matches(Object arg) + { + return arg instanceof Integer ? Math.mod((Integer)arg, 2) == 1: false; + } + } + + private class isEven implements fflib_IMatcher + { + public Boolean matches(Object arg) + { + return arg instanceof Integer ? Math.mod((Integer)arg, 2) == 0: false; + } + } +} diff --git a/sfdx-source/apex-mocks/tests/fflib_ApexMocksTest.cls-meta.xml b/sfdx-source/apex-mocks/tests/fflib_ApexMocksTest.cls-meta.xml new file mode 100644 index 00000000000..dd61d1f917e --- /dev/null +++ b/sfdx-source/apex-mocks/tests/fflib_ApexMocksTest.cls-meta.xml @@ -0,0 +1,5 @@ + + + 52.0 + Active + diff --git a/sfdx-source/apex-mocks/tests/fflib_ApexMocksUtilsTest.cls b/sfdx-source/apex-mocks/tests/fflib_ApexMocksUtilsTest.cls new file mode 100644 index 00000000000..5cdef400f47 --- /dev/null +++ b/sfdx-source/apex-mocks/tests/fflib_ApexMocksUtilsTest.cls @@ -0,0 +1,261 @@ +/** + * Copyright (c) 2014-2016, FinancialForce.com, inc + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the FinancialForce.com, inc nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +@isTest +public class fflib_ApexMocksUtilsTest +{ + public static Schema.FieldSet findAnyFieldSet() + { + for (Schema.SObjectType objectType : Schema.getGlobalDescribe().values()) + { + for (Schema.FieldSet fs : objectType.getDescribe().FieldSets.getMap().values()) + { + return fs; + } + } + + return null; + } + + @isTest + private static void makeRelationship_returnsObjectsWithRelationFieldSet() + { + //Given + Account acc = new Account( + Id = fflib_IDGenerator.generate(Account.SObjectType), + Name = 'AccName', + NumberOfEmployees = 7 + ); + + Contact contact1 = new Contact( + Id = fflib_IDGenerator.generate(Contact.SObjectType), + DoNotCall = true + ); + + Contact contact2 = new Contact( + Id = fflib_IDGenerator.generate(Contact.SObjectType), + DoNotCall = false + ); + + //When + Account accWithRelationships = ((List)fflib_ApexMocksUtils.makeRelationship( + List.class, + new List { acc }, + Contact.AccountId, + new List> { new List { contact1, contact2 }} + ))[0]; + + //Then + System.assertEquals(acc.Id, accWithRelationships.Id); + System.assertEquals(acc.Name, accWithRelationships.Name); + System.assertEquals(acc.NumberOfEmployees, accWithRelationships.NumberOfEmployees); + + //Assert relationship fields + List contacts = accWithRelationships.Contacts; + System.assertNotEquals(null, contacts); + System.assertEquals(2, contacts.size()); + + System.assertEquals(contact1.Id, contacts[0].Id); + System.assertEquals(contact1.DoNotCall, contacts[0].DoNotCall); + + System.assertEquals(contact2.Id, contacts[1].Id); + System.assertEquals(contact2.DoNotCall, contacts[1].DoNotCall); + } + + @isTest + private static void makeRelationship_GenericOverload_ReturnsObjectsWithRelationFieldSet() { + //Given + SObject acc = Schema.getGlobalDescribe().get('Account').newSObject(); + acc.put('Id', fflib_IDGenerator.generate(acc.getSObjectType())); + acc.put('Name', 'AccName'); + acc.put('NumberOfEmployees', 7); + + SObject contact1 = Schema.getGlobalDescribe().get('Contact').newSObject(); + contact1.put('Id', fflib_IDGenerator.generate(contact1.getSObjectType())); + contact1.put('DoNotCall', true); + + SObject contact2 = Schema.getGlobalDescribe().get('Contact').newSObject(); + contact2.put('Id', fflib_IDGenerator.generate(contact2.getSObjectType())); + contact2.put('DoNotCall', false); + + //When + SObject accWithRelationships = ((List)fflib_ApexMocksUtils.makeRelationship( + 'Account', + 'Contact', + new List { acc }, + 'AccountId', + new List> { new List { contact1, contact2 }} + + ))[0]; + + //Then + System.assertEquals(acc.Id, accWithRelationships.Id); + System.assertEquals(acc.get('Name'), accWithRelationships.get('Name')); + System.assertEquals(acc.get('NumberOfEmployees'), accWithRelationships.get('NumberOfEmployees')); + + //Assert relationship fields + List contacts = accWithRelationships.getSObjects('Contacts'); + System.assertNotEquals(null, contacts); + System.assertEquals(2, contacts.size()); + + System.assertEquals(contact1.Id, contacts[0].Id); + System.assertEquals((Boolean)contact1.get('DoNotCall'), (Boolean)contacts[0].get('DoNotCall')); + + System.assertEquals(contact2.Id, contacts[1].Id); + System.assertEquals((Boolean)contact2.get('DoNotCall'), (Boolean)contacts[1].get('DoNotCall')); + } + + @isTest + private static void makeRelationship_GenericOverload_ThrowsErrorOnInvalidParentType() { + + // Setup parent object + SObject acc = Schema.getGlobalDescribe().get('Account').newSObject(); + acc.put('Id', fflib_IDGenerator.generate(acc.getSObjectType())); + + // Setup child object + SObject cont = Schema.getGlobalDescribe().get('Contact').newSObject(); + cont.put('Id', fflib_IDGenerator.generate(cont.getSObjectType())); + + String errorMessage = ''; + try { + // Call method under test + SObject accWithRelationships = ((List)fflib_ApexMocksUtils.makeRelationship( + 'MyInvalidParentType', + 'Contact', + new List { acc }, + 'AccountId', + new List> { new List { cont }} + ))[0]; + } catch (Exception exc) { + errorMessage = exc.getMessage(); + } + System.assertEquals('SObject type not found: MyInvalidParentType', errorMessage); + } + + @isTest + private static void makeRelationship_GenericOverload_ThrowsErrorOnInvalidChildType() { + + // Setup parent object + SObject acc = Schema.getGlobalDescribe().get('Account').newSObject(); + acc.put('Id', fflib_IDGenerator.generate(acc.getSObjectType())); + + // Setup child object + SObject cont = Schema.getGlobalDescribe().get('Contact').newSObject(); + cont.put('Id', fflib_IDGenerator.generate(cont.getSObjectType())); + + String errorMessage = ''; + try { + // Call method under test + SObject accWithRelationships = ((List)fflib_ApexMocksUtils.makeRelationship( + 'Account', + 'MyInvalidChildType', + new List { acc }, + 'AccountId', + new List> { new List { cont }} + ))[0]; + } catch (Exception exc) { + errorMessage = exc.getMessage(); + } + System.assertEquals('SObject type not found: MyInvalidChildType', errorMessage); + } + + @isTest + private static void makeRelationship_GenericOverload_ThrowsErrorOnInvalidFieldName() { + + // Setup parent object + SObject acc = Schema.getGlobalDescribe().get('Account').newSObject(); + acc.put('Id', fflib_IDGenerator.generate(acc.getSObjectType())); + + // Setup child object + SObject cont = Schema.getGlobalDescribe().get('Contact').newSObject(); + cont.put('Id', fflib_IDGenerator.generate(cont.getSObjectType())); + + String errorMessage = ''; + try { + // Call method under test + SObject accWithRelationships = ((List)fflib_ApexMocksUtils.makeRelationship( + 'Account', + 'Contact', + new List { acc }, + 'MyInvalidField', + new List> { new List { cont }} + ))[0]; + } catch (Exception exc) { + errorMessage = exc.getMessage(); + } + System.assertEquals('SObject field not found: MyInvalidField', errorMessage); + } + + @isTest + static void setReadOnlyFields_CreatedByIdSetToCurrentUserId_IdFieldSetSuccessfully() { + + Account acc = new Account(); + Id userId = fflib_IDGenerator.generate((new User()).getSObjectType()); + + Test.startTest(); + acc = (Account)fflib_ApexMocksUtils.setReadOnlyFields( + acc, + Account.class, + new Map{Account.CreatedById => userId} + ); + Test.stopTest(); + + System.assertEquals(userId, acc.CreatedById); + } + + @isTest + static void setReadOnlyFields_LastReferencedDateSetOnAccount_DateTimeFieldSetSuccessfully() { + + Account acc = new Account(); + DateTime lastRefDate = DateTime.newInstanceGmt(2020, 1, 7, 23, 30, 0); + + Test.startTest(); + acc = (Account)fflib_ApexMocksUtils.setReadOnlyFields( + acc, + Account.class, + new Map {Account.LastReferencedDate => lastRefDate} + ); + Test.stopTest(); + + System.assertEquals(lastRefDate, acc.LastReferencedDate); + } + + @isTest + static void setReadOnlyFields_IsDeletedSetOnAccount_BooleanFieldSetSuccessfully() { + + Account acc = new Account(); + Boolean isDeleted = true; + + Test.startTest(); + acc = (Account)fflib_ApexMocksUtils.setReadOnlyFields( + acc, + Account.class, + new Map {Account.IsDeleted => isDeleted} + ); + Test.stopTest(); + + System.assertEquals(isDeleted, acc.IsDeleted); + } +} \ No newline at end of file diff --git a/sfdx-source/apex-mocks/tests/fflib_ApexMocksUtilsTest.cls-meta.xml b/sfdx-source/apex-mocks/tests/fflib_ApexMocksUtilsTest.cls-meta.xml new file mode 100644 index 00000000000..08c4dbd5cfa --- /dev/null +++ b/sfdx-source/apex-mocks/tests/fflib_ApexMocksUtilsTest.cls-meta.xml @@ -0,0 +1,4 @@ + + + 52.0 + diff --git a/sfdx-source/apex-mocks/tests/fflib_ArgumentCaptorTest.cls b/sfdx-source/apex-mocks/tests/fflib_ArgumentCaptorTest.cls new file mode 100644 index 00000000000..63c528bbd65 --- /dev/null +++ b/sfdx-source/apex-mocks/tests/fflib_ArgumentCaptorTest.cls @@ -0,0 +1,702 @@ +/* + * Copyright (c) 2016-2017 FinancialForce.com, inc. All rights reserved. + */ +/** + * @nodoc + */ +@isTest +private class fflib_ArgumentCaptorTest +{ + @isTest + static void thatArgumentValueIsCaptured() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('Fred'); + + // Then + fflib_ArgumentCaptor argument = fflib_ArgumentCaptor.forClass(String.class); + ((fflib_MyList.IList) mocks.verify(mockList)).add((String) argument.capture()); + + System.assertEquals('Fred', (String)argument.getValue(), 'the argument captured is not as expected'); + } + + @isTest + static void thatCanPerformFurtherAssertionsOnCapturedArgumentValue() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + //When + TestInnerClass testValue = new TestInnerClass(); + testValue.i = 4; + testValue.s = '5'; + + mockList.set(1, testValue); + + //Then + fflib_ArgumentCaptor argument = fflib_ArgumentCaptor.forClass(TestInnerClass.class); + + ((fflib_MyList.IList) mocks.verify(mockList)).set(fflib_Match.anyInteger(), argument.capture()); + + Object capturedArg = argument.getValue(); + System.assertNotEquals(null, capturedArg, 'CapturedArg should not be null'); + + System.assert(capturedArg instanceof TestInnerClass, 'CapturedArg should be SObject, instead was ' + capturedArg); + + TestInnerClass testValueCaptured = (TestInnerClass)capturedArg; + + System.assertEquals(4, testValueCaptured.i, 'the values inside the argument captured should be the same of the original one'); + System.assertEquals('5', testValueCaptured.s, 'the values inside the argument captured should be the same of the original one'); + } + + @isTest + static void thatCaptureArgumentOnlyFromVerifiedMethod() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('Fred'); + //the next call should be ignored because is not the method that has under verify, + //even if have the same type specified in the capturer. + mockList.addMore('Barney'); + + // Then + + fflib_ArgumentCaptor argument = fflib_ArgumentCaptor.forClass(String.class); + ((fflib_MyList.IList) mocks.verify(mockList)).add((String) argument.capture()); + + System.assertEquals('Fred', (String)argument.getValue(), 'the argument captured is not as expected'); + System.assertEquals(1, argument.getAllValues().size(), 'the argument captured should be only one'); + } + + @isTest + static void thatCaptureAllArgumentsForTheVerifiedMethods() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + List stringList = new List {'3'}; + // When + mockList.add('Fred'); + mockList.add(stringList); + + mockList.clear(); + + // Then + fflib_ArgumentCaptor argument = fflib_ArgumentCaptor.forClass(String.class); + + ((fflib_MyList.IList) mocks.verify(mockList)).add((String) argument.capture()); + ((fflib_MyList.IList) mocks.verify(mockList)).add((List) argument.capture()); + + System.assertEquals(stringList, (List)argument.getValue(), 'the argument captured is not as expected'); + + List argsCaptured = argument.getAllValues(); + + System.assertEquals(2, argsCaptured.size(), 'expected 2 argument to be captured'); + + System.assertEquals('Fred', (String) argsCaptured[0], 'the first value is not as expected'); + } + + @isTest + static void thatCaptureArgumentFromRequestedParameter() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('Fred', 'Barney', 'Wilma', 'Betty'); + + // Then + fflib_ArgumentCaptor argument = fflib_ArgumentCaptor.forClass(String.class); + + ((fflib_MyList.IList) mocks.verify(mockList)) + .add( + (String) fflib_Match.eq('Fred'), + (String) fflib_Match.eq('Barney'), + (String) argument.capture(), + (String) fflib_Match.eq('Betty')); + + System.assertEquals('Wilma', (String)argument.getValue(), + 'the argument captured is not as expected, should be Wilma because is the 3rd parameter in the call'); + } + + @isTest + static void thatCaptureLastArgument() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('Barney'); + mockList.add('Fred'); + + // Then + fflib_ArgumentCaptor argument = fflib_ArgumentCaptor.forClass(String.class); + + ((fflib_MyList.IList) mocks.verify(mockList, 2)).add((String) argument.capture()); + + System.assertEquals('Fred', (String)argument.getValue(), 'the argument captured is not as expected'); + } + + @isTest + static void thatCaptureAllArguments() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('Fred'); + mockList.add('Barney'); + mockList.add('Wilma'); + mockList.add('Betty'); + + // Then + fflib_ArgumentCaptor argument = fflib_ArgumentCaptor.forClass(String.class); + + ((fflib_MyList.IList) mocks.verify(mockList, 4)).add((String) argument.capture()); + + List argsCaptured = argument.getAllValues(); + + System.assertEquals(4, argsCaptured.size(), 'expected 4 argument to be captured'); + + System.assertEquals('Fred', (String) argsCaptured[0], 'the first value is not as expected'); + System.assertEquals('Barney', (String) argsCaptured[1], 'the second value is not as expected'); + System.assertEquals('Wilma', (String) argsCaptured[2], 'the third value is not as expected'); + System.assertEquals('Betty', (String) argsCaptured[3], 'the forth value is not as expected'); + } + + @isTest + static void thatCaptureAllArgumentsFromMultipleMethods() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('Fred'); + mockList.add('Barney'); + mockList.get2(3, 'pebble'); + + // Then + fflib_ArgumentCaptor argument = fflib_ArgumentCaptor.forClass(String.class); + + ((fflib_MyList.IList) mocks.verify(mockList, 2)).add((String) argument.capture()); + + ((fflib_MyList.IList) mocks.verify(mockList)) + .get2( + (Integer) fflib_Match.eq(3), + (String) argument.capture()); + + List argsCaptured = argument.getAllValues(); + + System.assertEquals(3, argsCaptured.size(), 'expected 3 argument to be captured'); + + System.assertEquals('Fred', (String) argsCaptured[0], 'the first value is not as expected'); + System.assertEquals('Barney', (String) argsCaptured[1], 'the second value is not as expected'); + + System.assertEquals('pebble', (String) argsCaptured[2], 'the third value is not as expected'); + } + + @isTest + static void thatCanHandleMultipleCapturesInOneMethodCall() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('Fred', 'Barney', 'Wilma', 'Betty'); + + // Then + fflib_ArgumentCaptor argument = fflib_ArgumentCaptor.forClass(String.class); + + ((fflib_MyList.IList) mocks.verify(mockList)) + .add( + (String) fflib_Match.eq('Fred'), + (String) argument.capture(), + (String) argument.capture(), + (String) fflib_Match.eq('Betty')); + + List argsCaptured = argument.getAllValues(); + + System.assertEquals(2, argsCaptured.size(), 'expected 2 argument to be captured'); + + System.assertEquals('Barney', (String) argsCaptured[0], 'the first value is not as expected'); + + System.assertEquals('Wilma', (String) argsCaptured[1], 'the second value is not as expected'); + } + + @isTest + static void thatDoesNotCaptureIfNotVerified() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('3'); + + // Then + fflib_ArgumentCaptor argument = fflib_ArgumentCaptor.forClass(List.class); + + ((fflib_MyList.IList) mocks.verify(mockList, fflib_ApexMocks.NEVER)) + .add((List) argument.capture()); + + List argsCaptured = argument.getAllValues(); + + System.assertEquals(0, argsCaptured.size(), 'expected 0 argument to be captured'); + + System.assertEquals(null, argument.getValue(), 'no value should be captured, so must return null'); + } + + @isTest + static void thatCaptureOnlyMethodsThatMatchesWithOtherMatcherAsWell() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('Same', 'Same', 'First call', 'First call'); + mockList.add('Same', 'Same', 'Second call', 'Second call'); + + // Then + fflib_ArgumentCaptor argument = fflib_ArgumentCaptor.forClass(String.class); + + ((fflib_MyList.IList) mocks.verify(mockList)).add( + fflib_Match.eqString('Same'), + fflib_Match.eqString('Same'), + (String)argument.capture(), + fflib_Match.eqString('First call')); + + System.assertEquals('First call', (String)argument.getValue()); + } + + @isTest + static void thatDoesNotCaptureAnythingWhenCaptorIsWrappedInAMatcher() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('Same', 'Same', 'First call', 'First call'); + mockList.add('Same', 'Same', 'Second call', 'Second call'); + + // Then + fflib_ArgumentCaptor argument = fflib_ArgumentCaptor.forClass(String.class); + + ((fflib_MyList.IList) mocks.verify(mockList)).add( + (String) fflib_Match.allOf( + fflib_Match.eqString('Same'), + fflib_Match.eqString('Same'), + argument.capture()), + (String) fflib_Match.allOf( + fflib_Match.eqString('Same'), + fflib_Match.eqString('Same'), + argument.capture()), + (String) fflib_Match.allOf( + argument.capture(), + fflib_Match.eqString('First call')), + (String) fflib_Match.allOf( + argument.capture(), + fflib_Match.eqString('First call')) + ); + + List capturedValues = argument.getAllValues(); + + System.assertEquals(0, capturedValues.size(), + 'nothing should have been capture because the matcher it not really a capture type, but a allOf()'); + System.assertEquals(null, (String)argument.getValue(), + 'nothing should have been capture because the matcher it not really a capture type, but a allOf()'); + } + + @isTest + static void thatArgumentValueIsCapturedWithInOrderVerification() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(mocks, new List{ mockList }); + + // When + mockList.add('Fred'); + + // Then + fflib_ArgumentCaptor argument = fflib_ArgumentCaptor.forClass(String.class); + + ((fflib_MyList.IList)inOrder1.verify(mockList, mocks.calls(1))).add((String) argument.capture()); + + System.assertEquals('Fred', (String)argument.getValue(), 'the argument captured is not as expected'); + } + + @isTest + static void thatCanPerformFurtherAssertionsOnCapturedArgumentValueWithInOrderVerification() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(mocks, new List{ mockList }); + + + //When + TestInnerClass testValue = new TestInnerClass(); + testValue.i = 4; + testValue.s = '5'; + + mockList.set(1, testValue); + + //Then + fflib_ArgumentCaptor argument = fflib_ArgumentCaptor.forClass(TestInnerClass.class); + + ((fflib_MyList.IList) inOrder1.verify(mockList, mocks.calls(1))).set(fflib_Match.anyInteger(), argument.capture()); + + Object capturedArg = argument.getValue(); + System.assertNotEquals(null, capturedArg, 'CapturedArg should not be null'); + + System.assert(capturedArg instanceof TestInnerClass, 'CapturedArg should be SObject, instead was ' + capturedArg); + + TestInnerClass testValueCaptured = (TestInnerClass)capturedArg; + + System.assertEquals(4, testValueCaptured.i, 'the values inside the argument captured should be the same of the original one'); + System.assertEquals('5', testValueCaptured.s, 'the values inside the argument captured should be the same of the original one'); + } + + @isTest + static void thatCaptureArgumentOnlyFromVerifiedMethodWithInOrderVerification() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(mocks, new List{ mockList }); + + + // When + mockList.add('Fred'); + //the next call should be ignored because is not the method that has under verify, + //even if have the same type specified in the capturer. + mockList.addMore('Barney'); + + // Then + + fflib_ArgumentCaptor argument = fflib_ArgumentCaptor.forClass(String.class); + ((fflib_MyList.IList) inOrder1.verify(mockList, mocks.calls(1))).add((String) argument.capture()); + + System.assertEquals('Fred', (String)argument.getValue(), 'the argument captured is not as expected'); + System.assertEquals(1, argument.getAllValues().size(), 'the argument captured should be only one'); + } + + @isTest + static void thatCaptureAllArgumentsForTheVerifiedMethodsWithInOrderVerification() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(mocks, new List{ mockList }); + + + List stringList = new List {'3'}; + // When + mockList.add('Fred'); + mockList.add(stringList); + + mockList.clear(); + + // Then + fflib_ArgumentCaptor argument = fflib_ArgumentCaptor.forClass(String.class); + + ((fflib_MyList.IList) inOrder1.verify(mockList, mocks.calls(1))).add((String) argument.capture()); + ((fflib_MyList.IList) inOrder1.verify(mockList, mocks.calls(1))).add((List) argument.capture()); + + System.assertEquals(stringList, (List)argument.getValue(), 'the argument captured is not as expected'); + + List argsCaptured = argument.getAllValues(); + + System.assertEquals(2, argsCaptured.size(), 'expected 2 argument to be captured'); + + System.assertEquals('Fred', (String) argsCaptured[0], 'the first value is not as expected'); + } + + @isTest + static void thatCaptureArgumentFromRequestedParameterWithInOrderVerification() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(mocks, new List{ mockList }); + + + // When + mockList.add('Fred', 'Barney', 'Wilma', 'Betty'); + + // Then + fflib_ArgumentCaptor argument = fflib_ArgumentCaptor.forClass(String.class); + + ((fflib_MyList.IList) inOrder1.verify(mockList, mocks.calls(1))) + .add( + (String) fflib_Match.eq('Fred'), + (String) fflib_Match.eq('Barney'), + (String) argument.capture(), + (String) fflib_Match.eq('Betty')); + + System.assertEquals('Wilma', (String)argument.getValue(), + 'the argument captured is not as expected, should be Wilma because is the 3rd parameter in the call'); + } + + @isTest + static void thatCaptureLastArgumentWithInOrderVerification() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(mocks, new List{ mockList }); + + + // When + mockList.add('Barney'); + mockList.add('Fred'); + + // Then + fflib_ArgumentCaptor argument = fflib_ArgumentCaptor.forClass(String.class); + + ((fflib_MyList.IList) inOrder1.verify(mockList, mocks.calls(2))).add((String) argument.capture()); + + System.assertEquals('Fred', (String)argument.getValue(), 'the argument captured is not as expected'); + } + + @isTest + static void thatCaptureAllArgumentsWithInOrderVerification() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(mocks, new List{ mockList }); + + + // When + mockList.add('Fred'); + mockList.add('Barney'); + mockList.add('Wilma'); + mockList.add('Betty'); + + // Then + fflib_ArgumentCaptor argument = fflib_ArgumentCaptor.forClass(String.class); + + ((fflib_MyList.IList) inOrder1.verify(mockList, mocks.calls(4))).add((String) argument.capture()); + + List argsCaptured = argument.getAllValues(); + + System.assertEquals(4, argsCaptured.size(), 'expected 4 argument to be captured'); + + System.assertEquals('Fred', (String) argsCaptured[0], 'the first value is not as expected'); + System.assertEquals('Barney', (String) argsCaptured[1], 'the second value is not as expected'); + System.assertEquals('Wilma', (String) argsCaptured[2], 'the third value is not as expected'); + System.assertEquals('Betty', (String) argsCaptured[3], 'the forth value is not as expected'); + } + + @isTest + static void thatCaptureAllArgumentsFromMultipleMethodsWithInOrderVerification() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(mocks, new List{ mockList }); + + + // When + mockList.add('Fred'); + mockList.add('Barney'); + mockList.get2(3, 'pebble'); + + // Then + fflib_ArgumentCaptor argument = fflib_ArgumentCaptor.forClass(String.class); + + ((fflib_MyList.IList) inOrder1.verify(mockList, mocks.calls(2))).add((String) argument.capture()); + + ((fflib_MyList.IList) inOrder1.verify(mockList, mocks.calls(1))) + .get2( + (Integer) fflib_Match.eq(3), + (String) argument.capture()); + + List argsCaptured = argument.getAllValues(); + + System.assertEquals(3, argsCaptured.size(), 'expected 3 argument to be captured'); + + System.assertEquals('Fred', (String) argsCaptured[0], 'the first value is not as expected'); + System.assertEquals('Barney', (String) argsCaptured[1], 'the second value is not as expected'); + + System.assertEquals('pebble', (String) argsCaptured[2], 'the third value is not as expected'); + } + + @isTest + static void thatCanHandleMultipleCapturesInOneMethodCallWithInOrderVerification() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(mocks, new List{ mockList }); + + + // When + mockList.add('Fred', 'Barney', 'Wilma', 'Betty'); + + // Then + fflib_ArgumentCaptor argument = fflib_ArgumentCaptor.forClass(String.class); + + ((fflib_MyList.IList) inOrder1.verify(mockList, mocks.calls(1))) + .add( + (String) fflib_Match.eq('Fred'), + (String) argument.capture(), + (String) argument.capture(), + (String) fflib_Match.eq('Betty')); + + List argsCaptured = argument.getAllValues(); + + System.assertEquals(2, argsCaptured.size(), 'expected 2 argument to be captured'); + + System.assertEquals('Barney', (String) argsCaptured[0], 'the first value is not as expected'); + + System.assertEquals('Wilma', (String) argsCaptured[1], 'the second value is not as expected'); + } + + @isTest + static void thatDoesNotCaptureIfNotVerifiedWithInOrderVerification() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(mocks, new List{ mockList }); + + + // When + mockList.add('3'); + + // Then + fflib_ArgumentCaptor argument = fflib_ArgumentCaptor.forClass(List.class); + + ((fflib_MyList.IList) inOrder1.verify(mockList, mocks.never())) + .add((List) argument.capture()); + + List argsCaptured = argument.getAllValues(); + + System.assertEquals(0, argsCaptured.size(), 'expected 0 argument to be captured'); + + System.assertEquals(null, argument.getValue(), 'no value should be captured, so must return null'); + } + + @isTest + static void thatCaptureOnlyMethodsThatMatchesWithOtherMatcherAsWellWithInOrderVerification() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(mocks, new List{ mockList }); + + + // When + mockList.add('Same', 'Same', 'First call', 'First call'); + mockList.add('Same', 'Same', 'Second call', 'Second call'); + + // Then + fflib_ArgumentCaptor argument = fflib_ArgumentCaptor.forClass(String.class); + + ((fflib_MyList.IList) inOrder1.verify(mockList, mocks.calls(1))).add( + fflib_Match.eqString('Same'), + fflib_Match.eqString('Same'), + (String)argument.capture(), + fflib_Match.eqString('First call')); + + System.assertEquals('First call', (String)argument.getValue()); + } + + @isTest + static void thatDoesNotCaptureAnythingWhenCaptorIsWrappedInAMatcherWithInOrderVerification() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(mocks, new List{ mockList }); + + + // When + mockList.add('Same', 'Same', 'First call', 'First call'); + mockList.add('Same', 'Same', 'Second call', 'Second call'); + + // Then + fflib_ArgumentCaptor argument = fflib_ArgumentCaptor.forClass(String.class); + + ((fflib_MyList.IList) inOrder1.verify(mockList, mocks.calls(1))).add( + (String) fflib_Match.allOf( + fflib_Match.eqString('Same'), + fflib_Match.eqString('Same'), + argument.capture()), + (String) fflib_Match.allOf( + fflib_Match.eqString('Same'), + fflib_Match.eqString('Same'), + argument.capture()), + (String) fflib_Match.allOf( + argument.capture(), + fflib_Match.eqString('First call')), + (String) fflib_Match.allOf( + argument.capture(), + fflib_Match.eqString('First call')) + ); + + List capturedValues = argument.getAllValues(); + + System.assertEquals(0, capturedValues.size(), + 'nothing should have been capture because the matcher it not really a capture type, but a allOf()'); + System.assertEquals(null, (String)argument.getValue(), + 'nothing should have been capture because the matcher it not really a capture type, but a allOf()'); + } + + @isTest + static void thatCaptureAllArgumentswhenMethodIsCalledWithTheSameArgument() + { + // Given + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_MyList mockList = (fflib_MyList)mocks.mock(fflib_MyList.class); + + // When + mockList.add('Fred'); + mockList.add('Barney'); + mockList.add('Wilma'); + mockList.add('Barney'); + mockList.add('Barney'); + mockList.add('Betty'); + + // Then + fflib_ArgumentCaptor argument = fflib_ArgumentCaptor.forClass(String.class); + + ((fflib_MyList.IList) mocks.verify(mockList, 6)).add((String) argument.capture()); + + List argsCaptured = argument.getAllValues(); + + System.assertEquals(6, argsCaptured.size(), 'expected 6 arguments to be captured'); + + System.assertEquals('Fred', (String) argsCaptured[0], 'the first value is not as expected'); + System.assertEquals('Barney', (String) argsCaptured[1], 'the second value is not as expected'); + System.assertEquals('Wilma', (String) argsCaptured[2], 'the third value is not as expected'); + System.assertEquals('Barney', (String) argsCaptured[3], 'the fourth value is not as expected'); + System.assertEquals('Barney', (String) argsCaptured[4], 'the fifth value is not as expected'); + System.assertEquals('Betty', (String) argsCaptured[5], 'the sixth value is not as expected'); + } + + + private class TestInnerClass + { + public Integer i; + public String s; + } +} \ No newline at end of file diff --git a/sfdx-source/apex-mocks/tests/fflib_ArgumentCaptorTest.cls-meta.xml b/sfdx-source/apex-mocks/tests/fflib_ArgumentCaptorTest.cls-meta.xml new file mode 100644 index 00000000000..dd61d1f917e --- /dev/null +++ b/sfdx-source/apex-mocks/tests/fflib_ArgumentCaptorTest.cls-meta.xml @@ -0,0 +1,5 @@ + + + 52.0 + Active + diff --git a/sfdx-source/apex-mocks/tests/fflib_IDGeneratorTest.cls b/sfdx-source/apex-mocks/tests/fflib_IDGeneratorTest.cls new file mode 100644 index 00000000000..e9c893ef6dc --- /dev/null +++ b/sfdx-source/apex-mocks/tests/fflib_IDGeneratorTest.cls @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2014, FinancialForce.com, inc + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the FinancialForce.com, inc nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +@isTest +private class fflib_IDGeneratorTest +{ + @isTest + static void itShouldGenerateValidIDs() + { + String id1 = fflib_IDGenerator.generate(Account.SObjectType); + String id2 = fflib_IDGenerator.generate(Account.SObjectType); + String id3 = fflib_IDGenerator.generate(Account.SObjectType); + String id4 = fflib_IDGenerator.generate(Account.SObjectType); + String id5 = fflib_IDGenerator.generate(Account.SObjectType); + String id6 = fflib_IDGenerator.generate(Account.SObjectType); + String id7 = fflib_IDGenerator.generate(Account.SObjectType); + String id8 = fflib_IDGenerator.generate(Account.SObjectType); + String id9 = fflib_IDGenerator.generate(Account.SObjectType); + String id10 = fflib_IDGenerator.generate(Account.SObjectType); + String id11 = fflib_IDGenerator.generate(Account.SObjectType); + + System.assertEquals('001000000000001AAA', id1); + System.assertEquals('001000000000002AAA', id2); + System.assertEquals('001000000000003AAA', id3); + System.assertEquals('001000000000004AAA', id4); + System.assertEquals('001000000000005AAA', id5); + System.assertEquals('001000000000006AAA', id6); + System.assertEquals('001000000000007AAA', id7); + System.assertEquals('001000000000008AAA', id8); + System.assertEquals('001000000000009AAA', id9); + System.assertEquals('001000000000010AAA', id10); + System.assertEquals('001000000000011AAA', id11); + } +} \ No newline at end of file diff --git a/sfdx-source/apex-mocks/tests/fflib_IDGeneratorTest.cls-meta.xml b/sfdx-source/apex-mocks/tests/fflib_IDGeneratorTest.cls-meta.xml new file mode 100644 index 00000000000..08c4dbd5cfa --- /dev/null +++ b/sfdx-source/apex-mocks/tests/fflib_IDGeneratorTest.cls-meta.xml @@ -0,0 +1,4 @@ + + + 52.0 + diff --git a/sfdx-source/apex-mocks/tests/fflib_InOrderTest.cls b/sfdx-source/apex-mocks/tests/fflib_InOrderTest.cls new file mode 100644 index 00000000000..43e96d8d936 --- /dev/null +++ b/sfdx-source/apex-mocks/tests/fflib_InOrderTest.cls @@ -0,0 +1,1831 @@ +/* + Copyright (c) 2014-2017 FinancialForce.com, inc. All rights reserved. + */ + +@isTest +private class fflib_InOrderTest +{ + private static fflib_ApexMocks MY_MOCKS = new fflib_ApexMocks(); + + @isTest + static void thatVerifyInOrderAllTheMethodsCalled() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + // When + firstMock.add('1-1'); + firstMock.add('1-2'); + firstMock.add('1-3'); + firstMock.add('1-4'); + + // Then + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('1-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('1-2'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('1-3'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('1-4'); + + try + { + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('1-2'); + System.assert(false, 'It should fail because 1-2 is in the wrong order'); + } + catch (fflib_ApexMocks.ApexMocksException error) + { + System.assertEquals('EXPECTED COUNT: 1 in order' + +'\nACTUAL COUNT: 0' + +'\nMETHOD: fflib_MyList__sfdc_ApexStub.add(String)' + +'\n---' + +'\nACTUAL ARGS: ("1-1"), ("1-2"), ("1-3"), ("1-4")' + +'\n---' + +'\nEXPECTED ARGS: "1-2"', + error.getMessage(), + 'Unexpected verify fail message'); + } + } + + @isTest + static void thatVerifyInOrderDifferentMethodsCalledWithSameArguments() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + // When + firstMock.add('1-1'); + firstMock.addMore('1-1'); + firstMock.add('1-2'); + firstMock.addMore('1-2'); + firstMock.add('1-3'); + firstMock.addMore('1-3'); + firstMock.add('1-4'); + firstMock.addMore('1-4'); + + // Then + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('1-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).addMore('1-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).addMore('1-3'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('1-4'); + } + + @isTest + static void thatVerifyInOrderDifferentMethodsCalledWithSameArgumentsOrderFail() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + // When + firstMock.add('1-1'); + firstMock.addMore('1-1'); + firstMock.add('1-2'); + firstMock.addMore('1-2'); + + // Then + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).addMore('1-1'); + + try + { + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('1-1'); + System.assert(false, 'It should fail because 1-1 is called before the addMore(1-1)'); + } + catch (fflib_ApexMocks.ApexMocksException error) + { + System.assertEquals('EXPECTED COUNT: 1 in order' + +'\nACTUAL COUNT: 0' + +'\nMETHOD: fflib_MyList__sfdc_ApexStub.add(String)' + +'\n---' + +'\nACTUAL ARGS: ("1-1"), ("1-1"), ("1-2"), ("1-2")' + +'\n---' + +'\nEXPECTED ARGS: "1-1"', + error.getMessage(), + 'Unexpected verify fail message'); + } + } + + @isTest + static void thatVerifyInOrderDifferentMethodsCalledWithSameArgumentsDoubleCallFail() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + // When + firstMock.add('1-1'); + firstMock.addMore('1-1'); + firstMock.add('1-2'); + firstMock.addMore('1-2'); + + // Then + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).addMore('1-1'); + + try + { + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).addMore('1-1'); + System.assert(false, 'It should fail because addMore(1-1) is called only Once'); + } + catch (fflib_ApexMocks.ApexMocksException error) + { + System.assertEquals('EXPECTED COUNT: 1 in order' + + '\nACTUAL COUNT: 0' + + '\nMETHOD: fflib_MyList__sfdc_ApexStub.addMore(String)' + + '\n---' + + '\nACTUAL ARGS: ("1-1"), ("1-1"), ("1-2"), ("1-2")' + + '\n---' + + '\nEXPECTED ARGS: "1-1"', + error.getMessage(), + 'Unexpected verify fail message'); + } + } + + @isTest + static void thatVerifyInOrderCallMethodWithMatches() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + // When + firstMock.add('1-0'); + firstMock.add('1-11'); + firstMock.add('1-12'); + firstMock.add('1-3'); + firstMock.add('1-4'); + + // Then + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(2))).add(fflib_Match.stringStartsWith('1-1')); + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('1-4'); + } + + @isTest + static void thatVerifyInOrderCallMethodWithMatchesFailsIfVerifyACallAlreadyInTheMatcher() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + // When + firstMock.add('1-0'); + firstMock.add('1-11'); + firstMock.add('1-12'); + firstMock.add('1-3'); + firstMock.add('1-4'); + + // Then + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(2))).add(fflib_Match.stringStartsWith('1-1')); + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('1-4'); + + try + { + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('1-11'); + System.assert(false, 'It should fail because addMore(1-11) has been already verified using the matchers'); + } + catch (fflib_ApexMocks.ApexMocksException error) + { + System.assertEquals('EXPECTED COUNT: 1 in order' + +'\nACTUAL COUNT: 0' + +'\nMETHOD: fflib_MyList__sfdc_ApexStub.add(String)' + +'\n---' + +'\nACTUAL ARGS: ("1-0"), ("1-11"), ("1-12"), ("1-3"), ("1-4")' + +'\n---' + +'\nEXPECTED ARGS: "1-11"', + error.getMessage(), + 'Unexpected verify fail message'); + } + } + + @isTest + static void thatVerifyInOrderCallMethodWithMultipleMatches() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + // When + + firstMock.add('1-0'); + firstMock.add('1-1'); + firstMock.add('1-3'); + firstMock.add('1-4'); + + + firstMock.add('2-0'); + firstMock.add('2-1'); + firstMock.add('2-3'); + firstMock.add('2-4'); + + // Then + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(4))).add(fflib_Match.stringStartsWith('1-')); + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(4))).add(fflib_Match.stringStartsWith('2-')); + } + + @isTest + static void thatVerifyInOrderCallMethodWithMultipleMatchesMixed() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + // When + firstMock.add('1-1'); + firstMock.add('2-1'); + firstMock.add('1-2'); + firstMock.add('2-2'); + firstMock.add('1-3'); + firstMock.add('2-3'); + firstMock.add('1-4'); + firstMock.add('2-4'); + + // Then + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(4))).add(fflib_Match.stringStartsWith('1-')); + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add(fflib_Match.stringStartsWith('2-')); + } + + @isTest + static void thatVerifyInOrderCallMethodWithMultipleMatchesMixedFailWhenMatcherHaveAlreadyVerifiedMethod() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + // When + firstMock.add('1-1'); + firstMock.add('2-1'); + firstMock.add('1-2'); + firstMock.add('2-2'); + firstMock.add('1-3'); + firstMock.add('2-3'); + firstMock.add('1-4'); + firstMock.add('2-4'); + + // Then + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(4))).add(fflib_Match.stringStartsWith('1-')); + + try + { + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(4))).add('1-11'); + System.assert(false, 'It should fail because only one call for the 2- is available to verify'); + } + catch (fflib_ApexMocks.ApexMocksException error) + { + System.assertEquals('EXPECTED COUNT: 4 in order' + +'\nACTUAL COUNT: 0' + +'\nMETHOD: fflib_MyList__sfdc_ApexStub.add(String)' + +'\n---' + +'\nACTUAL ARGS: ("1-1"), ("2-1"), ("1-2"), ("2-2"), ("1-3"), ("2-3"), ("1-4"), ("2-4")' + +'\n---' + +'\nEXPECTED ARGS: "1-11"', + error.getMessage(), + 'Unexpected verify fail message'); + } + } + + @isTest + static void thatVerifyInOrderCanSkipMethodsCalledUntilFindTheOneThatNeedsVerify() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + // When + firstMock.add('1-1'); + firstMock.add('1-2'); + firstMock.add('1-3'); + firstMock.add('1-4'); + + // Then + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('1-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('1-4'); + + try + { + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('1-2'); + System.assert(false, 'It should fail because is out of order'); + } + catch (fflib_ApexMocks.ApexMocksException error) + { + System.assertEquals('EXPECTED COUNT: 1 in order' + +'\nACTUAL COUNT: 0' + +'\nMETHOD: fflib_MyList__sfdc_ApexStub.add(String)' + +'\n---' + +'\nACTUAL ARGS: ("1-1"), ("1-2"), ("1-3"), ("1-4")' + +'\n---' + +'\nEXPECTED ARGS: "1-2"', + error.getMessage(), + 'Unexpected verify fail message'); + } + } + + @isTest + static void thatVerifyInOrderCanHandleMultipleMethodsCalls() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + // When + firstMock.add('1-1'); + firstMock.add('1-2'); + firstMock.add('1-2'); + firstMock.add('1-2'); + firstMock.add('1-3'); + firstMock.add('1-4'); + + // Then + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(3))).add('1-2'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('1-4'); + } + + @isTest + static void thatVerifyInOrderCanHandleMultipleMethodsCallsAndNotFailsIfVerifyCountIsGreaterThenExpected() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + // When + firstMock.add('1-1'); + firstMock.add('1-2'); + firstMock.add('1-2'); + firstMock.add('1-2'); + firstMock.add('1-3'); + firstMock.add('1-4'); + + // Then + + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(2))).add('1-2'); + } + + @isTest + static void thatVerifyInOrderCanHandleMultipleMethodsCallsButFailsIfVerifyCountIsLessThenExpected() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + // When + firstMock.add('1-1'); + firstMock.add('1-2'); + firstMock.add('1-2'); + firstMock.add('1-2'); + firstMock.add('1-3'); + firstMock.add('1-4'); + + // Then + try + { + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(4))).add('1-2'); + System.assert(false, 'It should fail because is actually called only 3 times'); + } + catch (fflib_ApexMocks.ApexMocksException e) + { + System.assertEquals('EXPECTED COUNT: 4 in order' + +'\nACTUAL COUNT: 3' + +'\nMETHOD: fflib_MyList__sfdc_ApexStub.add(String)' + +'\n---' + +'\nACTUAL ARGS: ("1-1"), ("1-2"), ("1-2"), ("1-2"), ("1-3"), ("1-4")' + +'\n---' + +'\nEXPECTED ARGS: "1-2"', + e.getMessage(), + 'Unexpected verify fail message'); + } + } + + @isTest + static void thatVerifyInOrderCanHandleMultipleMocks() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_MyList secondMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_MyList thirdMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + + fflib_InOrder inOrder = new fflib_InOrder(MY_MOCKS, new List{ firstMock, secondMock }); + + // When + firstMock.add('1-1'); + secondMock.add('2-1'); + thirdMock.add('3-1'); + + firstMock.add('1-2'); + secondMock.add('2-2'); + thirdMock.add('3-2'); + + firstMock.add('1-1'); + firstMock.add('1-3'); + secondMock.add('2-3'); + thirdMock.add('3-3'); + + // Then + ((fflib_MyList.IList)inOrder.verify(firstMock, MY_MOCKS.calls(1))).add('1-1'); + ((fflib_MyList.IList)inOrder.verify(secondMock, MY_MOCKS.calls(1))).add('2-1'); + ((fflib_MyList.IList)inOrder.verify(firstMock, MY_MOCKS.calls(1))).add('1-2'); + ((fflib_MyList.IList)inOrder.verify(secondMock, MY_MOCKS.calls(1))).add('2-2'); + ((fflib_MyList.IList)inOrder.verify(firstMock, MY_MOCKS.calls(1))).add('1-3'); + ((fflib_MyList.IList)inOrder.verify(secondMock, MY_MOCKS.calls(1))).add('2-3'); + + ((fflib_MyList.IList)MY_MOCKS.verify(thirdMock, MY_MOCKS.times(3))).add(fflib_Match.stringStartsWith('3-')); + } + + @isTest + static void thatVerifyInOrderCanHandleMixedInOrderInstance() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_MyList secondMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_MyList thirdMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + fflib_InOrder inOrder2 = new fflib_InOrder(MY_MOCKS, new List{ firstMock, secondMock }); + + // When + firstMock.add('1-1'); + secondMock.add('2-1'); + thirdMock.add('3-1'); + + firstMock.add('1-2'); + secondMock.add('2-2'); + thirdMock.add('3-2'); + + firstMock.add('1-1'); + firstMock.add('1-3'); + secondMock.add('2-3'); + thirdMock.add('3-3'); + + // Then + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(2))).add('1-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('1-3'); + + + ((fflib_MyList.IList)inOrder2.verify(firstMock, MY_MOCKS.calls(1))).add('1-1'); + + ((fflib_MyList.IList)inOrder2.verify(firstMock, MY_MOCKS.calls(1))).add('1-2'); + ((fflib_MyList.IList)inOrder2.verify(secondMock, MY_MOCKS.calls(1))).add('2-2'); + ((fflib_MyList.IList)inOrder2.verify(firstMock, MY_MOCKS.calls(1))).add('1-3'); + ((fflib_MyList.IList)inOrder2.verify(secondMock, MY_MOCKS.calls(1))).add('2-3'); + + ((fflib_MyList.IList)MY_MOCKS.verify(thirdMock, MY_MOCKS.times(3))).add(fflib_Match.stringStartsWith('3-')); + } + + @isTest + static void thatVerifyInOrderThrownExceptionIfVerifyMockInstanceNotInTheSet() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_MyList secondMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + // When + firstMock.add('1-1'); + secondMock.add('2-1'); + + // Then + + try + { + ((fflib_MyList.IList)inOrder1.verify(secondMock, MY_MOCKS.calls(1))).add('2-1'); + System.assert(false, 'An exception was expected, because this verify is not in the list of the mocks to verify'); + } + catch(fflib_ApexMocks.ApexMocksException mockexcep) + { + System.assertEquals('EXPECTED COUNT: 1 in order' + + '\nACTUAL COUNT: 0' + + '\nMETHOD: fflib_MyList__sfdc_ApexStub.add(String)' + + '\n---' + + '\nACTUAL ARGS: ("1-1"), ("2-1")' + + '\n---' + + '\nEXPECTED ARGS: "2-1"', + mockexcep.getMessage(), + 'Unexpected verify fail message'); + } + } + + @isTest + static void thatVerifyInOrderThrownExceptionWithCustomMessage() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + String customErrorMesage = 'Some custom error message'; + + // When + firstMock.add('1-1'); + firstMock.add('1-2'); + + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('1-2'); + // Then + try + { + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.description(customErrorMesage))).add('1-1'); + System.assert(false, 'expected some exception '); + } + catch (fflib_ApexMocks.ApexMocksException e) + { + System.assertEquals('EXPECTED COUNT: 1 in order' + + '\nACTUAL COUNT: 0' + + '\nMETHOD: fflib_MyList__sfdc_ApexStub.add(String)' + + '\nSome custom error message' + + '\n---' + + '\nACTUAL ARGS: ("1-1"), ("1-2")' + + '\n---' + + '\nEXPECTED ARGS: "1-1"', + e.getMessage(), + 'Unexpected verify fail message'); + } + } + + @isTest + static void thatVerifyAtMostThrowsExceptionBecauseNotImplemented() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + // When + firstMock.add('1-1'); + firstMock.add('2-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('2-1'); + + // Then + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('1-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('2-1'); + + try + { + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.atMost(3))).add('1-1'); + System.assert(false, 'an exception was expected because the method is not implemented for the InOrder class'); + } + catch (fflib_ApexMocks.ApexMocksException mockExcept) + { + String expectedMessage = 'The atMost method is not implemented for the fflib_InOrder class'; + System.assertEquals(expectedMessage, mockExcept.getMessage(), ' the error message is not as expected'); + } + } + + @isTest + static void thatVerifyBetweenThrowsExceptionBecauseNotImplemented() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + // When + firstMock.add('1-1'); + firstMock.add('2-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('2-1'); + + // Then + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('1-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('2-1'); + + try + { + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.between(3, 5))).add('1-1'); + System.assert(false, 'an exception was expected because the method is not implemented for the InOrder class'); + } + catch (fflib_ApexMocks.ApexMocksException mockExcept) + { + String expectedMessage = 'The between method is not implemented for the fflib_InOrder class'; + System.assertEquals(expectedMessage, mockExcept.getMessage(), ' the error message is not as expected'); + } + } + + @isTest + static void thatVerifyNever() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + // When + firstMock.add('1-1'); + firstMock.add('2-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('2-1'); + + // Then + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('1-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('2-1'); + + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.never())).add('3-1'); + } + + @isTest + static void thatVerifyNeverWithMatchers() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + // When + firstMock.add('1-1'); + firstMock.add('2-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('2-1'); + + // Then + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('1-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('2-1'); + + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.never())).add(fflib_Match.stringStartsWith('3-')); + } + + @isTest + static void thatVerifyNeverFailsWhenCalled() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + // When + firstMock.add('1-1'); + firstMock.add('2-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('2-1'); + + // Then + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('1-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('2-1'); + + try + { + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.never())).add('1-1'); + System.assert(false, 'expected some exception because the method has been called'); + } + catch (fflib_ApexMocks.ApexMocksException e) + { + System.assertEquals('EXPECTED COUNT: 0 in order' + + '\nACTUAL COUNT: 4' + + '\nMETHOD: fflib_MyList__sfdc_ApexStub.add(String)' + + '\n---' + + '\nACTUAL ARGS: ("1-1"), ("2-1"), ("1-1"), ("1-1"), ("1-1"), ("1-1"), ("2-1")' + + '\n---' + + '\nEXPECTED ARGS: "1-1"', + e.getMessage(), + 'Unexpected verify fail message'); + } + } + + @isTest + static void thatVerifyNeverFailsWhenCalledWithMatchers() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + // When + firstMock.add('1-1'); + firstMock.add('2-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('2-1'); + + // Then + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('1-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('2-1'); + + try + { + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.never())).add(fflib_Match.stringStartsWith('1-')); + System.assert(false, 'expected some exception because the method has been called'); + } + catch (fflib_ApexMocks.ApexMocksException e) + { + System.assertEquals('EXPECTED COUNT: 0 in order' + + '\nACTUAL COUNT: 4' + + '\nMETHOD: fflib_MyList__sfdc_ApexStub.add(String)' + + '\n---' + + '\nACTUAL ARGS: ("1-1"), ("2-1"), ("1-1"), ("1-1"), ("1-1"), ("1-1"), ("2-1")' + + '\n---' + + '\nEXPECTED ARGS: [starts with "1-"]', + e.getMessage(), + 'Unexpected verify fail message'); + } + } + + @isTest + static void thatVerifyThrowsExceptionWhenCallsIsInvochedFromStandardMock() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + // When + firstMock.add('1-1'); + firstMock.add('2-1'); + firstMock.add('1-1'); + + // Then + try + { + ((fflib_MyList.IList) MY_MOCKS.verify(firstMock, MY_MOCKS.calls(1))).add('1-1'); + System.assert(false, 'an exception was expected because the method is only implemented for the InOrder class'); + } + catch (fflib_ApexMocks.ApexMocksException mockExcept) + { + String expectedMessage = 'The calls() method is available only in the InOrder Verification.'; + System.assertEquals(expectedMessage, mockExcept.getMessage(), ' the error message is not as expected'); + } + } + + @isTest + static void thatVerifyNoMoreInteractionsFails() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + // When + firstMock.add('1-1', '1-1', '1-1', '1-1'); + firstMock.addMore('2-1'); + firstMock.add('1-1'); + + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('1-1', '1-1', '1-1', '1-1'); + + // Then + try + { + inOrder1.verifyNoMoreInteractions(); + System.assert(false, 'an exception was expected because there are other interactions'); + } + catch (fflib_ApexMocks.ApexMocksException mockExcept) + { + String expectedMessage = 'No more Interactions were expected after the ' + fflib_MyList.getStubClassName() + '.add(String, String, String, String) method.'; + System.assertEquals(expectedMessage, mockExcept.getMessage(), ' the error message is not as expected'); + } + } + + @isTest + static void thatVerifyNoMoreInteractionsFailsWhenOnLyOneMethodLeft() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + // When + firstMock.add('1-1', '1-1', '1-1', '1-1'); + firstMock.addMore('2-1'); + firstMock.add('1-1'); + + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).addMore('2-1'); + + // Then + try + { + inOrder1.verifyNoMoreInteractions(); + System.assert(false, 'an exception was expected because there are other interactions'); + } + catch (fflib_ApexMocks.ApexMocksException mockExcept) + { + String expectedMessage = 'No more Interactions were expected after the ' + fflib_MyList.getStubClassName() + '.addMore(String) method.'; + System.assertEquals(expectedMessage, mockExcept.getMessage(), ' the error message is not as expected'); + } + } + + @isTest + static void thatVerifyNoMoreInteractionsPass() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_MyList secondMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + fflib_InOrder inOrder2 = new fflib_InOrder(MY_MOCKS, new List{ secondMock }); + + // When + firstMock.add('1-1'); + firstMock.add('2-1'); + firstMock.add('1-1'); + secondMock.add('1-1'); + secondMock.add('1-1'); + + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('1-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('2-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('1-1'); + + inOrder1.verifyNoMoreInteractions(); + + ((fflib_MyList.IList)inOrder2.verify(secondMock, MY_MOCKS.calls(2))).add('1-1'); + + inOrder2.verifyNoMoreInteractions(); + } + + @isTest + static void thatVerifyNoMoreInteractionsFailsWhenNoInteracionOccurs() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + // When + firstMock.add('1-1'); + firstMock.add('2-1'); + firstMock.add('1-1'); + + // Then + try + { + inOrder1.verifyNoMoreInteractions(); + System.assert(false, 'an exception was expected because there are other interactions'); + } + catch (fflib_ApexMocks.ApexMocksException mockExcept) + { + String expectedMessage = 'No Interactions expected on this InOrder Mock instance!'; + System.assertEquals(expectedMessage, mockExcept.getMessage(), ' the error message is not as expected'); + } + } + + @isTest + static void thatVerifyNoInteractionsFails() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + // When + firstMock.add('1-1'); + firstMock.add('2-1'); + firstMock.add('1-1'); + + // Then + try + { + inOrder1.verifyNoInteractions(); + System.assert(false, 'an exception was expected because there are other interactions'); + } + catch (fflib_ApexMocks.ApexMocksException mockExcept) + { + String expectedMessage = 'No Interactions expected on this InOrder Mock instance!'; + System.assertEquals(expectedMessage, mockExcept.getMessage(), ' the error message is not as expected'); + } + } + + @isTest + static void thatVerifyNoInteractionsPass() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_MyList secondMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + // When + secondMock.add('1-2'); + //Then + inOrder1.verifyNoInteractions(); + } + + @isTest + static void thatStrictVerificationCanBePerformed() + { + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + // When + firstMock.add('1-1'); + firstMock.add('2-1'); + firstMock.add('1-1'); + firstMock.add('4-1'); + // Then + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('1-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('2-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('1-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('4-1'); + } + + @isTest + static void thatMixedVerificationDoNotInterfierWithOtherImplementationChecking() + { + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + // When + firstMock.add('1-1'); + firstMock.add('2-1'); + firstMock.add('1-1'); + firstMock.add('4-1'); + // Then + ((fflib_MyList.IList)MY_MOCKS.verify(firstMock, MY_MOCKS.times(2))).add('1-1'); + + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('1-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('2-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('1-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('4-1'); + + ((fflib_MyList.IList)MY_MOCKS.verify(firstMock, MY_MOCKS.times(2))).add('1-1'); + } + + @isTest + static void thatVerifyAtLeastPassWithSameCallsOfAssertion() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + // When + firstMock.add('1-1'); //consumed by -> verify(firstMock, MY_MOCKS.calls(1))).add('1-1'); + firstMock.add('2-1'); //consumed by -> verify(firstMock, MY_MOCKS.calls(1))).add('2-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); //all consumed until there by -> verify(firstMock, MY_MOCKS.atLeast(3))).add('1-1'); + firstMock.add('2-1'); //finally consumed by -> verify(firstMock, MY_MOCKS.calls(1))).add('2-1'); + + // Then + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('1-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('2-1'); + + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.atLeast(3))).add('1-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('2-1'); + } + + @isTest + static void thatVerifyAtLeastPassWithMoreCallsThenAsserted() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + // When + firstMock.add('1-1'); //consumed by -> verify(firstMock, MY_MOCKS.calls(1))).add('1-1'); + firstMock.add('2-1'); //consumed by -> verify(firstMock, MY_MOCKS.calls(1))).add('2-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); //it verifies until here, but + firstMock.add('1-1'); //this is consumed as well + firstMock.add('2-1'); //finally consumed by -> verify(firstMock, MY_MOCKS.calls(1))).add('2-1'); + + // Then + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('1-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('2-1'); + + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.atLeast(2))).add('1-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('2-1'); + } + + @isTest + static void thatVerifyAtLeastThrowsErrorIfCalledLessTimes() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + // When + firstMock.add('1-1'); //consumed by -> verify(firstMock, MY_MOCKS.calls(1))).add('1-1'); + firstMock.add('2-1'); //consumed by -> verify(firstMock, MY_MOCKS.calls(1))).add('2-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); //there are then only 3 calls available, the assert 4 would fail + firstMock.add('2-1'); + + // Then + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('1-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('2-1'); + + try + { + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.atLeast(4))).add('1-1'); + System.assert(false, 'an exception was expected because the atLeast is asserting for 4 calls when instead there are only 3 not consumed calls'); + } + catch (fflib_ApexMocks.ApexMocksException mockExcept) + { + System.assertEquals('EXPECTED COUNT: 4 in order' + +'\nACTUAL COUNT: 3' + +'\nMETHOD: fflib_MyList__sfdc_ApexStub.add(String)' + +'\n---' + +'\nACTUAL ARGS: ("1-1"), ("2-1"), ("1-1"), ("1-1"), ("1-1"), ("2-1")' + +'\n---' + +'\nEXPECTED ARGS: "1-1"', + mockExcept.getMessage(), + 'Unexpected verify fail message'); + } + } + + @isTest + static void thatVerifyAtLeastConsumeAllTheInstances() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + // When + firstMock.add('1-1'); //consumed by -> verify(firstMock, MY_MOCKS.calls(1))).add('1-1'); + firstMock.add('2-1'); //consumed by -> verify(firstMock, MY_MOCKS.calls(1))).add('2-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); // the verify atLeast(2) it verifies until here, but it keep going through the instances + firstMock.add('1-1'); + firstMock.add('2-1'); //so this would fail because have to first consume all the instances of the ('1-1') + firstMock.add('1-1'); + firstMock.add('1-1'); + + + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('1-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('2-1'); + + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.atLeast(2))).add('1-1'); + + // Then + try + { + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('2-1'); + System.assert(false, 'an exception was expected because the atLeast have consumed all the interactions of 1-1'); + } + catch (fflib_ApexMocks.ApexMocksException mockExcept) + { + System.assertEquals('EXPECTED COUNT: 1 in order' + +'\nACTUAL COUNT: 0' + +'\nMETHOD: fflib_MyList__sfdc_ApexStub.add(String)' + +'\n---' + +'\nACTUAL ARGS: ("1-1"), ("2-1"), ("1-1"), ("1-1"), ("1-1"), ("2-1"), ("1-1"), ("1-1")' + +'\n---' + +'\nEXPECTED ARGS: "2-1"', + mockExcept.getMessage(), + 'Unexpected verify fail message'); + } + } + + @isTest + static void thatVerifyAtLeastConsumeAllTheInstancesForOnlyTheMethodVerified() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + // When + firstMock.add('1-1'); + firstMock.add('2-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('2-1'); // verify(firstMock, MY_MOCKS.atLeast(2))).add('2-1'); consume until here + firstMock.add('1-1'); + firstMock.add('1-1'); //those are then free for the second atLeast assertion + + // Then + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.atLeast(2))).add('2-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.atLeast(2))).add('1-1'); + } + + @isTest + static void thatVerifyAtLeastOnce() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + // When + firstMock.add('1-1'); + firstMock.add('2-1'); + firstMock.add('1-1'); + + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.atLeastOnce())).add('1-1'); + } + + @isTest + static void thatVerifyAtLeastOnceConsumesInstancesUntilLastMethodVerified() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + // When + firstMock.add('1-1'); + firstMock.add('2-1'); + firstMock.add('1-1'); // consumed until there by -> verify(firstMock, MY_MOCKS.atLeastOnce())).add('1-1'); + firstMock.add('2-1'); // free for another assertion + + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.atLeastOnce())).add('1-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.atLeastOnce())).add('2-1'); + } + + @isTest + static void thatVerifyAtLeastOnceThrowsErrorIfCalledLessTimes() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + // When + firstMock.add('1-1'); + firstMock.add('2-1'); + firstMock.add('1-1'); + + // Then + try + { + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.atLeastOnce())).add('1-3'); + System.assert(false, 'an exception was expected because the atLeastOnce is asserting for 1 calls when instead the method is not called at all with that argument'); + } + catch (fflib_ApexMocks.ApexMocksException mockExcept) + { + System.assertEquals('EXPECTED COUNT: 1 in order' + +'\nACTUAL COUNT: 0' + +'\nMETHOD: fflib_MyList__sfdc_ApexStub.add(String)' + +'\n---' + +'\nACTUAL ARGS: ("1-1"), ("2-1"), ("1-1")' + +'\n---' + +'\nEXPECTED ARGS: "1-3"', + mockExcept.getMessage(), + 'Unexpected verify fail message'); + } + } + + @isTest + static void thatVerifyAtLeastOnceConsumesAllTheInstances() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + // When + firstMock.add('1-1'); + firstMock.add('2-1'); + firstMock.add('1-1'); + firstMock.add('2-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); //all the instance have been consumed + + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.atLeastOnce())).add('1-1'); + + // Then + try + { + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('2-1'); + System.assert(false, 'an exception was expected because the atLeast have consumed all the interactions of 1-1'); + } + catch (fflib_ApexMocks.ApexMocksException mockExcept) + { + System.assertEquals('EXPECTED COUNT: 1 in order' + +'\nACTUAL COUNT: 0' + +'\nMETHOD: fflib_MyList__sfdc_ApexStub.add(String)' + +'\n---' + +'\nACTUAL ARGS: ("1-1"), ("2-1"), ("1-1"), ("2-1"), ("1-1"), ("1-1")' + +'\n---' + +'\nEXPECTED ARGS: "2-1"', + mockExcept.getMessage(), + 'Unexpected verify fail message'); + } + } + + @isTest + static void thatVerifyTimes() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + // When + firstMock.add('1-1'); + firstMock.add('2-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('2-1'); + + // Then + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('1-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('2-1'); + + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.times(3))).add('1-1'); + } + + @isTest + static void thatVerifyTimesThrowsExceptionIfCalledMoreTimesThanExpected() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + // When + firstMock.add('1-1'); + firstMock.add('2-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('2-1'); + + // Then + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('1-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('2-1'); + + try + { + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.times(2))).add('1-1'); + System.assert(false, 'exception expected because the method is called more times than expected in the verification'); + } + catch (fflib_ApexMocks.ApexMocksException mockExcept) + { + System.assertEquals('EXPECTED COUNT: 2 in order' + + '\nACTUAL COUNT: 3' + + '\nMETHOD: fflib_MyList__sfdc_ApexStub.add(String)' + + '\n---' + + '\nACTUAL ARGS: ("1-1"), ("2-1"), ("1-1"), ("1-1"), ("1-1"), ("2-1")' + + '\n---' + + '\nEXPECTED ARGS: "1-1"', + mockExcept.getMessage(), + 'Unexpected verify fail message'); + } + } + + @isTest + static void thatVerifyTimesThrowsExceptionIfCalledLessTimesThanExpected() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + // When + firstMock.add('1-1'); + firstMock.add('2-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('2-1'); + + // Then + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('1-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('2-1'); + + try + { + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.times(4))).add('1-1'); + System.assert(false, 'exception expected because the method is called more times than expected in the verification'); + } + catch (fflib_ApexMocks.ApexMocksException mockExcept) + { + System.assertEquals('EXPECTED COUNT: 4 in order' + + '\nACTUAL COUNT: 3' + + '\nMETHOD: fflib_MyList__sfdc_ApexStub.add(String)' + + '\n---' + + '\nACTUAL ARGS: ("1-1"), ("2-1"), ("1-1"), ("1-1"), ("1-1"), ("2-1")' + + '\n---' + + '\nEXPECTED ARGS: "1-1"', + mockExcept.getMessage(), + 'Unexpected verify fail message'); + } + } + + @isTest + static void thatVerifyTimesPassWhenAnotherMethodIsCalledBetweenMethodsCalls() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + // When + firstMock.add('1-1'); + firstMock.add('2-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('2-1'); + firstMock.add('1-1'); + firstMock.add('2-1'); + + // Then + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('1-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('2-1'); + + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.times(4))).add('1-1'); + + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.times(1))).add('2-1'); + } + + @isTest + static void thatVerifyTimesPassWhenAnotherMethodIsCalledBetweenMethodsCalls2() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + // When + firstMock.add('1-1'); + firstMock.add('2-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('2-1'); + firstMock.add('1-1'); + firstMock.add('2-1'); + + // Then + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('1-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('2-1'); + + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.times(3))).add('1-1'); + + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.times(1))).add('2-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.times(1))).add('1-1'); + } + + @isTest + static void thatVerifyTimesPassWhenAnotherMethodIsCalledBetweenMethodsCalls3() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + // When + firstMock.add('1-1'); + firstMock.add('2-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('2-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('2-1'); + firstMock.add('1-1'); + + // Then + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('1-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('2-1'); + + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.times(3))).add('1-1'); + + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.times(1))).add('2-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.times(2))).add('1-1'); + + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.times(1))).add('2-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.times(1))).add('1-1'); + } + + @isTest + static void thatVerifyTimesPassWhenAnotherMethodIsCalledBetweenMethodsCalls4() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + // When + firstMock.add('1-1'); + firstMock.add('2-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('2-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('2-1'); + firstMock.add('1-1'); + + // Then + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('1-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('2-1'); + + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.times(5))).add('1-1'); + + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.times(1))).add('2-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.times(1))).add('1-1'); + } + + @isTest + static void thatVerifyTimesThrowsExceptionWhenAnotherMethodIsCalledBetweenMethodsCalls() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + // When + firstMock.add('1-1'); + firstMock.add('2-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('2-1'); + firstMock.add('1-1'); + firstMock.add('2-1'); + + // Then + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('1-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('2-1'); + + try + { + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.times(5))).add('1-1'); + System.assert(false, 'exception expected because the method is called more times than expected in the verification'); + } + catch (fflib_ApexMocks.ApexMocksException mockExcept) + { + System.assertEquals('EXPECTED COUNT: 5 in order' + + '\nACTUAL COUNT: 4' + + '\nMETHOD: fflib_MyList__sfdc_ApexStub.add(String)' + + '\n---' + + '\nACTUAL ARGS: ("1-1"), ("2-1"), ("1-1"), ("1-1"), ("1-1"), ("2-1"), ("1-1"), ("2-1")' + + '\n---' + + '\nEXPECTED ARGS: "1-1"', + mockExcept.getMessage(), + 'Unexpected verify fail message'); + } + } + + @isTest + static void thatStrictVerificationCanBeEnforced() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + // When + firstMock.add('1-1'); + firstMock.add('2-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('2-1'); + firstMock.add('1-1'); + firstMock.add('2-1'); + + // Then + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.times(1))).add('1-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.times(1))).add('2-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.times(3))).add('1-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.times(1))).add('2-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.times(1))).add('1-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.times(1))).add('2-1'); + } + + @isTest + static void thatTimesOneIsTheDefaultVerification() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + // When + firstMock.add('1-1'); + firstMock.add('2-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('2-1'); + firstMock.add('1-1'); + firstMock.add('2-1'); + + // Then + ((fflib_MyList.IList)inOrder1.verify(firstMock)).add('1-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock)).add('2-1'); + + try + { + ((fflib_MyList.IList)inOrder1.verify(firstMock)).add('1-1'); + System.assert(false, 'exception expected because the method is called more times than expected in the verification'); + } + catch (fflib_ApexMocks.ApexMocksException mockExcept) + { + System.assertEquals('EXPECTED COUNT: 1 in order' + +'\nACTUAL COUNT: 4' + +'\nMETHOD: fflib_MyList__sfdc_ApexStub.add(String)' + +'\n---' + +'\nACTUAL ARGS: ("1-1"), ("2-1"), ("1-1"), ("1-1"), ("1-1"), ("2-1"), ("1-1"), ("2-1")' + +'\n---' + +'\nEXPECTED ARGS: "1-1"', + mockExcept.getMessage(), + 'Unexpected verify fail message'); + } + } + + @isTest + static void thatWithOldNotation() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + // When + firstMock.add('1-1'); + firstMock.add('2-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('2-1'); + + // Then + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('1-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('2-1'); + + ((fflib_MyList.IList)inOrder1.verify(firstMock, 3)).add('1-1'); + } + + @isTest + static void thatWithOldNotationThrowsExceptionIfCalledMoreTimesThanExpected() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + // When + firstMock.add('1-1'); + firstMock.add('2-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('2-1'); + + // Then + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('1-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('2-1'); + + try + { + ((fflib_MyList.IList)inOrder1.verify(firstMock, 2)).add('1-1'); + System.assert(false, 'exception expected because the method is called more times than expected in the verification'); + } + catch (fflib_ApexMocks.ApexMocksException mockExcept) + { + System.assertEquals('EXPECTED COUNT: 2 in order' + + '\nACTUAL COUNT: 3' + + '\nMETHOD: fflib_MyList__sfdc_ApexStub.add(String)' + + '\n---' + + '\nACTUAL ARGS: ("1-1"), ("2-1"), ("1-1"), ("1-1"), ("1-1"), ("2-1")' + + '\n---' + + '\nEXPECTED ARGS: "1-1"', + mockExcept.getMessage(), + 'Unexpected verify fail message'); + } + } + + @isTest + static void thatWithOldNotationThrowsExceptionIfCalledLessTimesThanExpected() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + // When + firstMock.add('1-1'); + firstMock.add('2-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('2-1'); + + // Then + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('1-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('2-1'); + + try + { + ((fflib_MyList.IList)inOrder1.verify(firstMock, 4)).add('1-1'); + System.assert(false, 'exception expected because the method is called more times than expected in the verification'); + } + catch (fflib_ApexMocks.ApexMocksException mockExcept) + { + System.assertEquals('EXPECTED COUNT: 4 in order' + + '\nACTUAL COUNT: 3' + + '\nMETHOD: fflib_MyList__sfdc_ApexStub.add(String)' + + '\n---' + + '\nACTUAL ARGS: ("1-1"), ("2-1"), ("1-1"), ("1-1"), ("1-1"), ("2-1")' + + '\n---' + + '\nEXPECTED ARGS: "1-1"', + mockExcept.getMessage(), + 'Unexpected verify fail message'); + } + } + + @isTest + static void thatWithOldNotationPassWhenAnotherMethodIsCalledBetweenMethodsCalls() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + // When + firstMock.add('1-1'); + firstMock.add('2-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('2-1'); + firstMock.add('1-1'); + firstMock.add('2-1'); + + // Then + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('1-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('2-1'); + + ((fflib_MyList.IList)inOrder1.verify(firstMock, 4)).add('1-1'); + } + + @isTest + static void thatWithOldNotationThrowsExceptionWhenAnotherMethodIsCalledBetweenMethodsCalls() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + // When + firstMock.add('1-1'); + firstMock.add('2-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('2-1'); + firstMock.add('1-1'); + firstMock.add('2-1'); + + // Then + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('1-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('2-1'); + + try + { + ((fflib_MyList.IList)inOrder1.verify(firstMock, 5)).add('1-1'); + System.assert(false, 'exception expected because the method is called more times than expected in the verification'); + } + catch (fflib_ApexMocks.ApexMocksException mockExcept) + { + System.assertEquals('EXPECTED COUNT: 5 in order' + + '\nACTUAL COUNT: 4' + + '\nMETHOD: fflib_MyList__sfdc_ApexStub.add(String)' + + '\n---' + + '\nACTUAL ARGS: ("1-1"), ("2-1"), ("1-1"), ("1-1"), ("1-1"), ("2-1"), ("1-1"), ("2-1")' + + '\n---' + + '\nEXPECTED ARGS: "1-1"', + mockExcept.getMessage(), + 'Unexpected verify fail message'); + } + } + + @isTest + static void thatStrictVerificationCanBeEnforcedWithOldNotation() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + // When + firstMock.add('1-1'); + firstMock.add('2-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('2-1'); + firstMock.add('1-1'); + firstMock.add('2-1'); + + // Then + ((fflib_MyList.IList)inOrder1.verify(firstMock, 1)).add('1-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, 1)).add('2-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, 3)).add('1-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, 1)).add('2-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, 1)).add('1-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, 1)).add('2-1'); + } + + @isTest + static void thatStrictVerificationCanBeEnforcedWithOldNotationUsingDefaultTimesOne() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + // When + firstMock.add('1-1'); + firstMock.add('2-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('2-1'); + firstMock.add('1-1'); + firstMock.add('2-1'); + + // Then + ((fflib_MyList.IList)inOrder1.verify(firstMock)).add('1-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock)).add('2-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock, 3)).add('1-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock)).add('2-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock)).add('1-1'); + ((fflib_MyList.IList)inOrder1.verify(firstMock)).add('2-1'); + } + + @isTest + static void thatVerifyAtLeastConsumesAllTheInstances2() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + //When + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('1-1'); + firstMock.add('2-1'); + firstMock.add('2-1'); + firstMock.add('2-1'); + firstMock.add('2-1'); + firstMock.add('2-1'); + firstMock.add('1-1'); + + // Then + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.atLeast(2))).add('1-1'); + + try + { + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(1))).add('2-1'); + System.assert(false, 'exception expected because the atLeast have consumed all the calls'); + } + catch (fflib_ApexMocks.ApexMocksException mockExcept) + { + System.assertEquals('EXPECTED COUNT: 1 in order' + +'\nACTUAL COUNT: 0' + +'\nMETHOD: fflib_MyList__sfdc_ApexStub.add(String)' + +'\n---' + +'\nACTUAL ARGS: ("1-1"), ("1-1"), ("1-1"), ("2-1"), ("2-1"), ("2-1"), ("2-1"), ("2-1"), ("1-1")' + +'\n---' + +'\nEXPECTED ARGS: "2-1"', + mockExcept.getMessage(), + 'Unexpected verify fail message'); + } + } + + @isTest + static void verifyAtLeastAndCapture() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + //When + firstMock.get2(1, '1-1'); + firstMock.get2(2, '2-1'); + firstMock.get2(1, '3-1'); + firstMock.get2(1, '4-1'); + firstMock.get2(2, '5-1'); + + fflib_ArgumentCaptor argument = fflib_ArgumentCaptor.forClass(String.class); + + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.atLeast(2))).get2( fflib_Match.eqInteger(1), (String) argument.capture()); + + System.assertEquals('4-1', (string) argument.getValue(), 'the last value captured is not as expected'); + } + + @isTest + static void verifyTimesAndCaptor() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + //When + firstMock.get2(1, '1-1'); + firstMock.get2(2, '1-2'); + firstMock.get2(1, '2-1'); + firstMock.get2(2, '2-2'); + firstMock.get2(1, '3-1'); + firstMock.get2(2, '3-2'); + firstMock.get2(1, '4-1'); + firstMock.get2(2, '4-2'); + firstMock.get2(1, '5-1'); + firstMock.get2(2, '5-2'); + + fflib_ArgumentCaptor argument = fflib_ArgumentCaptor.forClass(String.class); + + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.times(5))).get2(fflib_Match.eqInteger(1), (String) argument.capture()); + + System.assertEquals('5-1', (string) argument.getValue(), 'the last value captured is not as expected'); + } + + @isTest + static void verifyCallsAndCapture() + { + // Given + fflib_MyList firstMock = (fflib_MyList)MY_MOCKS.mock(fflib_MyList.class); + fflib_InOrder inOrder1 = new fflib_InOrder(MY_MOCKS, new List{ firstMock }); + + //When + firstMock.get2(1, '1-1'); + firstMock.get2(2, '1-2'); + firstMock.get2(1, '2-1'); + firstMock.get2(2, '2-2'); + firstMock.get2(1, '3-1'); + firstMock.get2(2, '3-2'); + firstMock.get2(1, '4-1'); + firstMock.get2(2, '4-2'); + firstMock.get2(1, '5-1'); + firstMock.get2(2, '5-2'); + + fflib_ArgumentCaptor argument = fflib_ArgumentCaptor.forClass(String.class); + + ((fflib_MyList.IList)inOrder1.verify(firstMock, MY_MOCKS.calls(2))).get2(fflib_Match.eqInteger(1), (String) argument.capture()); + + System.assertEquals('2-1', (string) argument.getValue(), 'the last value captured is not as expected'); + } +} \ No newline at end of file diff --git a/sfdx-source/apex-mocks/tests/fflib_InOrderTest.cls-meta.xml b/sfdx-source/apex-mocks/tests/fflib_InOrderTest.cls-meta.xml new file mode 100644 index 00000000000..dd61d1f917e --- /dev/null +++ b/sfdx-source/apex-mocks/tests/fflib_InOrderTest.cls-meta.xml @@ -0,0 +1,5 @@ + + + 52.0 + Active + diff --git a/sfdx-source/apex-mocks/tests/fflib_InheritorTest.cls b/sfdx-source/apex-mocks/tests/fflib_InheritorTest.cls new file mode 100644 index 00000000000..fa2d384c0a4 --- /dev/null +++ b/sfdx-source/apex-mocks/tests/fflib_InheritorTest.cls @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2016-2017 FinancialForce.com, inc. All rights reserved. + */ +@isTest +public class fflib_InheritorTest +{ + @isTest + public static void canInstantiateMultipleInterfaceInheritor() + { + fflib_ApexMocks mocks = new fflib_ApexMocks(); + Object inheritor = mocks.mock(fflib_Inheritor.class); + System.assert(inheritor instanceof fflib_Inheritor.IA); + System.assert(inheritor instanceof fflib_Inheritor.IB); + System.assert(inheritor instanceof fflib_Inheritor.IC); + } + + @isTest + public static void canStubMultipleInterfaceInheritor() + { + fflib_ApexMocks mocks = new fflib_ApexMocks(); + fflib_Inheritor inheritor = (fflib_Inheritor)mocks.mock(fflib_Inheritor.class); + + mocks.startStubbing(); + mocks.when(inheritor.doA()).thenReturn('Did not do A'); + mocks.when(inheritor.doB()).thenReturn('Did not do B'); + mocks.when(inheritor.doC()).thenReturn('Did not do C'); + mocks.stopStubbing(); + + System.assertEquals('Did not do A', inheritor.doA()); + System.assertEquals('Did not do B', inheritor.doB()); + System.assertEquals('Did not do C', inheritor.doC()); + } +} \ No newline at end of file diff --git a/sfdx-source/apex-mocks/tests/fflib_InheritorTest.cls-meta.xml b/sfdx-source/apex-mocks/tests/fflib_InheritorTest.cls-meta.xml new file mode 100644 index 00000000000..f539c3a316c --- /dev/null +++ b/sfdx-source/apex-mocks/tests/fflib_InheritorTest.cls-meta.xml @@ -0,0 +1,4 @@ + + + 52.0 + diff --git a/sfdx-source/apex-mocks/tests/fflib_MatchTest.cls b/sfdx-source/apex-mocks/tests/fflib_MatchTest.cls new file mode 100644 index 00000000000..ba8dd22778a --- /dev/null +++ b/sfdx-source/apex-mocks/tests/fflib_MatchTest.cls @@ -0,0 +1,1387 @@ +/** + * Copyright (c) 2014-2016, FinancialForce.com, inc + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the FinancialForce.com, inc nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +@isTest +public with sharing class fflib_MatchTest +{ + @isTest + private static void whenMatchesAllArgsWithOneMatchReturnsTrue() + { + //Given + fflib_MethodArgValues argValues = new fflib_MethodArgValues(new List{ null }); + List targetMatchers = new List{ new AlwaysMatch() }; + + //When + Boolean match = fflib_Match.matchesAllArgs(argValues, targetMatchers); + + //Then + System.assert(match); + } + + @isTest + private static void whenMatchesAllArgsWithManyMatchesReturnsTrue() + { + //Given + fflib_MethodArgValues argValues = new fflib_MethodArgValues(new List{ + null, + null, + null, + null + }); + + List targetMatchers = new List{ + new AlwaysMatch(), + new AlwaysMatch(), + new AlwaysMatch(), + new AlwaysMatch() + }; + + //When + Boolean match = fflib_Match.matchesAllArgs(argValues, targetMatchers); + + //Then + System.assert(match); + } + + @isTest + private static void whenMatchesAllArgsWithOneMismatchReturnsFalse() + { + //Given + fflib_MethodArgValues argValues = new fflib_MethodArgValues(new List{ null }); + List targetMatchers = new List{ new NeverMatch() }; + + //When + Boolean match = fflib_Match.matchesAllArgs(argValues, targetMatchers); + + //Then + System.assert(!match); + } + + @isTest + private static void whenMatchesAllArgsWithManyMismatchesReturnsFalse() + { + //Given + fflib_MethodArgValues argValues = new fflib_MethodArgValues(new List{ + null, + null, + null, + null + }); + + List targetMatchers = new List{ + new NeverMatch(), + new NeverMatch(), + new NeverMatch(), + new NeverMatch() + }; + + //When + Boolean match = fflib_Match.matchesAllArgs(argValues, targetMatchers); + + //Then + System.assert(!match); + } + + @isTest + private static void whenMatchesAllArgsWithMatchesAndMismatchesReturnsFalse() + { + //Given + fflib_MethodArgValues argValues = new fflib_MethodArgValues(new List{ + null, + null, + null, + null + }); + + List targetMatchers = new List{ + new AlwaysMatch(), + new AlwaysMatch(), + new NeverMatch(), + new AlwaysMatch() + }; + + //When + Boolean match = fflib_Match.matchesAllArgs(argValues, targetMatchers); + + //Then + System.assert(!match); + } + + @isTest + private static void whenMatchesAllArgsWithNullMethodArgsThrowsException() + { + //Given + fflib_MethodArgValues methodArg = null; + List targetMatchers = new List{ new AlwaysMatch() }; + + //When + try + { + fflib_Match.matchesAllArgs(methodArg, targetMatchers); + System.assert(false, 'Expected exception'); + } + catch (fflib_ApexMocks.ApexMocksException e) + { + //Then + System.assertEquals('MethodArgs cannot be null', e.getMessage()); + } + } + + @isTest + private static void whenMatchesAllArgsWithNullMethodArgsArgValuesThrowsException() + { + //Given + fflib_MethodArgValues methodArg = new fflib_MethodArgValues(null); + List targetMatchers = new List{ new AlwaysMatch() }; + + //When + try + { + fflib_Match.matchesAllArgs(methodArg, targetMatchers); + System.assert(false, 'Expected exception'); + } + catch (fflib_ApexMocks.ApexMocksException e) + { + //Then + System.assertEquals('MethodArgs.argValues cannot be null', e.getMessage()); + } + } + + @isTest + private static void whenMatchesAllArgsWithNullMatchersThrowsException() + { + //Given + fflib_MethodArgValues methodArg = new fflib_MethodArgValues(new List{ 'Test' }); + List targetMatchers = null; + + //When + try + { + fflib_Match.matchesAllArgs(methodArg, targetMatchers); + System.assert(false, 'Expected exception'); + } + catch (fflib_ApexMocks.ApexMocksException e) + { + //Then + System.assertEquals('Matchers cannot be null', e.getMessage()); + } + } + + @isTest + private static void whenMatchesAllArgsWithDifferentSizeArgValuesAndMatchersThrowsException() + { + //Given + fflib_MethodArgValues methodArg = new fflib_MethodArgValues(new List{ 'Test' }); + List targetMatchers = new List{ new AlwaysMatch(), new AlwaysMatch() }; + + //When + try + { + fflib_Match.matchesAllArgs(methodArg, targetMatchers); + System.assert(false, 'Expected exception'); + } + catch (fflib_ApexMocks.ApexMocksException e) + { + //Then + String expectedMessage = 'MethodArgs and matchers must have the same count' + + ', MethodArgs: (' + methodArg.argValues.size() + ') ' + methodArg.argValues + + ', Matchers: (' + targetMatchers.size() + ') ' + targetMatchers; + + System.assertEquals(expectedMessage, e.getMessage()); + } + } + + @isTest + private static void whenMatchesWithOneMatcherSetsMatchingToTrue() + { + //Given + fflib_IMatcher matcher = new AlwaysMatch(); + + //When + fflib_Match.matches(matcher); + + //Then + System.assert(fflib_Match.Matching); + } + + @isTest + private static void whenMatchesWithOneMatcherRegistersMatcher() + { + //Given + fflib_IMatcher matcher = new AlwaysMatch(); + + //When + fflib_Match.matches(matcher); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(matcher === registeredMatchers[0]); + } + + @isTest + private static void whenMatchesWithOneMatcherReturnsNull() + { + //Given + fflib_IMatcher matcher = new AlwaysMatch(); + + //When + Object retval = fflib_Match.matches(matcher); + + //Then + System.assertEquals(null, retval); + } + + @isTest + private static void allOfWithNoArgsThrowsException() + { + //Given/When + try + { + Object x = fflib_Match.allOf((List)null); + System.assert(false, 'Expected exception'); + } + catch (fflib_ApexMocks.ApexMocksException e) + { + //Then + System.assertEquals('Must register matchers to combine', e.getMessage()); + } + } + + @isTest + private static void allOfWithEmptyArgsThrowsException() + { + //Given/When + try + { + Object x = fflib_Match.allOf(new List{}); + System.assert(false, 'Expected exception'); + } + catch (fflib_ApexMocks.ApexMocksException e) + { + //Then + System.assertEquals('Must register matchers to combine', e.getMessage()); + } + } + + @isTest + private static void allOfWithoutRegisteringInnerMatchersThrowsException() + { + //Given/When + try + { + //Should be using fflib_Match.allOf(new List{ fflib_Match.myMatcher('Hello') }) + //to register the inner matcher. + Object x = fflib_Match.allOf(new List{ 'Hello' }); + System.assert(false, 'Expected exception'); + } + catch (fflib_ApexMocks.ApexMocksException e) + { + //Then + String expectedMessage = 'Error reclaiming inner matchers for combined matcher. ' + + 'Wanted 1 matchers but only got ' + new List(); + System.assertEquals(expectedMessage, e.getMessage()); + } + } + + @isTest + private static void allOfWith2ArgsRegistersCorrectMatcherType() + { + //Given/When + Object x = fflib_Match.allOf(fflib_Match.eq('hello1'), fflib_Match.eq('hello2')); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.Combined); + } + + @isTest + private static void allOfWith3ArgsRegistersCorrectMatcherType() + { + //Given/When + Object x = fflib_Match.allOf(fflib_Match.eq('hello1'), fflib_Match.eq('hello2'), fflib_Match.eq('hello3')); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.Combined); + } + + @isTest + private static void allOfWith4ArgsRegistersCorrectMatcherType() + { + //Given/When + Object x = fflib_Match.allOf(fflib_Match.eq('hello1'), fflib_Match.eq('hello2'), fflib_Match.eq('hello3'), fflib_Match.eq('hello4')); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.Combined); + } + + @isTest + private static void allOfWithListArgsRegistersCorrectMatcherType() + { + //Given/When + Object x = fflib_Match.allOf(new List{ fflib_Match.eq('hello') }); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.Combined); + } + + @isTest + private static void anyOfWith2ArgsRegistersCorrectMatcherType() + { + //Given/When + Object x = fflib_Match.anyOf(fflib_Match.eq('hello1'), fflib_Match.eq('hello2')); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.Combined); + } + + @isTest + private static void anyOfWith3ArgsRegistersCorrectMatcherType() + { + //Given/When + Object x = fflib_Match.anyOf(fflib_Match.eq('hello1'), fflib_Match.eq('hello2'), fflib_Match.eq('hello3')); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.Combined); + } + + @isTest + private static void anyOfWith4ArgsRegistersCorrectMatcherType() + { + //Given/When + Object x = fflib_Match.anyOf(fflib_Match.eq('hello1'), fflib_Match.eq('hello2'), fflib_Match.eq('hello3'), fflib_Match.eq('hello4')); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.Combined); + } + + @isTest + private static void anyOfWithListArgsRegistersCorrectMatcherType() + { + //Given/When + Object x = fflib_Match.anyOf(new List{ fflib_Match.eq('hello') }); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.Combined); + } + + @isTest + private static void isNotRegistersCorrectMatcherType() + { + //Given/When + Object x = fflib_Match.isNot(fflib_Match.eq('hello1')); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.Combined); + } + + @isTest + private static void noneOfWith2ArgsRegistersCorrectMatcherType() + { + //Given/When + Object x = fflib_Match.noneOf(fflib_Match.eq('hello1'), fflib_Match.eq('hello2')); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.Combined); + } + + @isTest + private static void noneOfWith3ArgsRegistersCorrectMatcherType() + { + //Given/When + Object x = fflib_Match.noneOf(fflib_Match.eq('hello1'), fflib_Match.eq('hello2'), fflib_Match.eq('hello3')); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.Combined); + } + + @isTest + private static void noneOfWith4ArgsRegistersCorrectMatcherType() + { + //Given/When + Object x = fflib_Match.noneOf(fflib_Match.eq('hello1'), fflib_Match.eq('hello2'), fflib_Match.eq('hello3'), fflib_Match.eq('hello4')); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.Combined); + } + + @isTest + private static void noneOfWithListArgsRegistersCorrectMatcherType() + { + //Given/When + Object x = fflib_Match.noneOf(new List{ fflib_Match.eq('hello') }); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.Combined); + } + + @isTest + private static void eqRegistersCorrectMatcherType() + { + //Given/When + Object x = fflib_Match.eq('hello'); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.Eq); + } + + @isTest + public static void eqBooleanRegistersCorrectMatcherType() + { + //Given/When + Object x = fflib_Match.eqBoolean(true); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.Eq); + } + + @isTest + public static void eqDateRegistersCorrectMatcherType() + { + //Given/When + Object x = fflib_Match.eqDate(Date.today()); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.Eq); + } + + @isTest + public static void eqDatetimeRegistersCorrectMatcherType() + { + //Given/When + Object x = fflib_Match.eqDatetime(System.now()); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.Eq); + } + + @isTest + public static void eqDecimalRegistersCorrectMatcherType() + { + //Given/When + Object x = fflib_Match.eqDecimal(123); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.Eq); + } + + @isTest + public static void eqDoubleRegistersCorrectMatcherType() + { + //Given/When + Object x = fflib_Match.eqDouble(123); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.Eq); + } + + @isTest + public static void eqIdRegistersCorrectMatcherType() + { + //Given/When + Object x = fflib_Match.eqId('001000000000001AAA'); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.Eq); + } + + @isTest + public static void eqIntegerRegistersCorrectMatcherType() + { + //Given/When + Object x = fflib_Match.eqInteger(123); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.Eq); + } + + @isTest + public static void eqListRegistersCorrectMatcherType() + { + //Given/When + Object x = fflib_Match.eqList(new List{ 'hello' }); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.Eq); + } + + @isTest + public static void eqLongRegistersCorrectMatcherType() + { + //Given/When + Object x = fflib_Match.eqLong(123); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.Eq); + } + + @isTest + public static void eqSObjectFieldRegistersCorrectMatcherType() + { + //Given/When + Schema.SObjectField f = Schema.getGlobalDescribe().get('Account').getDescribe().fields.getMap().get('Id'); + Object x = fflib_Match.eqSObjectField(f); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.Eq); + } + + @isTest + public static void eqSObjectTypeRegistersCorrectMatcherType() + { + //Given/When + Schema.SObjectType ot = Schema.getGlobalDescribe().get('Account'); + Object x = fflib_Match.eqSObjectType(ot); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.Eq); + } + + @isTest + public static void eqStringRegistersCorrectMatcherType() + { + //Given/When + Object x = fflib_Match.eqString('hello'); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.Eq); + } + + @isTest + private static void refEqRegistersCorrectMatcherType() + { + //Given/When + Object x = fflib_Match.refEq('hello'); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.RefEq); + } + + @isTest + private static void anyBooleanRegistersCorrectMatcherType() + { + //Given/When + Boolean x = fflib_Match.anyBoolean(); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.AnyBoolean); + } + + @isTest + private static void anyDateRegistersCorrectMatcherType() + { + //Given/When + Date x = fflib_Match.anyDate(); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.AnyDate); + } + + @isTest + private static void anyDatetimeRegistersCorrectMatcherType() + { + //Given/When + Datetime x = fflib_Match.anyDatetime(); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.AnyDatetime); + } + + @isTest + private static void anyDecimalRegistersCorrectMatcherType() + { + //Given/When + Decimal x = fflib_Match.anyDecimal(); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.AnyDecimal); + } + + @isTest + private static void anyDoubleRegistersCorrectMatcherType() + { + //Given/When + Double x = fflib_Match.anyDouble(); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.AnyDouble); + } + + @isTest + private static void anyFieldSetRegistersCorrectMatcherType() + { + //Given/When + Schema.FieldSet x = fflib_Match.anyFieldSet(); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.AnyFieldSet); + } + + @isTest + private static void anyIdRegistersCorrectMatcherType() + { + //Given/When + Id x = fflib_Match.anyId(); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.AnyId); + } + + @isTest + private static void anyIntegerRegistersCorrectMatcherType() + { + //Given/When + Integer x = fflib_Match.anyInteger(); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.AnyInteger); + } + + @isTest + private static void anyListRegistersCorrectMatcherType() + { + //Given/When + List x = fflib_Match.anyList(); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.AnyList); + } + + @isTest + private static void anyLongRegistersCorrectMatcherType() + { + //Given/When + Long x = fflib_Match.anyLong(); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.AnyLong); + } + + @isTest + private static void anyObjectRegistersCorrectMatcherType() + { + //Given/When + Object x = fflib_Match.anyObject(); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.AnyObject); + } + + @isTest + private static void anyStringRegistersCorrectMatcherType() + { + //Given/When + String x = fflib_Match.anyString(); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.AnyString); + } + + @isTest + private static void anySObjectRegistersCorrectMatcherType() + { + //Given/When + SObject x = fflib_Match.anySObject(); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.AnySObject); + } + + @isTest + private static void anySObjectFieldRegistersCorrectMatcherType() + { + //Given/When + SObjectField x = fflib_Match.anySObjectField(); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.AnySObjectField); + } + + @isTest + private static void anySObjectTypeRegistersCorrectMatcherType() + { + //Given/When + SObjectType x = fflib_Match.anySObjectType(); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.AnySObjectType); + } + + @isTest + private static void dateAfterRegistersCorrectMatcherType() + { + //Given/When + Date x = fflib_Match.dateAfter(Date.today()); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.DatetimeAfter); + } + + @isTest + private static void dateBeforeRegistersCorrectMatcherType() + { + //Given/When + Date x = fflib_Match.dateBefore(Date.today()); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.DatetimeBefore); + } + + @isTest + private static void dateBetweenRegistersCorrectMatcherType() + { + //Given/When + Date x = fflib_Match.dateBetween(Date.today(), Date.today()); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.DatetimeBetween); + } + + @isTest + private static void datetimeAfterRegistersCorrectMatcherType() + { + //Given/When + Datetime x = fflib_Match.datetimeAfter(System.now()); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.DatetimeAfter); + } + + @isTest + private static void datetimeBeforeRegistersCorrectMatcherType() + { + //Given/When + Datetime x = fflib_Match.datetimeBefore(System.now()); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.DatetimeBefore); + } + + @isTest + private static void datetimeBetweenRegistersCorrectMatcherType() + { + //Given/When + Datetime x = fflib_Match.datetimeBetween(System.now(), System.now()); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.DatetimeBetween); + } + + @isTest + private static void decimalBetweenRegistersCorrectMatcherType() + { + //Given/When + Decimal x = fflib_Match.decimalBetween(0, 10); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.DecimalBetween); + } + + @isTest + private static void decimalLessThanRegistersCorrectMatcherType() + { + //Given/When + Decimal x = fflib_Match.decimalLessThan(0); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.DecimalLessThan); + } + + @isTest + private static void decimalMoreThanRegistersCorrectMatcherType() + { + //Given/When + Decimal x = fflib_Match.decimalMoreThan(0); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.DecimalMoreThan); + } + + @isTest + private static void doubleBetweenRegistersCorrectMatcherType() + { + //Given/When + Double x = fflib_Match.doubleBetween(0, 10); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.DecimalBetween); + } + + @isTest + private static void doubleLessThanRegistersCorrectMatcherType() + { + //Given/When + Double x = fflib_Match.doubleLessThan(0); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.DecimalLessThan); + } + + @isTest + private static void doubleMoreThanRegistersCorrectMatcherType() + { + //Given/When + Double x = fflib_Match.doubleMoreThan(0); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.DecimalMoreThan); + } + + @isTest + private static void fieldSetEquivalentWithNullFieldSetThrowsException() + { + try + { + fflib_Match.fieldSetEquivalentTo(null); + System.assert(false, 'ExpectedException'); + } + catch (fflib_ApexMocks.ApexMocksException e) + { + System.assertEquals('Arg cannot be null: null', e.getMessage()); + } + } + + @isTest + private static void fieldSetEquivalentToRegistersCorrectMatcherType() + { + Schema.FieldSet anyFieldSet = fflib_ApexMocksUtilsTest.findAnyFieldSet(); + if (anyFieldSet == null) + { + return; + } + + //Given/When + Schema.FieldSet x = fflib_Match.fieldSetEquivalentTo(anyFieldSet); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.FieldSetEquivalentTo); + } + + @isTest + private static void integerBetweenRegistersCorrectMatcherType() + { + //Given/When + Integer x = fflib_Match.integerBetween(0, 10); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.DecimalBetween); + } + + @isTest + private static void integerLessThanRegistersCorrectMatcherType() + { + //Given/When + Integer x = fflib_Match.integerLessThan(0); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.DecimalLessThan); + } + + @isTest + private static void integerMoreThanRegistersCorrectMatcherType() + { + //Given/When + Integer x = fflib_Match.integerMoreThan(0); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.DecimalMoreThan); + } + + @isTest + private static void isNotNullRegistersCorrectMatcherType() + { + //Given/When + Object x = fflib_Match.isNotNull(); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.IsNotNull); + } + + @isTest + private static void isNullRegistersCorrectMatcherType() + { + //Given/When + Object x = fflib_Match.isNull(); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.IsNull); + } + + @isTest + private static void listContainsRegistersCorrectMatcherType() + { + //Given/When + Object x = fflib_Match.listContains('fred'); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.ListContains); + } + + @isTest + private static void listIsEmptyRegistersCorrectMatcherType() + { + //Given/When + Object x = fflib_Match.listIsEmpty(); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.ListIsEmpty); + } + + @isTest + private static void longBetweenRegistersCorrectMatcherType() + { + //Given/When + Long x = fflib_Match.longBetween(0, 10); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.DecimalBetween); + } + + @isTest + private static void longLessThanRegistersCorrectMatcherType() + { + //Given/When + Long x = fflib_Match.longLessThan(0); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.DecimalLessThan); + } + + @isTest + private static void longMoreThanRegistersCorrectMatcherType() + { + //Given/When + Long x = fflib_Match.longMoreThan(0); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.DecimalMoreThan); + } + + @isTest + private static void sObjectOfTypeRegistersCorrectMatcherType() + { + //Given + Schema.SObjectType ot = Schema.getGlobalDescribe().get('Account'); + if (ot == null) + { + return; + } + + //When + SObject x = fflib_Match.sObjectOfType(ot); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.SObjectOfType); + } + + @isTest + private static void sObjectWithRegistersCorrectMatcherType() + { + //Given + Schema.SObjectType ot = Schema.getGlobalDescribe().get('Account'); + if (ot == null) + { + return; + } + + Schema.SObjectField f = ot.getDescribe().fields.getMap().get('Id'); + + //When + SObject x = fflib_Match.sObjectWith(new Map{ f=>null }); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.SObjectWith); + } + + @isTest + private static void sObjectsWithRegistersCorrectMatcherType() + { + //Given + Schema.SObjectType ot = Schema.getGlobalDescribe().get('Account'); + if (ot == null) + { + return; + } + + Schema.SObjectField f = ot.getDescribe().fields.getMap().get('Id'); + + //When + SObject[] x = fflib_Match.sObjectsWith(new list>{ + new map {f=>null} + }); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.SObjectsWith); + } + + @isTest + private static void sObjectsWithMatchInOrderRegistersCorrectMatcherType() + { + //Given + Schema.SObjectType ot = Schema.getGlobalDescribe().get('Account'); + if (ot == null) + { + return; + } + + Schema.SObjectField f = ot.getDescribe().fields.getMap().get('Id'); + + //When + SObject[] x = fflib_Match.sObjectsWith(new list>{ + new map {f=>null} + }, false); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.SObjectsWith,registeredMatchers); + } + + @isTest + private static void sObjectWithIdRegistersCorrectMatcherType() + { + //Given/When + SObject x = fflib_Match.sObjectWithId('001000000000001AAA'); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.SObjectWithId); + } + + @isTest + private static void sObjectWithNameRegistersCorrectMatcherType() + { + //Given/When + SObject x = fflib_Match.sObjectWithName('hello'); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.SObjectWithName); + } + + @isTest + private static void stringContainsRegistersCorrectMatcherType() + { + //Given/When + String x = fflib_Match.stringContains('hello'); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.StringContains); + } + + @isTest + private static void stringEndsWithRegistersCorrectMatcherType() + { + //Given/When + String x = fflib_Match.stringEndsWith('hello'); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.StringEndsWith); + } + + @isTest + private static void stringIsBlankRegistersCorrectMatcherType() + { + //Given/When + String x = fflib_Match.stringIsBlank(); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.StringIsBlank); + } + + @isTest + private static void stringIsNotBlankRegistersCorrectMatcherType() + { + //Given/When + String x = fflib_Match.stringIsNotBlank(); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.StringIsNotBlank); + } + + @isTest + private static void stringMatchesRegistersCorrectMatcherType() + { + //Given/When + String x = fflib_Match.stringMatches('[a-z]*'); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.StringMatches); + } + + @isTest + private static void stringStartsWithRegistersCorrectMatcherType() + { + //Given/When + String x = fflib_Match.stringStartsWith('hello'); + + //Then + List registeredMatchers = fflib_Match.getAndClearMatchers(1); + System.assertNotEquals(null, registeredMatchers); + System.assertEquals(1, registeredMatchers.size()); + System.assert(registeredMatchers[0] instanceof fflib_MatcherDefinitions.StringStartsWith); + } + + private class AlwaysMatch implements fflib_IMatcher + { + public Boolean matches(Object arg) + { + return true; + } + } + + private class NeverMatch implements fflib_IMatcher + { + public Boolean matches(Object arg) + { + return false; + } + } +} \ No newline at end of file diff --git a/sfdx-source/apex-mocks/tests/fflib_MatchTest.cls-meta.xml b/sfdx-source/apex-mocks/tests/fflib_MatchTest.cls-meta.xml new file mode 100644 index 00000000000..08c4dbd5cfa --- /dev/null +++ b/sfdx-source/apex-mocks/tests/fflib_MatchTest.cls-meta.xml @@ -0,0 +1,4 @@ + + + 52.0 + diff --git a/sfdx-source/apex-mocks/tests/fflib_MatcherDefinitionsTest.cls b/sfdx-source/apex-mocks/tests/fflib_MatcherDefinitionsTest.cls new file mode 100644 index 00000000000..3465bb56f04 --- /dev/null +++ b/sfdx-source/apex-mocks/tests/fflib_MatcherDefinitionsTest.cls @@ -0,0 +1,1634 @@ +/** + * Copyright (c) 2014-2016, FinancialForce.com, inc + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the FinancialForce.com, inc nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + @isTest +public class fflib_MatcherDefinitionsTest +{ + private static final List INTERNAL_MATCHERS = new List{ + new fflib_MatcherDefinitions.StringContains('bob'), + new fflib_MatcherDefinitions.StringContains('tom'), + new fflib_MatcherDefinitions.StringContains('fred') + }; + + private static final Date TODAY = System.today(); + private static final Datetime NOW = System.now(); + + private static final SObject ACCOUNT_RECORD; + private static final Schema.SObjectType ACCOUNT_OBJECT_TYPE; + private static final Schema.SObjectType OPPORTUNITY_OBJECT_TYPE; + private static final Schema.SobjectType GROUP_OBJECT_TYPE; + private static final Sobject[] GROUP_RECORDS; + + static + { + Map globalDescribe = Schema.getGlobalDescribe(); + + ACCOUNT_OBJECT_TYPE = globalDescribe.get('Account'); + OPPORTUNITY_OBJECT_TYPE = globalDescribe.get('Opportunity'); + GROUP_OBJECT_TYPE = globalDescribe.get('Group'); + + SObject accountRecord = ACCOUNT_OBJECT_TYPE.newSObject(); + accountRecord.put('Name', 'MatcherDefinitionTestAccount' + System.now()); + accountRecord.Id = fflib_IDGenerator.generate(Account.SObjectType); + ACCOUNT_RECORD = accountRecord; + + GROUP_RECORDS = new list { + new Group(Name = 'MatcherDefnTestGroup0'+System.now(), DeveloperName='MatcherDefnTestGroup0'+System.now().getTime(),Type='Queue'), + new Group(Name = 'MatcherDefnTestGroup1'+System.now(), DeveloperName='MatcherDefnTestGroup1'+System.now().getTime(),Type='Queue') + }; + insert GROUP_RECORDS; + + } + + @isTest + private static void whenConstructingCombinedWithNullConnectiveExpressionShouldThrowException() + { + try + { + fflib_IMatcher matcher = new fflib_MatcherDefinitions.Combined(null, INTERNAL_MATCHERS); + System.assert(false, 'Expecting exception'); + } + catch (fflib_ApexMocks.ApexMocksException e) + { + System.assertEquals('Invalid connective expression: null', e.getMessage()); + } + } + + @isTest + private static void whenConstructingCombinedWithNullInternalMatchersShouldThrowException() + { + try + { + fflib_IMatcher matcher = new fflib_MatcherDefinitions.Combined(fflib_MatcherDefinitions.Connective.ALL, null); + System.assert(false, 'Expecting exception'); + } + catch (fflib_ApexMocks.ApexMocksException e) + { + System.assertEquals('Invalid inner matchers: null', e.getMessage()); + } + } + + @isTest + private static void whenConstructingCombinedWithEmptyInternalMatchersShouldThrowException() + { + try + { + fflib_IMatcher matcher = new fflib_MatcherDefinitions.Combined(fflib_MatcherDefinitions.Connective.ALL, new List()); + System.assert(false, 'Expecting exception'); + } + catch (fflib_ApexMocks.ApexMocksException e) + { + System.assertEquals('Invalid inner matchers: ()', e.getMessage()); + } + } + + @isTest + private static void whenCombinedMatchesWithAllExpressionShouldReturnCorrectResults() + { + fflib_IMatcher matcher = new fflib_MatcherDefinitions.Combined(fflib_MatcherDefinitions.Connective.ALL, INTERNAL_MATCHERS); + System.assert(!matcher.matches(null)); + System.assert(!matcher.matches('ted')); + System.assert(!matcher.matches('bob')); + System.assert(!matcher.matches('tomfred')); + System.assert(matcher.matches('bobtomfred')); + } + + @isTest + private static void whenCombinedMatchesWithAtLeastOneExpressionShouldReturnCorrectResults() + { + fflib_IMatcher matcher = new fflib_MatcherDefinitions.Combined(fflib_MatcherDefinitions.Connective.AT_LEAST_ONE, INTERNAL_MATCHERS); + System.assert(!matcher.matches(null)); + System.assert(!matcher.matches('ted')); + System.assert(matcher.matches('bob')); + System.assert(matcher.matches('tomfred')); + System.assert(matcher.matches('bobtomfred')); + } + + @isTest + private static void whenCombinedMatchesWithNoneExpressionShouldReturnCorrectResults() + { + fflib_IMatcher matcher = new fflib_MatcherDefinitions.Combined(fflib_MatcherDefinitions.Connective.NONE, INTERNAL_MATCHERS); + System.assert(matcher.matches(null)); + System.assert(matcher.matches('ted')); + System.assert(!matcher.matches('bob')); + System.assert(!matcher.matches('tomfred')); + System.assert(!matcher.matches('bobtomfred')); + } + + @isTest + private static void whenCombinedMatcherToStringReturnsExpectedString() + { + List innerMatchers = new List{ + new StringMatcher('one'), + new StringMatcher('two'), + new StringMatcher('three') + }; + + System.assertEquals( + '[any of: "one", "two", "three"]', + '' + new fflib_MatcherDefinitions.Combined(fflib_MatcherDefinitions.Connective.AT_LEAST_ONE, innerMatchers) + ); + System.assertEquals( + '[all of: "one", "two", "three"]', + '' + new fflib_MatcherDefinitions.Combined(fflib_MatcherDefinitions.Connective.ALL, innerMatchers) + ); + System.assertEquals( + '[none of: "one", "two", "three"]', + '' + new fflib_MatcherDefinitions.Combined(fflib_MatcherDefinitions.Connective.NONE, innerMatchers) + ); + } + + @isTest + private static void constructEq_WithNullArg_ThrowsException() + { + try + { + new fflib_MatcherDefinitions.Eq(null); + System.assert(false, 'Expected exception'); + } + catch (fflib_ApexMocks.ApexMocksException e) + { + System.assertEquals('Arg cannot be null: null', e.getMessage()); + } + } + + @isTest + private static void whenEqMatchesShouldReturnCorrectResults() + { + List s1 = new List {'bob', 'tom'}; + List s2 = new List {'bob', 'tom'}; + fflib_IMatcher matcher = new fflib_MatcherDefinitions.Eq(s1); + System.assert(!matcher.matches(null)); + System.assert(!matcher.matches(new List {'bob'})); + System.assert(matcher.matches(s2)); + System.assert(matcher.matches(s1)); + } + + @isTest + private static void whenEqToStringShouldReturnExpectedString() + { + System.assertEquals('[equals 1]', '' + new fflib_MatcherDefinitions.Eq(1)); + } + + @isTest + private static void constructRefEq_WithNullArg_ThrowsException() + { + try + { + new fflib_MatcherDefinitions.RefEq(null); + System.assert(false, 'Expected exception'); + } + catch (fflib_ApexMocks.ApexMocksException e) + { + System.assertEquals('Arg cannot be null: null', e.getMessage()); + } + } + + @isTest + private static void whenRefEqMatchesShouldReturnCorrectResults() + { + List s1 = new List {'bob', 'tom'}; + List s2 = new List {'bob', 'tom'}; + fflib_IMatcher matcher = new fflib_MatcherDefinitions.RefEq(s1); + System.assert(!matcher.matches(null)); + System.assert(!matcher.matches(s2)); + System.assert(matcher.matches(s1)); + } + + @isTest + private static void whenRefEqToStringReturnsExpectedString() + { + List s1 = new List {'bob', 'tom'}; + System.assertEquals('[reference equals ' + JSON.serialize(s1, false) + ']', '' + new fflib_MatcherDefinitions.RefEq(s1)); + } + + @isTest + private static void whenAnyBooleanMatchesShouldReturnCorrectResults() + { + fflib_IMatcher matcher = new fflib_MatcherDefinitions.AnyBoolean(); + System.assert(!matcher.matches(null)); + System.assert(!matcher.matches(9)); + System.assert(matcher.matches(true)); + System.assert(matcher.matches(false)); + } + + @isTest + private static void whenAnyBooleanToStringReturnsExpectedString() + { + System.assertEquals('[any Boolean]', '' + new fflib_MatcherDefinitions.AnyBoolean()); + } + + @isTest + private static void whenAnyDateMatchesShouldReturnCorrectResults() + { + fflib_IMatcher matcher = new fflib_MatcherDefinitions.AnyDate(); + System.assert(!matcher.matches(null)); + System.assert(!matcher.matches(NOW)); + System.assert(matcher.matches(TODAY)); + } + + @isTest + private static void whenAnyDateToStringReturnsExpectedString() + { + System.assertEquals('[any Date]', '' + new fflib_MatcherDefinitions.AnyDate()); + } + + @isTest + private static void whenAnyDatetimeMatchesShouldReturnCorrectResults() + { + fflib_IMatcher matcher = new fflib_MatcherDefinitions.AnyDatetime(); + System.assert(!matcher.matches(null)); + System.assert(matcher.matches(NOW)); + System.assert(matcher.matches(TODAY)); + } + + @isTest + private static void whenAnyDatetimeToStringReturnsExpectedString() + { + System.assertEquals('[any DateTime]', '' + new fflib_MatcherDefinitions.AnyDatetime()); + } + + @isTest + private static void whenAnyDecimalMatchesShouldReturnCorrectResults() + { + fflib_IMatcher matcher = new fflib_MatcherDefinitions.AnyDecimal(); + System.assert(!matcher.matches(null)); + System.assert(!matcher.matches('bob')); + System.assert(matcher.matches(9)); + System.assert(matcher.matches(9L)); + System.assert(matcher.matches(9.99)); + } + + @isTest + private static void whenAnyDecimalToStringReturnsExpectedString() + { + System.assertEquals('[any Decimal]', '' + new fflib_MatcherDefinitions.AnyDecimal()); + } + + @isTest + private static void whenAnyDoubleMatchesShouldReturnCorrectResults() + { + fflib_IMatcher matcher = new fflib_MatcherDefinitions.AnyDouble(); + System.assert(!matcher.matches(null)); + System.assert(!matcher.matches('bob')); + System.assert(matcher.matches(9)); + System.assert(matcher.matches(9L)); + System.assert(matcher.matches(9.99)); + } + + @isTest + private static void whenAnyDoubleToStringReturnsExpectedString() + { + System.assertEquals('[any Double]', '' + new fflib_MatcherDefinitions.AnyDouble()); + } + + @isTest + private static void whenAnyFieldSetMatchesShouldReturnCorrectResults() + { + fflib_IMatcher matcher = new fflib_MatcherDefinitions.AnyFieldSet(); + System.assert(!matcher.matches(null)); + System.assert(!matcher.matches('bob')); + + Schema.FieldSet anyFieldSet = fflib_ApexMocksUtilsTest.findAnyFieldSet(); + if (anyFieldSet != null) + { + System.assert(matcher.matches(anyFieldSet)); + } + } + + @isTest + private static void whenAnyFieldSetToStringReturnsExpectedString() + { + System.assertEquals('[any FieldSet]', '' + new fflib_MatcherDefinitions.AnyFieldSet()); + } + + @isTest + private static void whenAnyIdMatchesShouldReturnCorrectResults() + { + String idString = fflib_IDGenerator.generate(Account.SObjectType); + Id accountId = fflib_IDGenerator.generate(Account.SObjectType); + fflib_IMatcher matcher = new fflib_MatcherDefinitions.AnyId(); + System.assert(!matcher.matches(null)); + System.assert(!matcher.matches('bob')); + System.assert(matcher.matches(idString)); + System.assert(matcher.matches(accountId)); + } + + @isTest + private static void whenAnyIdToStringReturnsExpectedString() + { + System.assertEquals('[any Id]', '' + new fflib_MatcherDefinitions.AnyId()); + } + + @isTest + private static void whenAnyIntegerMatchesShouldReturnCorrectResults() + { + fflib_IMatcher matcher = new fflib_MatcherDefinitions.AnyInteger(); + System.assert(!matcher.matches(null)); + System.assert(!matcher.matches('bob')); + System.assert(!matcher.matches(9L)); + System.assert(!matcher.matches(9.99)); + System.assert(matcher.matches(9)); + } + + @isTest + private static void whenAnyIntegerToStringReturnsExpectedString() + { + System.assertEquals('[any Integer]', '' + new fflib_MatcherDefinitions.AnyInteger()); + } + + @isTest + private static void whenAnyListMatchesShouldReturnCorrectResults() + { + fflib_IMatcher matcher = new fflib_MatcherDefinitions.AnyList(); + System.assert(!matcher.matches(null)); + System.assert(!matcher.matches('bob')); + System.assert(matcher.matches(new List())); + System.assert(matcher.matches(new List())); + System.assert(matcher.matches(new List())); + } + + @isTest + private static void whenAnyListToStringReturnsExpectedString() + { + System.assertEquals('[any list]', '' + new fflib_MatcherDefinitions.AnyList()); + } + + @isTest + private static void whenAnyLongMatchesShouldReturnCorrectResults() + { + fflib_IMatcher matcher = new fflib_MatcherDefinitions.AnyLong(); + System.assert(!matcher.matches(null)); + System.assert(!matcher.matches('bob')); + System.assert(!matcher.matches(9.99)); + System.assert(matcher.matches(9)); + System.assert(matcher.matches(9L)); + } + + @isTest + private static void whenAnyLongToStringReturnsExpectedString() + { + System.assertEquals('[any Long]', '' + new fflib_MatcherDefinitions.AnyLong()); + } + + @isTest + private static void whenAnyObjectMatchesShouldReturnCorrectResults() + { + fflib_IMatcher matcher = new fflib_MatcherDefinitions.AnyObject(); + System.assert(!matcher.matches(null)); + System.assert(matcher.matches('bob')); + System.assert(matcher.matches(9)); + System.assert(matcher.matches(new List())); + } + + @isTest + private static void whenAnyObjectToStringReturnsExpectedString() + { + System.assertEquals('[any Object]', '' + new fflib_MatcherDefinitions.AnyObject()); + } + + @isTest + private static void whenAnyStringMatchesShouldReturnCorrectResults() + { + fflib_IMatcher matcher = new fflib_MatcherDefinitions.AnyString(); + System.assert(!matcher.matches(null)); + System.assert(!matcher.matches(9)); + System.assert(matcher.matches('bob')); + } + + @isTest + private static void whenAnyStringToStringReturnsExpectedString() + { + System.assertEquals('[any String]', '' + new fflib_MatcherDefinitions.AnyString()); + } + + @isTest + private static void whenAnySObjectMatchesShouldReturnCorrectResults() + { + fflib_IMatcher matcher = new fflib_MatcherDefinitions.AnySObject(); + System.assert(!matcher.matches(null)); + System.assert(!matcher.matches('bob')); + System.assert(matcher.matches(new Account())); + } + + @isTest + private static void whenAnySObjectToStringReturnsExpectedString() + { + System.assertEquals('[any SObject]', '' + new fflib_MatcherDefinitions.AnySObject()); + } + + @isTest + private static void whenAnySObjectFieldMatchesShouldReturnCorrectResults() + { + fflib_IMatcher matcher = new fflib_MatcherDefinitions.AnySObjectField(); + System.assert(!matcher.matches(null)); + System.assert(!matcher.matches(new Account())); + System.assert(matcher.matches(Account.Id)); + } + + @isTest + private static void whenAnySObjectFieldToStringReturnsExpectedString() + { + System.assertEquals('[any SObjectField]', '' + new fflib_MatcherDefinitions.AnySObjectField()); + } + + @isTest + private static void whenAnySObjectTypeMatchesShouldReturnCorrectResults() + { + fflib_IMatcher matcher = new fflib_MatcherDefinitions.AnySObjectType(); + System.assert(!matcher.matches(null)); + System.assert(!matcher.matches(new Account())); + System.assert(matcher.matches(Account.SObjectType)); + } + + @isTest + private static void whenAnySObjectTypeToStringReturnsExpectedString() + { + System.assertEquals('[any SObjectType]', '' + new fflib_MatcherDefinitions.AnySObjectType()); + } + + @isTest + private static void constructDatetimeAfter_WithNullFromDatetime_ThrowsException() + { + try + { + new fflib_MatcherDefinitions.DatetimeAfter(null, true); + System.assert(false, 'Expected exception'); + } + catch (fflib_ApexMocks.ApexMocksException e) + { + System.assertEquals('Arg cannot be null: null', e.getMessage()); + } + } + + @isTest + private static void constructDatetimeAfter_WithNullInclusive_ThrowsException() + { + try + { + new fflib_MatcherDefinitions.DatetimeAfter(System.now(), null); + System.assert(false, 'Expected exception'); + } + catch (fflib_ApexMocks.ApexMocksException e) + { + System.assertEquals('Arg cannot be null: null', e.getMessage()); + } + } + + @isTest + private static void whenDatetimeAfterMatchesWithoutInclusiveShouldReturnCorrectResults() + { + fflib_IMatcher matcher = new fflib_MatcherDefinitions.DatetimeAfter(NOW, false); + System.assert(!matcher.matches(null)); + System.assert(!matcher.matches('bob')); + System.assert(!matcher.matches(NOW.addSeconds(-1))); + System.assert(!matcher.matches(NOW)); + System.assert(matcher.matches(NOW.addSeconds(1))); + } + + @isTest + private static void whenDatetimeAfterMatchesWithInclusiveShouldReturnCorrectResults() + { + fflib_IMatcher matcher = new fflib_MatcherDefinitions.DatetimeAfter(NOW, true); + System.assert(!matcher.matches(null)); + System.assert(!matcher.matches('bob')); + System.assert(!matcher.matches(NOW.addSeconds(-1))); + System.assert(matcher.matches(NOW)); + System.assert(matcher.matches(NOW.addSeconds(1))); + } + + @isTest + private static void whenDatetimeAfterWithInclusiveToStringReturnsExpectedString() + { + // Given + DateTime fromDate = DateTime.newInstanceGmt(2019, 1, 1, 12, 0, 0); + fflib_IMatcher matcher = new fflib_MatcherDefinitions.DatetimeAfter(fromDate, true); + + // When + String actual = '' + matcher; + + // Then + System.assertEquals('[on or after "2019-01-01T12:00:00.000Z"]', actual); + } + + @isTest + private static void whenDatetimeAfterWithNotInclusiveToStringReturnsExpectedString() + { + // Given + DateTime fromDate = DateTime.newInstanceGmt(2019, 1, 1, 12, 0, 0); + fflib_IMatcher matcher = new fflib_MatcherDefinitions.DatetimeAfter(fromDate, false); + + // When + String actual = '' + matcher; + + // Then + System.assertEquals('[after "2019-01-01T12:00:00.000Z"]', actual); + } + + @isTest + private static void constructDatetimeBefore_WithNullToDatetime_ThrowsException() + { + try + { + new fflib_MatcherDefinitions.DatetimeBefore(null, true); + System.assert(false, 'Expected exception'); + } + catch (fflib_ApexMocks.ApexMocksException e) + { + System.assertEquals('Arg cannot be null: null', e.getMessage()); + } + } + + @isTest + private static void constructDatetimeBefore_WithNullInclusive_ThrowsException() + { + try + { + new fflib_MatcherDefinitions.DatetimeBefore(System.now(), null); + System.assert(false, 'Expected exception'); + } + catch (fflib_ApexMocks.ApexMocksException e) + { + System.assertEquals('Arg cannot be null: null', e.getMessage()); + } + } + + @isTest + private static void whenDatetimeBeforeMatchesWithoutInclusiveShouldReturnCorrectResults() + { + fflib_IMatcher matcher = new fflib_MatcherDefinitions.DatetimeBefore(NOW, false); + System.assert(!matcher.matches(null)); + System.assert(!matcher.matches('bob')); + System.assert(!matcher.matches(NOW.addSeconds(1))); + System.assert(!matcher.matches(NOW)); + System.assert(matcher.matches(NOW.addSeconds(-1))); + } + + @isTest + private static void whenDatetimeBeforeMatchesWithInclusiveShouldReturnCorrectResults() + { + fflib_IMatcher matcher = new fflib_MatcherDefinitions.DatetimeBefore(NOW, true); + System.assert(!matcher.matches(null)); + System.assert(!matcher.matches('bob')); + System.assert(!matcher.matches(NOW.addSeconds(1))); + System.assert(matcher.matches(NOW)); + System.assert(matcher.matches(NOW.addSeconds(-1))); + } + + @isTest + private static void whenDatetimeBeforeWithInclusiveToStringReturnsExpectedString() + { + // Given + DateTime toDate = DateTime.newInstanceGmt(2019, 1, 1, 12, 0, 0); + fflib_IMatcher matcher = new fflib_MatcherDefinitions.DatetimeBefore(toDate, true); + + // When + String actual = '' + matcher; + + // Then + System.assertEquals('[on or before "2019-01-01T12:00:00.000Z"]', actual); + } + + @isTest + private static void whenDatetimeBeforeWithNotInclusiveToStringReturnsExpectedString() + { + // Given + DateTime toDate = DateTime.newInstanceGmt(2019, 1, 1, 12, 0, 0); + fflib_IMatcher matcher = new fflib_MatcherDefinitions.DatetimeBefore(toDate, false); + + // When + String actual = '' + matcher; + + // Then + System.assertEquals('[before "2019-01-01T12:00:00.000Z"]', actual); + } + + @isTest + private static void constructDatetimeBetween_WithNullFromDatetime_ThrowsException() + { + try + { + new fflib_MatcherDefinitions.DatetimeBetween(null, true, System.now(), true); + System.assert(false, 'Expected exception'); + } + catch (fflib_ApexMocks.ApexMocksException e) + { + System.assertEquals('Arg cannot be null: null', e.getMessage()); + } + } + + @isTest + private static void constructDatetimeBetween_WithNullToDatetime_ThrowsException() + { + try + { + new fflib_MatcherDefinitions.DatetimeBetween(System.now(), true, null, true); + System.assert(false, 'Expected exception'); + } + catch (fflib_ApexMocks.ApexMocksException e) + { + System.assertEquals('Arg cannot be null: null', e.getMessage()); + } + } + + @isTest + private static void constructDatetimeBetween_WithNullInclusiveFrom_ThrowsException() + { + try + { + new fflib_MatcherDefinitions.DatetimeBetween(System.now(), null, System.now(), true); + System.assert(false, 'Expected exception'); + } + catch (fflib_ApexMocks.ApexMocksException e) + { + System.assertEquals('Arg cannot be null: null', e.getMessage()); + } + } + + @isTest + private static void constructDatetimeBetween_WithNullInclusiveTo_ThrowsException() + { + try + { + new fflib_MatcherDefinitions.DatetimeBetween(System.now(), true, System.now(), null); + System.assert(false, 'Expected exception'); + } + catch (fflib_ApexMocks.ApexMocksException e) + { + System.assertEquals('Arg cannot be null: null', e.getMessage()); + } + } + + @isTest + private static void whenDatetimeBetweenMatchesWithInclusiveFromWithoutInclusiveToShouldReturnCorrectResults() + { + fflib_IMatcher matcher = new fflib_MatcherDefinitions.DatetimeBetween(NOW.addSeconds(-1), true, NOW.addSeconds(1), false); + System.assert(!matcher.matches(null)); + System.assert(!matcher.matches('bob')); + System.assert(!matcher.matches(NOW.addSeconds(-2))); + System.assert(!matcher.matches(NOW.addSeconds(1))); + System.assert(matcher.matches(NOW.addSeconds(-1))); + System.assert(matcher.matches(NOW)); + } + + @isTest + private static void whenDatetimeBetweenMatchesWithInclusiveToWithoutInclusiveFromShouldReturnCorrectResults() + { + fflib_IMatcher matcher = new fflib_MatcherDefinitions.DatetimeBetween(NOW.addSeconds(-1), false, NOW.addSeconds(1), true); + System.assert(!matcher.matches(null)); + System.assert(!matcher.matches('bob')); + System.assert(!matcher.matches(NOW.addSeconds(2))); + System.assert(!matcher.matches(NOW.addSeconds(-1))); + System.assert(matcher.matches(NOW)); + System.assert(matcher.matches(NOW.addSeconds(1))); + } + + @isTest + private static void whenDatetimeBetweenWithInclusiveToStringReturnsExpectedString() + { + // Given + DateTime fromDate = DateTime.newInstanceGmt(2019, 1, 1, 12, 0, 0); + DateTime toDate = DateTime.newInstanceGmt(2019, 1, 3, 12, 0, 0); + fflib_IMatcher matcher = new fflib_MatcherDefinitions.DatetimeBetween(fromDate, true, toDate, true); + + // When + String actual = '' + matcher; + + // Then + System.assertEquals('[on or after "2019-01-01T12:00:00.000Z" and on or before "2019-01-03T12:00:00.000Z"]', actual); + } + + @isTest + private static void whenDatetimeBetweenWithNotInclusiveToStringReturnsExpectedString() + { + // Given + DateTime fromDate = DateTime.newInstanceGmt(2019, 1, 1, 12, 0, 0); + DateTime toDate = DateTime.newInstanceGmt(2019, 1, 3, 12, 0, 0); + fflib_IMatcher matcher = new fflib_MatcherDefinitions.DatetimeBetween(fromDate, false, toDate, false); + + // When + String actual = '' + matcher; + + // Then + System.assertEquals('[after "2019-01-01T12:00:00.000Z" and before "2019-01-03T12:00:00.000Z"]', actual); + } + + @isTest + private static void constructDecimalBetween_WithNullLower_ThrowsException() + { + try + { + new fflib_MatcherDefinitions.DecimalBetween(null, true, 123, true); + System.assert(false, 'Expected exception'); + } + catch (fflib_ApexMocks.ApexMocksException e) + { + System.assertEquals('Arg cannot be null: null', e.getMessage()); + } + } + + @isTest + private static void constructDecimalBetween_WithNullInclusiveLower_ThrowsException() + { + try + { + new fflib_MatcherDefinitions.DecimalBetween(123, null, 123, true); + System.assert(false, 'Expected exception'); + } + catch (fflib_ApexMocks.ApexMocksException e) + { + System.assertEquals('Arg cannot be null: null', e.getMessage()); + } + } + + @isTest + private static void constructDecimalBetween_WithNullUpper_ThrowsException() + { + try + { + new fflib_MatcherDefinitions.DecimalBetween(123, true, null, true); + System.assert(false, 'Expected exception'); + } + catch (fflib_ApexMocks.ApexMocksException e) + { + System.assertEquals('Arg cannot be null: null', e.getMessage()); + } + } + + @isTest + private static void constructDecimalBetween_WithNullInclusiveUpper_ThrowsException() + { + try + { + new fflib_MatcherDefinitions.DecimalBetween(123, true, 123, null); + System.assert(false, 'Expected exception'); + } + catch (fflib_ApexMocks.ApexMocksException e) + { + System.assertEquals('Arg cannot be null: null', e.getMessage()); + } + } + + @isTest + private static void whenDecimalBetweenMatchesShouldReturnCorrectResults() + { + Integer lower = 5; + Integer upper = 10; + + fflib_IMatcher exLowerExUpper = new fflib_MatcherDefinitions.DecimalBetween(lower, false, upper, false); + fflib_IMatcher exLowerInUpper = new fflib_MatcherDefinitions.DecimalBetween(lower, false, upper, true); + fflib_IMatcher inLowerExUpper = new fflib_MatcherDefinitions.DecimalBetween(lower, true, upper, false); + fflib_IMatcher inLowerInUpper = new fflib_MatcherDefinitions.DecimalBetween(lower, true, upper, true); + + //Exclusive lower, exclusive upper + System.assert(!exLowerExUpper.matches(lower - 1)); + System.assert(!exLowerExUpper.matches(lower)); + System.assert(exLowerExUpper.matches(lower + 1)); + System.assert(exLowerExUpper.matches(upper - 1)); + System.assert(!exLowerExUpper.matches(upper)); + System.assert(!exLowerExUpper.matches(upper + 1)); + System.assert(!exLowerExUpper.matches(null)); + System.assert(!exLowerExUpper.matches('NotADecimal')); + + //Exclusive lower, inclusive upper + System.assert(!exLowerInUpper.matches(lower - 1)); + System.assert(!exLowerInUpper.matches(lower)); + System.assert(exLowerInUpper.matches(lower + 1)); + System.assert(exLowerInUpper.matches(upper - 1)); + System.assert(exLowerInUpper.matches(upper)); + System.assert(!exLowerInUpper.matches(upper + 1)); + System.assert(!exLowerInUpper.matches(null)); + System.assert(!exLowerInUpper.matches('NotADecimal')); + + //Inclusive lower, exclusive upper + System.assert(!inLowerExUpper.matches(lower - 1)); + System.assert(inLowerExUpper.matches(lower)); + System.assert(inLowerExUpper.matches(lower + 1)); + System.assert(inLowerExUpper.matches(upper - 1)); + System.assert(!inLowerExUpper.matches(upper)); + System.assert(!inLowerExUpper.matches(upper + 1)); + System.assert(!inLowerExUpper.matches(null)); + System.assert(!inLowerExUpper.matches('NotADecimal')); + + //Inclusive lower, inclusive upper + System.assert(!inLowerInUpper.matches(lower - 1)); + System.assert(inLowerInUpper.matches(lower)); + System.assert(inLowerInUpper.matches(lower + 1)); + System.assert(inLowerInUpper.matches(upper - 1)); + System.assert(inLowerInUpper.matches(upper)); + System.assert(!inLowerInUpper.matches(upper + 1)); + System.assert(!inLowerInUpper.matches(null)); + System.assert(!inLowerInUpper.matches('NotADecimal')); + } + + @isTest + private static void whenDecimalBetweenToStringReturnsExpectedString() + { + Integer lower = 5; + Integer upper = 10; + + List formatList = new List{lower, upper}; + + System.assertEquals(String.format('greater than {0} and less than {1}', formatList), + '' + new fflib_MatcherDefinitions.DecimalBetween(lower, false, upper, false)); + System.assertEquals(String.format('greater than {0} and less than or equal to {1}', formatList), + '' + new fflib_MatcherDefinitions.DecimalBetween(lower, false, upper, true)); + System.assertEquals(String.format('greater than or equal to {0} and less than {1}', formatList), + '' + new fflib_MatcherDefinitions.DecimalBetween(lower, true, upper, false)); + System.assertEquals(String.format('greater than or equal to {0} and less than or equal to {1}', formatList), + '' + new fflib_MatcherDefinitions.DecimalBetween(lower, true, upper, true)); + } + + @isTest + private static void constructDecimalLessThan_WithNullToMatch_ThrowsException() + { + try + { + new fflib_MatcherDefinitions.DecimalLessThan(null, true); + System.assert(false, 'Expected exception'); + } + catch (fflib_ApexMocks.ApexMocksException e) + { + System.assertEquals('Arg cannot be null: null', e.getMessage()); + } + } + + @isTest + private static void constructDecimalLessThan_WithNullInclusive_ThrowsException() + { + try + { + new fflib_MatcherDefinitions.DecimalLessThan(123, null); + System.assert(false, 'Expected exception'); + } + catch (fflib_ApexMocks.ApexMocksException e) + { + System.assertEquals('Arg cannot be null: null', e.getMessage()); + } + } + + @isTest + private static void whenDecimalLessThanMatchesShouldReturnCorrectResults() + { + Integer toMatch = 5; + + fflib_IMatcher exclusive = new fflib_MatcherDefinitions.DecimalLessThan(toMatch, false); + fflib_IMatcher inclusive = new fflib_MatcherDefinitions.DecimalLessThan(toMatch, true); + + //Exclusive + System.assert(exclusive.matches(toMatch - 1)); + System.assert(!exclusive.matches(toMatch)); + System.assert(!exclusive.matches(toMatch + 1)); + System.assert(!exclusive.matches(null)); + System.assert(!exclusive.matches('NotADecimal')); + + //Inclusive + System.assert(inclusive.matches(toMatch - 1)); + System.assert(inclusive.matches(toMatch)); + System.assert(!inclusive.matches(toMatch + 1)); + System.assert(!inclusive.matches(null)); + System.assert(!inclusive.matches('NotADecimal')); + } + + @isTest + private static void whenDecimalLessThanToStringReturnsExpectedString() + { + Integer toMatch = 5; + + System.assertEquals('[less than or equal to ' + toMatch + ']', + '' + new fflib_MatcherDefinitions.DecimalLessThan(toMatch, true)); + System.assertEquals('[less than ' + toMatch + ']', + '' + new fflib_MatcherDefinitions.DecimalLessThan(toMatch, false)); + } + + @isTest + private static void constructDecimalMoreThan_WithNullToMatch_ThrowsException() + { + try + { + new fflib_MatcherDefinitions.DecimalMoreThan(null, true); + System.assert(false, 'Expected exception'); + } + catch (fflib_ApexMocks.ApexMocksException e) + { + System.assertEquals('Arg cannot be null: null', e.getMessage()); + } + } + + @isTest + private static void constructDecimalMoreThan_WithNullInclusive_ThrowsException() + { + try + { + new fflib_MatcherDefinitions.DecimalMoreThan(123, null); + System.assert(false, 'Expected exception'); + } + catch (fflib_ApexMocks.ApexMocksException e) + { + System.assertEquals('Arg cannot be null: null', e.getMessage()); + } + } + + @isTest + private static void whenDecimalMoreThanMatchesShouldReturnCorrectResults() + { + Integer toMatch = 5; + + fflib_IMatcher exclusive = new fflib_MatcherDefinitions.DecimalMoreThan(toMatch, false); + fflib_IMatcher inclusive = new fflib_MatcherDefinitions.DecimalMoreThan(toMatch, true); + + //Exclusive + System.assert(!exclusive.matches(toMatch - 1)); + System.assert(!exclusive.matches(toMatch)); + System.assert(exclusive.matches(toMatch + 1)); + System.assert(!exclusive.matches(null)); + System.assert(!exclusive.matches('NotADecimal')); + + //Inclusive + System.assert(!inclusive.matches(toMatch - 1)); + System.assert(inclusive.matches(toMatch)); + System.assert(inclusive.matches(toMatch + 1)); + System.assert(!inclusive.matches(null)); + System.assert(!inclusive.matches('NotADecimal')); + } + + @isTest + private static void whenDecimalMoreThanToStringReturnsExpectedString() + { + Integer toMatch = 5; + + System.assertEquals('[greater than or equal to ' + toMatch + ']', + '' + new fflib_MatcherDefinitions.DecimalMoreThan(toMatch, true)); + System.assertEquals('[greater than ' + toMatch + ']', + '' + new fflib_MatcherDefinitions.DecimalMoreThan(toMatch, false)); + } + + @isTest + private static void constructFieldSetEquivalentTo_WithNullFieldSet_ThrowsException() + { + try + { + new fflib_MatcherDefinitions.FieldSetEquivalentTo(null); + System.assert(false, 'Expected exception'); + } + catch (fflib_ApexMocks.ApexMocksException e) + { + System.assertEquals('Arg cannot be null: null', e.getMessage()); + } + } + + @isTest + private static void whenFieldSetEquivalentToWithoutFieldSetShouldNeverMatch() + { + //Cheap test to maintain 100% code coverage, even in orgs without field sets defined. + fflib_IMatcher matcher = new fflib_MatcherDefinitions.FieldSetEquivalentTo(); + System.assert(!matcher.matches(null)); + } + + @isTest + private static void whenFieldSetEquivalentToMatchesShouldReturnCorrectResults() + { + Schema.FieldSet anyFieldSet = fflib_ApexMocksUtilsTest.findAnyFieldSet(); + if (anyFieldSet == null) + { + return; + } + + fflib_IMatcher matcher = new fflib_MatcherDefinitions.FieldSetEquivalentTo(anyFieldSet); + System.assert(!matcher.matches(null)); + System.assert(!matcher.matches('hello')); + System.assert(matcher.matches(anyFieldSet)); + } + + @isTest + private static void whenFieldSetEquivalentToToStringReturnsExpectedString() + { + Schema.FieldSet anyFieldSet = fflib_ApexMocksUtilsTest.findAnyFieldSet(); + if (anyFieldSet == null) + { + return; + } + Set fieldSetMembers = new Set((anyFieldSet).getFields()); + + System.assertEquals('[FieldSet with fields ' + JSON.serialize(fieldSetMembers, false) + ']', + '' + new fflib_MatcherDefinitions.FieldSetEquivalentTo(anyFieldSet)); + } + + @isTest + private static void whenIsNullMatchesShouldReturnCorrectResults() + { + fflib_IMatcher matcher = new fflib_MatcherDefinitions.IsNull(); + System.assert(!matcher.matches('bob')); + System.assert(matcher.matches(null)); + } + + @isTest + private static void whenIsNullToStringReturnsExpectedString() + { + System.assertEquals('[is null]', '' + new fflib_MatcherDefinitions.IsNull()); + } + + @isTest + private static void whenIsNotNullMatchesShouldReturnCorrectResults() + { + fflib_IMatcher matcher = new fflib_MatcherDefinitions.IsNotNull(); + System.assert(!matcher.matches(null)); + System.assert(matcher.matches('bob')); + } + + @isTest + private static void whenIsNotNullToStringReturnsExpectedString() + { + System.assertEquals('[is not null]', '' + new fflib_MatcherDefinitions.IsNotNull()); + } + + @isTest + private static void whenListContainsMatchesShouldReturnCorrectResults() + { + List names = new List{ 'bob', 'tom', 'fred' }; + List empty = new List(); + + System.assert(!new fflib_MatcherDefinitions.ListContains('fred').matches(null)); + System.assert(!new fflib_MatcherDefinitions.ListContains('fred').matches(empty)); + System.assert(!new fflib_MatcherDefinitions.ListContains('jack').matches(names)); + System.assert(new fflib_MatcherDefinitions.ListContains('fred').matches(names)); + System.assert(!new fflib_MatcherDefinitions.ListContains('fred').matches('NotAList')); + } + + @isTest + private static void whenListContainsToStringReturnsExpectedString() + { + System.assertEquals('[list containing "hello"]', '' + new fflib_MatcherDefinitions.ListContains('hello')); + } + + @isTest + private static void whenListIsEmptyMatchesShouldReturnCorrectResults() + { + fflib_IMatcher matcher = new fflib_MatcherDefinitions.ListIsEmpty(); + + List names = new List{ 'bob', 'tom', 'fred' }; + List empty = new List(); + + System.assert(!matcher.matches(null)); + System.assert(!matcher.matches(names)); + System.assert(matcher.matches(empty)); + System.assert(!matcher.matches('NotAList')); + } + + @isTest + private static void whenListIsEmptyToStringReturnsExpectedString() + { + System.assertEquals('[empty list]', '' + new fflib_MatcherDefinitions.ListIsEmpty()); + } + + @isTest + private static void constructSObjectOfType_WithNullArg_ThrowsException() + { + try + { + new fflib_MatcherDefinitions.SObjectOfType(null); + System.assert(false, 'Expected exception'); + } + catch (fflib_ApexMocks.ApexMocksException e) + { + System.assertEquals('Arg cannot be null: null', e.getMessage()); + } + } + + @isTest + private static void whenSObjectOfTypeMatchesShouldReturnCorrectResults() + { + fflib_IMatcher matcher = new fflib_MatcherDefinitions.SObjectOfType(ACCOUNT_OBJECT_TYPE); + + System.assert(!matcher.matches(null)); + System.assert(!matcher.matches(OPPORTUNITY_OBJECT_TYPE.newSObject())); + System.assert(!matcher.matches('NotASObject')); + + System.assert(matcher.matches(ACCOUNT_OBJECT_TYPE.newSObject())); + System.assert(matcher.matches(ACCOUNT_RECORD)); + } + + @isTest + private static void whenSObjectOfTypeToStringReturnsExpectedString() + { + System.assertEquals('[SObject of type Account]', '' + new fflib_MatcherDefinitions.SObjectOfType(ACCOUNT_OBJECT_TYPE)); + } + + @isTest + private static void constructSObjectWith_WithNullArg_ThrowsException() + { + try + { + new fflib_MatcherDefinitions.SObjectWith(null); + System.assert(false, 'Expected exception'); + } + catch (fflib_ApexMocks.ApexMocksException e) + { + System.assertEquals('Arg cannot be null/empty: null', e.getMessage()); + } + } + + @isTest + private static void constructSObjectWith_WithEmptyArg_ThrowsException() + { + try + { + new fflib_MatcherDefinitions.SObjectWith(new Map()); + System.assert(false, 'Expected exception'); + } + catch (fflib_ApexMocks.ApexMocksException e) + { + System.assertEquals('Arg cannot be null/empty: {}', e.getMessage()); + } + } + + @isTest + private static void whenSObjectWithMatchesShouldReturnCorrectResults() + { + Map fields = ACCOUNT_OBJECT_TYPE.getDescribe().fields.getMap(); + Schema.SObjectField idField = fields.get('Id'); + Schema.SObjectField nameField = fields.get('Name'); + Schema.SObjectField createdDateField = fields.get('CreatedDate'); + + Map queriedFieldValues = new Map + { + idField => ACCOUNT_RECORD.Id, + nameField => ACCOUNT_RECORD.get('Name') + }; + + Map notQueriedFieldValues = new Map + { + createdDateField => System.now() + }; + + fflib_IMatcher matcher = new fflib_MatcherDefinitions.SObjectWith(queriedFieldValues); + + System.assert(!matcher.matches(null)); + System.assert(!matcher.matches(OPPORTUNITY_OBJECT_TYPE.newSObject())); + System.assert(!matcher.matches(ACCOUNT_OBJECT_TYPE.newSObject())); + System.assert(!matcher.matches('NotASObject')); + + System.assert(matcher.matches(ACCOUNT_RECORD)); + + System.assert(!new fflib_MatcherDefinitions.SObjectWith(notQueriedFieldValues).matches(ACCOUNT_RECORD)); + } + + @isTest + private static void whenSObjectWithToStringReturnsExpectedString() + { + fflib_IMatcher matcher = new fflib_MatcherDefinitions.SObjectWith(new Map{ + Account.Name => 'Test' + }); + System.assertEquals('[SObject with fields {"Name":"Test"}]', '' + matcher); + } + + @isTest + private static void whenSObjectsWithInOrderMatchesShouldReturnCorrectResults() + { + Map fields = GROUP_OBJECT_TYPE.getDescribe().fields.getMap(); + Schema.SObjectField idField = fields.get('Id'); + Schema.SObjectField nameField = fields.get('Name'); + Schema.SObjectField createdDateField = fields.get('CreatedDate'); + + list> queriedFieldValues = new list> + { + new map + { + idField => GROUP_RECORDS[0].Id, + nameField => GROUP_RECORDS[0].get('Name') + }, + new map + { + idField => GROUP_RECORDS[1].Id, + nameField => GROUP_RECORDS[1].get('Name') + } + }; + + list> failingFieldValues = new list> + { + new map + { + idField => GROUP_RECORDS[0].Id, + nameField => GROUP_RECORDS[0].get('Name') + }, + new map + { + idField => GROUP_RECORDS[1].Id, + nameField => GROUP_RECORDS[1].get('Name') + 'test' + } + }; + + list> notQueriedFieldValues = new list> + { + new map + { + createdDateField => System.now() + }, + new map + { + createdDateField => System.now() + } + }; + + fflib_IMatcher matcher = new fflib_MatcherDefinitions.SObjectsWith(queriedFieldValues); + + System.assert(!matcher.matches(null)); + System.assert(!matcher.matches(new list + { + OPPORTUNITY_OBJECT_TYPE.newSObject(), + OPPORTUNITY_OBJECT_TYPE.newSObject() + } + )); + System.assert(!matcher.matches(new list + { + GROUP_OBJECT_TYPE.newSObject(), + GROUP_OBJECT_TYPE.newSObject() + } + ),'sObjectsWith arity agrees but arg doesn\'t'); + System.assert(!matcher.matches('NotAListofSObject')); + + System.assert(matcher.matches(GROUP_RECORDS),'toMatch and args have same arity and in same order'); + System.assert (!matcher.matches(new list {GROUP_RECORDS[1],GROUP_RECORDS[0]}), + 'sObjectsWith toMatch and args have same arity but args are in different order than toMatch') ; + + + System.assert(!new fflib_MatcherDefinitions.SObjectsWith(notQueriedFieldValues).matches(GROUP_RECORDS)); + System.assert(!new fflib_MatcherDefinitions.SObjectsWith(failingFieldValues).matches(GROUP_RECORDS)); + } + + @isTest + private static void whenSObjectsInAnyOrderWithMatchesShouldReturnCorrectResults() + { + Map fields = GROUP_OBJECT_TYPE.getDescribe().fields.getMap(); + Schema.SObjectField idField = fields.get('Id'); + Schema.SObjectField nameField = fields.get('Name'); + Schema.SObjectField createdDateField = fields.get('CreatedDate'); + + list> queriedFieldValues = new list> + { + new map + { + idField => GROUP_RECORDS[0].Id, + nameField => GROUP_RECORDS[0].get('Name') + }, + new map + { + idField => GROUP_RECORDS[1].Id, + nameField => GROUP_RECORDS[1].get('Name') + } + }; + + list> notQueriedFieldValues = new list> + { + new map + { + createdDateField => System.now() + }, + new map + { + createdDateField => System.now() + } + }; + + fflib_IMatcher matcher = new fflib_MatcherDefinitions.SObjectsWith(queriedFieldValues,false); // any order + + System.assert(!matcher.matches(null)); + System.assert(!matcher.matches(new list + { + OPPORTUNITY_OBJECT_TYPE.newSObject(), + OPPORTUNITY_OBJECT_TYPE.newSObject() + } + )); + System.assert(!matcher.matches(new list + { + GROUP_OBJECT_TYPE.newSObject(), + GROUP_OBJECT_TYPE.newSObject() + } + ),'sObjectsWith arity agrees but arg doesn\'t match matcher'); + System.assert(!matcher.matches('NotAListofSObject')); + + System.assert(matcher.matches(GROUP_RECORDS),'toMatch and args have same arity and in same order. Match should be OK'); + System.assert (matcher.matches(new list {GROUP_RECORDS[1],GROUP_RECORDS[0]}), + 'sObjectsWith toMatch and args have same arity but args are in diff order than matcher. Should be OK') ; + + + System.assert(!new fflib_MatcherDefinitions.SObjectsWith(notQueriedFieldValues,false).matches(GROUP_RECORDS)); + } + + @isTest + private static void whenSObjectsWithToStringReturnsExpectedString() + { + List> toMatch = new List>{ + new Map { + Account.Name => 'Test' + } + }; + + System.assertEquals('[ordered SObjects with [{"Name":"Test"}]]', '' + new fflib_MatcherDefinitions.SObjectsWith(toMatch, true)); + System.assertEquals('[unordered SObjects with [{"Name":"Test"}]]', '' + new fflib_MatcherDefinitions.SObjectsWith(toMatch, false)); + } + + @isTest + private static void whenSObjectsWithDifferentArityMatchesShouldReturnFalse() + { + List> toMatch = new List>{ + new Map { + Account.Name => 'Test' + } + }; + Boolean matchResult = new fflib_MatcherDefinitions.SObjectsWith(toMatch).matches(new List{}); + System.assertEquals(matchResult, false); + } + + @isTest + private static void constructSObjectsWith_WithNullArg_ThrowsException() + { + try { + new fflib_MatcherDefinitions.SObjectsWith(null); + System.assert(false, 'Expected exception'); + } + catch (fflib_ApexMocks.ApexMocksException e) + { + System.assertEquals('Arg cannot be null/empty/other than list of map: null', e.getMessage()); + } + } + + @isTest + private static void constructSObjectWithId_WithNullArg_ThrowsException() + { + try + { + new fflib_MatcherDefinitions.SObjectWithId(null); + System.assert(false, 'Expected exception'); + } + catch (fflib_ApexMocks.ApexMocksException e) + { + System.assertEquals('Arg cannot be null: null', e.getMessage()); + } + } + + @isTest + private static void whenSObjectWithIdMatchesShouldReturnCorrectResults() + { + fflib_IMatcher matcher = new fflib_MatcherDefinitions.SObjectWithId(ACCOUNT_RECORD.Id); + + System.assert(!matcher.matches(null)); + System.assert(!matcher.matches(OPPORTUNITY_OBJECT_TYPE.newSObject())); + System.assert(!matcher.matches(ACCOUNT_OBJECT_TYPE.newSObject())); + System.assert(!matcher.matches('NotASObject')); + + System.assert(matcher.matches(ACCOUNT_RECORD)); + } + + @isTest + private static void whenSObjectWithIdToStringReturnsExpectedString() + { + Id recordId = '001000000000001AAA'; + System.assertEquals('[SObject with Id "001000000000001AAA"]', '' + new fflib_MatcherDefinitions.SObjectWithId(recordId)); + } + + @isTest + private static void constructSObjectWithName_WithNullArg_ThrowsException() + { + try + { + new fflib_MatcherDefinitions.SObjectWithName(null); + System.assert(false, 'Expected exception'); + } + catch (fflib_ApexMocks.ApexMocksException e) + { + System.assertEquals('Arg cannot be null: null', e.getMessage()); + } + } + + @isTest + private static void whenSObjectWithNameMatchesShouldReturnCorrectResults() + { + fflib_IMatcher matcher = new fflib_MatcherDefinitions.SObjectWithName((String)ACCOUNT_RECORD.get('Name')); + + System.assert(!matcher.matches(null)); + System.assert(!matcher.matches(OPPORTUNITY_OBJECT_TYPE.newSObject())); + System.assert(!matcher.matches(ACCOUNT_OBJECT_TYPE.newSObject())); + System.assert(!matcher.matches('NotASObject')); + + System.assert(matcher.matches(ACCOUNT_RECORD)); + } + + @isTest + private static void whenSObjectWithNameToStringReturnsExpectedString() + { + System.assertEquals('[SObject with Name "Test"]', '' + new fflib_MatcherDefinitions.SObjectWithName('Test')); + } + + @isTest + private static void whenStringContainsMatchesShouldReturnCorrectResults() + { + fflib_IMatcher matcher = new fflib_MatcherDefinitions.StringContains('bob'); + System.assert(!matcher.matches(7)); + System.assert(!matcher.matches(null)); + System.assert(!matcher.matches('')); + System.assert(!matcher.matches('blob')); + System.assert(matcher.matches('bob')); + System.assert(matcher.matches('bobby')); + } + + @isTest + private static void constructStringContains_WithNullArg_ThrowsException() + { + try + { + new fflib_MatcherDefinitions.StringContains(null); + System.assert(false, 'Expected exception'); + } + catch (fflib_ApexMocks.ApexMocksException e) + { + System.assertEquals('Arg cannot be null: null', e.getMessage()); + } + } + + @isTest + private static void whenStringContainsToStringReturnsExpectedStrings() + { + System.assertEquals('[contains "hello"]', '' + new fflib_MatcherDefinitions.StringContains('hello')); + } + + @isTest + private static void whenStringEndsWithMatchesShouldReturnCorrectResults() + { + fflib_IMatcher matcher = new fflib_MatcherDefinitions.StringEndsWith('bob'); + System.assert(!matcher.matches(7)); + System.assert(!matcher.matches(null)); + System.assert(!matcher.matches('')); + System.assert(!matcher.matches('bobby')); + System.assert(matcher.matches('bob')); + System.assert(matcher.matches('jimbob')); + } + + @isTest + private static void constructStringEndsWith_WithNullArg_ThrowsException() + { + try + { + new fflib_MatcherDefinitions.StringEndsWith(null); + System.assert(false, 'Expected exception'); + } + catch (fflib_ApexMocks.ApexMocksException e) + { + System.assertEquals('Arg cannot be null: null', e.getMessage()); + } + } + + @isTest + private static void whenStringEndsWithToStringReturnsExpectedStrings() + { + System.assertEquals('[ends with "hello"]', '' + new fflib_MatcherDefinitions.StringEndsWith('hello')); + } + + @isTest + private static void whenIsBlankWithMatchesShouldReturnCorrectResults() + { + fflib_IMatcher matcher = new fflib_MatcherDefinitions.StringIsBlank(); + System.assert(!matcher.matches(7)); + System.assert(!matcher.matches('bob')); + System.assert(matcher.matches(null)); + System.assert(matcher.matches('')); + } + + @isTest + private static void whenStringIsBlankToStringReturnsExpectedStrings() + { + System.assertEquals('[blank String]', '' + new fflib_MatcherDefinitions.StringIsBlank()); + } + + @isTest + private static void whenIsNotBlankWithMatchesShouldReturnCorrectResults() + { + fflib_IMatcher matcher = new fflib_MatcherDefinitions.StringIsNotBlank(); + System.assert(!matcher.matches(7)); + System.assert(!matcher.matches(null)); + System.assert(!matcher.matches('')); + System.assert(matcher.matches('bob')); + } + + @isTest + private static void whenStringIsNotBlankToStringReturnsExpectedStrings() + { + System.assertEquals('[non-blank String]', '' + new fflib_MatcherDefinitions.StringIsNotBlank()); + } + + @isTest + private static void whenStringMatchesMatchesShouldReturnCorrectResults() + { + fflib_IMatcher matcher = new fflib_MatcherDefinitions.StringMatches('(b|m)o[a-z]*'); + System.assert(!matcher.matches(7)); + System.assert(!matcher.matches(null)); + System.assert(!matcher.matches('bib')); + System.assert(!matcher.matches('jimbob')); + System.assert(!matcher.matches('tom')); + System.assert(!matcher.matches('bob1')); + System.assert(matcher.matches('bobby')); + System.assert(matcher.matches('mo')); + } + + @isTest + private static void whenStringMatchesToStringReturnsExpectedStrings() + { + System.assertEquals('[matches regex "hello"]', '' + new fflib_MatcherDefinitions.StringMatches('hello')); + } + + @isTest + private static void constructStringMatches_WithNullArg_ThrowsException() + { + try + { + new fflib_MatcherDefinitions.StringMatches(null); + System.assert(false, 'Expected exception'); + } + catch (fflib_ApexMocks.ApexMocksException e) + { + System.assertEquals('Arg cannot be null: null', e.getMessage()); + } + } + + @isTest + private static void whenStringStartsWithMatchesShouldReturnCorrectResults() + { + fflib_IMatcher matcher = new fflib_MatcherDefinitions.StringStartsWith('bob'); + System.assert(!matcher.matches(7)); + System.assert(!matcher.matches(null)); + System.assert(!matcher.matches('')); + System.assert(!matcher.matches('jimbob')); + System.assert(matcher.matches('bob')); + System.assert(matcher.matches('bobby')); + } + + @isTest + private static void whenStringStartsWithToStringReturnsExpectedStrings() + { + System.assertEquals('[starts with "hello"]', '' + new fflib_MatcherDefinitions.StringStartsWith('hello')); + } + + @isTest + private static void constructStringStartsWith_WithNullArg_ThrowsException() + { + try + { + new fflib_MatcherDefinitions.StringStartsWith(null); + System.assert(false, 'Expected exception'); + } + catch (fflib_ApexMocks.ApexMocksException e) + { + System.assertEquals('Arg cannot be null: null', e.getMessage()); + } + } + + @isTest + private static void whenJSONExceptionOccursStringifyShouldReturnsObjectToString() + { + // SObjectField object definitely can't be serialized by JSON.serialize() method + Schema.SObjectField sObjField = Account.Description; + System.assertEquals('' + sObjField, fflib_MatcherDefinitions.stringify(sObjField)); + } + + private class StringMatcher implements fflib_IMatcher + { + private final String value; + + public StringMatcher(String value) + { + this.value = value; + } + + public Boolean matches(Object arg) + { + return true; + } + + public override String toString() + { + return '"' + value + '"'; + } + } +} diff --git a/sfdx-source/apex-mocks/tests/fflib_MatcherDefinitionsTest.cls-meta.xml b/sfdx-source/apex-mocks/tests/fflib_MatcherDefinitionsTest.cls-meta.xml new file mode 100644 index 00000000000..08c4dbd5cfa --- /dev/null +++ b/sfdx-source/apex-mocks/tests/fflib_MatcherDefinitionsTest.cls-meta.xml @@ -0,0 +1,4 @@ + + + 52.0 + diff --git a/sfdx-source/apex-mocks/tests/fflib_MethodArgValuesTest.cls b/sfdx-source/apex-mocks/tests/fflib_MethodArgValuesTest.cls new file mode 100644 index 00000000000..14051aad1b8 --- /dev/null +++ b/sfdx-source/apex-mocks/tests/fflib_MethodArgValuesTest.cls @@ -0,0 +1,108 @@ +/** + * Copyright (c) 2014-2016, FinancialForce.com, inc + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the FinancialForce.com, inc nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +@isTest +public with sharing class fflib_MethodArgValuesTest +{ + @isTest + private static void equalsReturnsExpectedResults() + { + //Given + fflib_MethodArgValues qm1 = new fflib_MethodArgValues(new List{ 'hello' }); + fflib_MethodArgValues qm2 = new fflib_MethodArgValues(new List{ 'hello' }); + fflib_MethodArgValues qm3 = new fflib_MethodArgValues(new List{ 'hi' }); + fflib_MethodArgValues qm4 = new fflib_MethodArgValues(new List{ 'hello', 'hello', 'hello' }); + fflib_MethodArgValues qm5 = new fflib_MethodArgValues(new List()); + fflib_MethodArgValues qm6 = new fflib_MethodArgValues(null); + + //When/thens + System.assertEquals(qm1, qm1); + System.assertEquals(qm1, qm2); + System.assertNotEquals(qm1, qm3); + System.assertNotEquals(qm1, qm4); + System.assertNotEquals(qm1, qm5); + System.assertNotEquals(qm1, qm6); + + System.assertEquals(qm2, qm2); + System.assertNotEquals(qm2, qm3); + System.assertNotEquals(qm2, qm4); + System.assertNotEquals(qm2, qm5); + System.assertNotEquals(qm2, qm6); + + System.assertEquals(qm3, qm3); + System.assertNotEquals(qm3, qm4); + System.assertNotEquals(qm3, qm5); + System.assertNotEquals(qm3, qm6); + + System.assertEquals(qm4, qm4); + System.assertNotEquals(qm4, qm5); + System.assertNotEquals(qm4, qm6); + + System.assertEquals(qm5, qm5); + System.assertNotEquals(qm5, qm6); + + System.assertEquals(qm6, qm6); + } + + @isTest + private static void hashCodeReturnsExpectedResults() + { + //Given + fflib_MethodArgValues qm1 = new fflib_MethodArgValues(new List{ 'hello' }); + fflib_MethodArgValues qm2 = new fflib_MethodArgValues(new List{ 'hello' }); + fflib_MethodArgValues qm3 = new fflib_MethodArgValues(new List{ 'hi' }); + fflib_MethodArgValues qm4 = new fflib_MethodArgValues(new List{ 'hello', 'hello', 'hello' }); + fflib_MethodArgValues qm5 = new fflib_MethodArgValues(new List()); + fflib_MethodArgValues qm6 = new fflib_MethodArgValues(null); + + //When/thens + System.assertEquals(qm1.hashCode(), qm1.hashCode()); + System.assertEquals(qm1.hashCode(), qm2.hashCode()); + System.assertNotEquals(qm1.hashCode(), qm3.hashCode()); + System.assertNotEquals(qm1.hashCode(), qm4.hashCode()); + System.assertNotEquals(qm1.hashCode(), qm5.hashCode()); + System.assertNotEquals(qm1.hashCode(), qm6.hashCode()); + + System.assertEquals(qm2.hashCode(), qm2.hashCode()); + System.assertNotEquals(qm2.hashCode(), qm3.hashCode()); + System.assertNotEquals(qm2.hashCode(), qm4.hashCode()); + System.assertNotEquals(qm2.hashCode(), qm5.hashCode()); + System.assertNotEquals(qm2.hashCode(), qm6.hashCode()); + + System.assertEquals(qm3.hashCode(), qm3.hashCode()); + System.assertNotEquals(qm3.hashCode(), qm4.hashCode()); + System.assertNotEquals(qm3.hashCode(), qm5.hashCode()); + System.assertNotEquals(qm3.hashCode(), qm6.hashCode()); + + System.assertEquals(qm4.hashCode(), qm4.hashCode()); + System.assertNotEquals(qm4.hashCode(), qm5.hashCode()); + System.assertNotEquals(qm4.hashCode(), qm6.hashCode()); + + System.assertEquals(qm5.hashCode(), qm5.hashCode()); + System.assertNotEquals(qm5.hashCode(), qm6.hashCode()); + + System.assertEquals(qm6.hashCode(), qm6.hashCode()); + } +} \ No newline at end of file diff --git a/sfdx-source/apex-mocks/tests/fflib_MethodArgValuesTest.cls-meta.xml b/sfdx-source/apex-mocks/tests/fflib_MethodArgValuesTest.cls-meta.xml new file mode 100644 index 00000000000..08c4dbd5cfa --- /dev/null +++ b/sfdx-source/apex-mocks/tests/fflib_MethodArgValuesTest.cls-meta.xml @@ -0,0 +1,4 @@ + + + 52.0 + diff --git a/sfdx-source/apex-mocks/tests/fflib_MyList.cls b/sfdx-source/apex-mocks/tests/fflib_MyList.cls new file mode 100644 index 00000000000..772a44d04e4 --- /dev/null +++ b/sfdx-source/apex-mocks/tests/fflib_MyList.cls @@ -0,0 +1,67 @@ +/* + Copyright (c) 2014-2017 FinancialForce.com, inc. All rights reserved. + */ + +/** + * @nodoc + */ +@isTest +public with sharing class fflib_MyList implements IList +{ + public interface IList + { + void add(String value); + void add(String value1, String value2, String value3, String value4); + void addMore(String value); + void add(String[] value); // Test methods with the same name and number of params + String get(Integer index); + String get2(Integer index, String value); // This is just a method signature to allow me to test stubbing a method with multiple arguments + void clear(); + Boolean isEmpty(); + void set(Integer index, Object value); + } + + public void add(String[] value) + { + } + + public void add(String value) + { + } + + public void add(String value1, String value2, String value3, String value4) + { + } + + public void addMore(String value) + { + } + + public String get(Integer index) + { + return 'fred'; + } + + public void clear() + { + } + + public Boolean isEmpty() + { + return true; + } + + public void set(Integer index, Object value) + { + } + + public String get2(Integer index, String value) + { + return 'mary'; + } + + public static String getStubClassName() + { + return fflib_ApexMocks.extractTypeName(new fflib_ApexMocks().mock(fflib_MyList.class)); + } +} \ No newline at end of file diff --git a/sfdx-source/apex-mocks/tests/fflib_MyList.cls-meta.xml b/sfdx-source/apex-mocks/tests/fflib_MyList.cls-meta.xml new file mode 100644 index 00000000000..dd61d1f917e --- /dev/null +++ b/sfdx-source/apex-mocks/tests/fflib_MyList.cls-meta.xml @@ -0,0 +1,5 @@ + + + 52.0 + Active + diff --git a/sfdx-source/apex-mocks/tests/fflib_QualifiedMethodAndArgValuesTest.cls b/sfdx-source/apex-mocks/tests/fflib_QualifiedMethodAndArgValuesTest.cls new file mode 100644 index 00000000000..9e87b4877cb --- /dev/null +++ b/sfdx-source/apex-mocks/tests/fflib_QualifiedMethodAndArgValuesTest.cls @@ -0,0 +1,24 @@ +@isTest +public class fflib_QualifiedMethodAndArgValuesTest +{ + @isTest + private static void equalsReturnsExpectedResults() + { + fflib_QualifiedMethod qm1 = new fflib_QualifiedMethod('Type1', 'Method1', new List{ Integer.class } ); + fflib_MethodArgValues mav1 = new fflib_MethodArgValues(new List{ 'hello' }); + Object obj1 = 'hello'; + + fflib_QualifiedMethodAndArgValues qmaav = new fflib_QualifiedMethodAndArgValues(qm1, mav1, obj1); + + fflib_QualifiedMethod qm2 = qmaav.getQualifiedMethod(); + fflib_MethodArgValues mav2 = qmaav.getMethodArgValues(); + Object obj2 = qmaav.getMockInstance(); + String string1 = qmaav.toString(); + + System.assertEquals(qm1, qm2); + System.assertEquals(mav1, mav2); + System.assertEquals(obj1, obj2); + System.assertEquals('Type1.Method1(Integer) with args: [hello]', string1); + + } +} diff --git a/sfdx-source/apex-mocks/tests/fflib_QualifiedMethodAndArgValuesTest.cls-meta.xml b/sfdx-source/apex-mocks/tests/fflib_QualifiedMethodAndArgValuesTest.cls-meta.xml new file mode 100644 index 00000000000..dd61d1f917e --- /dev/null +++ b/sfdx-source/apex-mocks/tests/fflib_QualifiedMethodAndArgValuesTest.cls-meta.xml @@ -0,0 +1,5 @@ + + + 52.0 + Active + diff --git a/sfdx-source/apex-mocks/tests/fflib_QualifiedMethodTest.cls b/sfdx-source/apex-mocks/tests/fflib_QualifiedMethodTest.cls new file mode 100644 index 00000000000..48fa743f4d4 --- /dev/null +++ b/sfdx-source/apex-mocks/tests/fflib_QualifiedMethodTest.cls @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2016-2017 FinancialForce.com, inc. All rights reserved. + */ +@isTest +public with sharing class fflib_QualifiedMethodTest +{ + @isTest + private static void equalsReturnsExpectedResults() + { + //Given + fflib_QualifiedMethod qm1 = new fflib_QualifiedMethod('Type1', 'Method1', new List{ Integer.class } ); + fflib_QualifiedMethod qm2 = new fflib_QualifiedMethod('Type1', 'Method1', new List{ Integer.class } ); + fflib_QualifiedMethod qm3 = new fflib_QualifiedMethod('Type1', 'Method1', new List{ String.class } ); + fflib_QualifiedMethod qm4 = new fflib_QualifiedMethod('Type2', 'Method2', new List{ Integer.class, String.class, fflib_QualifiedMethodTest.class } ); + fflib_QualifiedMethod qm5 = new fflib_QualifiedMethod('', '', new List{} ); + fflib_QualifiedMethod qm6 = new fflib_QualifiedMethod(null, null, null ); + + //When/thens + System.assertEquals(qm1, qm1); + System.assertEquals(qm1, qm2); + System.assertNotEquals(qm1, qm3); + System.assertNotEquals(qm1, qm4); + System.assertNotEquals(qm1, qm5); + System.assertNotEquals(qm1, qm6); + + System.assertEquals(qm2, qm2); + System.assertNotEquals(qm2, qm3); + System.assertNotEquals(qm2, qm4); + System.assertNotEquals(qm2, qm5); + System.assertNotEquals(qm2, qm6); + + System.assertEquals(qm3, qm3); + System.assertNotEquals(qm3, qm4); + System.assertNotEquals(qm3, qm5); + System.assertNotEquals(qm3, qm6); + + System.assertEquals(qm4, qm4); + System.assertNotEquals(qm4, qm5); + System.assertNotEquals(qm4, qm6); + + System.assertEquals(qm5, qm5); + System.assertNotEquals(qm5, qm6); + + System.assertEquals(qm6, qm6); + } + + @isTest + private static void hashCodeReturnsExpectedResults() + { + //Given + fflib_QualifiedMethod qm1 = new fflib_QualifiedMethod('Type1', 'Method1', new List{ Integer.class } ); + fflib_QualifiedMethod qm2 = new fflib_QualifiedMethod('Type1', 'Method1', new List{ Integer.class } ); + fflib_QualifiedMethod qm3 = new fflib_QualifiedMethod('Type1', 'Method1', new List{ String.class } ); + fflib_QualifiedMethod qm4 = new fflib_QualifiedMethod('Type2', 'Method2', new List{ Integer.class, String.class, fflib_QualifiedMethodTest.class } ); + fflib_QualifiedMethod qm5 = new fflib_QualifiedMethod('', '', new List{} ); + fflib_QualifiedMethod qm6 = new fflib_QualifiedMethod(null, null, null ); + + //When/thens + System.assertEquals(qm1.hashCode(), qm1.hashCode()); + System.assertEquals(qm1.hashCode(), qm2.hashCode()); + System.assertNotEquals(qm1.hashCode(), qm3.hashCode()); + System.assertNotEquals(qm1.hashCode(), qm4.hashCode()); + System.assertNotEquals(qm1.hashCode(), qm5.hashCode()); + System.assertNotEquals(qm1.hashCode(), qm6.hashCode()); + + System.assertEquals(qm2.hashCode(), qm2.hashCode()); + System.assertNotEquals(qm2.hashCode(), qm3.hashCode()); + System.assertNotEquals(qm2.hashCode(), qm4.hashCode()); + System.assertNotEquals(qm2.hashCode(), qm5.hashCode()); + System.assertNotEquals(qm2.hashCode(), qm6.hashCode()); + + System.assertEquals(qm3.hashCode(), qm3.hashCode()); + System.assertNotEquals(qm3.hashCode(), qm4.hashCode()); + System.assertNotEquals(qm3.hashCode(), qm5.hashCode()); + System.assertNotEquals(qm3.hashCode(), qm6.hashCode()); + + System.assertEquals(qm4.hashCode(), qm4.hashCode()); + System.assertNotEquals(qm4.hashCode(), qm5.hashCode()); + System.assertNotEquals(qm4.hashCode(), qm6.hashCode()); + + System.assertEquals(qm5.hashCode(), qm5.hashCode()); + System.assertNotEquals(qm5.hashCode(), qm6.hashCode()); + + System.assertEquals(qm6.hashCode(), qm6.hashCode()); + } + + @isTest + public static void toStringReturnsExpectedResult() + { + System.assertEquals('MyClass.MyMethod(Integer)', new fflib_QualifiedMethod('MyClass', 'MyMethod', new List{ Integer.class }).toString()); + } + + @isTest + private static void equalsReturnsExpectedResultsForHasDependentMocks() + { + //Given + String instance = 'My object instance'; + String instance2 = 'My other object instance'; + fflib_QualifiedMethod qm1 = new fflib_QualifiedMethod('Type1', 'Method1', new List{ Integer.class } ); + fflib_QualifiedMethod qm2 = new fflib_QualifiedMethod('Type1', 'Method1', new List{ Integer.class }, instance); + fflib_QualifiedMethod qm3 = new fflib_QualifiedMethod('Type1', 'Method1', new List{ Integer.class }, instance); + fflib_QualifiedMethod qm4 = new fflib_QualifiedMethod('Type1', 'Method1', new List{ Integer.class }, instance2); + + //When/thens + fflib_ApexMocksConfig.HasIndependentMocks = false; + + System.assertEquals(qm1, qm2); + System.assertEquals(qm1, qm3); + System.assertEquals(qm1, qm4); + + fflib_ApexMocksConfig.HasIndependentMocks = true; + + System.assertNotEquals(qm1, qm2); + System.assertNotEquals(qm1, qm3); + System.assertNotEquals(qm1, qm4); + + System.assertEquals(qm2, qm3); + System.assertNotEquals(qm2, qm4); + + System.assertNotEquals(qm3, qm4); + } + + @isTest + private static void hashCodeReturnsExpectedResultsForHasDependentMocks() + { + //Given + String instance = 'My object instance'; + String instance2 = 'My other object instance'; + fflib_QualifiedMethod qm1 = new fflib_QualifiedMethod('Type1', 'Method1', new List{ Integer.class } ); + fflib_QualifiedMethod qm2 = new fflib_QualifiedMethod('Type1', 'Method1', new List{ Integer.class }, instance); + fflib_QualifiedMethod qm3 = new fflib_QualifiedMethod('Type1', 'Method1', new List{ Integer.class }, instance); + fflib_QualifiedMethod qm4 = new fflib_QualifiedMethod('Type1', 'Method1', new List{ Integer.class }, instance2); + + //When/thens + fflib_ApexMocksConfig.HasIndependentMocks = false; + + System.assertEquals(qm1.hashCode(), qm2.hashCode()); + System.assertEquals(qm1.hashCode(), qm3.hashCode()); + System.assertEquals(qm1.hashCode(), qm4.hashCode()); + + fflib_ApexMocksConfig.HasIndependentMocks = true; + + System.assertNotEquals(qm1.hashCode(), qm2.hashCode()); + System.assertNotEquals(qm1.hashCode(), qm3.hashCode()); + System.assertNotEquals(qm1.hashCode(), qm4.hashCode()); + + System.assertEquals(qm2.hashCode(), qm3.hashCode()); + System.assertNotEquals(qm2.hashCode(), qm4.hashCode()); + + System.assertNotEquals(qm3.hashCode(), qm4.hashCode()); + } +} \ No newline at end of file diff --git a/sfdx-source/apex-mocks/tests/fflib_QualifiedMethodTest.cls-meta.xml b/sfdx-source/apex-mocks/tests/fflib_QualifiedMethodTest.cls-meta.xml new file mode 100644 index 00000000000..08c4dbd5cfa --- /dev/null +++ b/sfdx-source/apex-mocks/tests/fflib_QualifiedMethodTest.cls-meta.xml @@ -0,0 +1,4 @@ + + + 52.0 + diff --git a/sfdx-source/apex-mocks/tests/fflib_SystemTest.cls b/sfdx-source/apex-mocks/tests/fflib_SystemTest.cls new file mode 100644 index 00000000000..678a5433216 --- /dev/null +++ b/sfdx-source/apex-mocks/tests/fflib_SystemTest.cls @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2017 FinancialForce.com, inc. All rights reserved. + */ +@IsTest +private class fflib_SystemTest +{ + @IsTest + private static void assertEquals_WithNoMatchers_ShouldThrowException() + { + try + { + fflib_System.assertEquals('Test String', 'Test String'); + System.assert(false, 'Expected exception'); + } + catch (fflib_ApexMocks.ApexMocksException e) + { + System.assertEquals('fflib_System.assertEquals expects you to register exactly 1 fflib_IMatcher (typically through the helpers in fflib_Match).', e.getMessage()); + } + } + + @IsTest + private static void assertEquals_WithTooManyMatchers_ShouldThrowException() + { + //Register matchers prematurely + fflib_Match.stringStartsWith('Test S'); + fflib_Match.stringEndsWith('t String'); + fflib_Match.stringIsNotBlank(); + + try + { + fflib_System.assertEquals(fflib_Match.stringStartsWith('Test S'), 'Test String'); + System.assert(false, 'Expected exception'); + } + catch (fflib_ApexMocks.ApexMocksException e) + { + System.assertEquals('fflib_System.assertEquals expects you to register exactly 1 fflib_IMatcher (typically through the helpers in fflib_Match).', e.getMessage()); + } + } + + @IsTest + private static void assertEquals_WithMismatch_ShouldThrowException() + { + try + { + fflib_System.assertEquals(fflib_Match.stringStartsWith('Test X'), 'Test String'); + System.assert(false, 'Expected exception'); + } + catch (fflib_ApexMocks.ApexMocksException e) + { + String expected = 'Actual: Test String'; + String actual = e.getMessage(); + System.assert(actual.contains(expected), 'Expected: ' + expected + ', Actual: ' + actual); + } + } + + @IsTest + private static void assertEquals_WithMatch_ShouldPass() + { + fflib_System.assertEquals(fflib_Match.stringStartsWith('Test S'), 'Test String'); + } + + @IsTest + private static void assertEquals_WithCombinedMatcher_ShouldPass() + { + fflib_System.assertEquals(fflib_Match.allOf( + fflib_Match.stringStartsWith('Test S'), + fflib_Match.stringEndsWith('t String'), + fflib_Match.stringIsNotBlank()) + , 'Test String'); + } + + @IsTest + private static void assertEquals_WithCustomMessage_WithNoMatchers_ShouldThrowException() + { + try + { + fflib_System.assertEquals('Test String', 'Test String', 'My Custom Message'); + System.assert(false, 'Expected exception'); + } + catch (fflib_ApexMocks.ApexMocksException e) + { + System.assertEquals('fflib_System.assertEquals expects you to register exactly 1 fflib_IMatcher (typically through the helpers in fflib_Match).', e.getMessage()); + } + } + + @IsTest + private static void assertEquals_WithCustomMessage_WithTooManyMatchers_ShouldThrowException() + { + //Register matchers prematurely + fflib_Match.stringStartsWith('Test S'); + fflib_Match.stringEndsWith('t String'); + fflib_Match.stringIsNotBlank(); + + try + { + fflib_System.assertEquals(fflib_Match.stringStartsWith('Test S'), 'Test String', 'My Custom Message'); + System.assert(false, 'Expected exception'); + } + catch (fflib_ApexMocks.ApexMocksException e) + { + System.assertEquals('fflib_System.assertEquals expects you to register exactly 1 fflib_IMatcher (typically through the helpers in fflib_Match).', e.getMessage()); + } + } + + @IsTest + private static void assertEquals_WithCustomMessage_WithMismatch_ShouldThrowException() + { + try + { + fflib_System.assertEquals(fflib_Match.stringStartsWith('Test X'), 'Test String', 'My Custom Message'); + System.assert(false, 'Expected exception'); + } + catch (fflib_ApexMocks.ApexMocksException e) + { + String expected = 'Actual: Test String -- My Custom Message'; + String actual = e.getMessage(); + System.assert(actual.contains(expected), 'Expected: ' + expected + ', Actual: ' + actual); + } + } + + @IsTest + private static void assertEquals_WithCustomMessage_WithMatch_ShouldPass() + { + fflib_System.assertEquals(fflib_Match.stringStartsWith('Test S'), 'Test String', 'My Custom Message'); + } + + @IsTest + private static void assertEquals_WithCustomMessage_WithCombinedMatcher_ShouldPass() + { + fflib_System.assertEquals(fflib_Match.allOf( + fflib_Match.stringStartsWith('Test S'), + fflib_Match.stringEndsWith('t String'), + fflib_Match.stringIsNotBlank()) + , 'Test String', 'My Custom Message'); + } +} \ No newline at end of file diff --git a/sfdx-source/apex-mocks/tests/fflib_SystemTest.cls-meta.xml b/sfdx-source/apex-mocks/tests/fflib_SystemTest.cls-meta.xml new file mode 100644 index 00000000000..dd61d1f917e --- /dev/null +++ b/sfdx-source/apex-mocks/tests/fflib_SystemTest.cls-meta.xml @@ -0,0 +1,5 @@ + + + 52.0 + Active + diff --git a/sfdx-source/apex-mocks/tests/mocks/fflib_Mocks.cls b/sfdx-source/apex-mocks/tests/mocks/fflib_Mocks.cls new file mode 100644 index 00000000000..20ed44f7f77 --- /dev/null +++ b/sfdx-source/apex-mocks/tests/mocks/fflib_Mocks.cls @@ -0,0 +1,60 @@ +/* Generated by apex-mocks-generator version 4.0.1 */ +@isTest +public class fflib_Mocks +{ + public class Mockfflib_MyList implements fflib_MyList.IList + { + private fflib_ApexMocks mocks; + + public Mockfflib_MyList(fflib_ApexMocks mocks) + { + this.mocks = mocks; + } + + public void add(String value) + { + mocks.mockVoidMethod(this, 'add', new List {System.Type.forName('String')}, new List {value}); + } + + public void add(String value1, String value2, String value3, String value4) + { + mocks.mockVoidMethod(this, 'add', new List {System.Type.forName('String'), System.Type.forName('String'), System.Type.forName('String'), System.Type.forName('String')}, new List {value1, value2, value3, value4}); + } + + public void addMore(String value) + { + mocks.mockVoidMethod(this, 'addMore', new List {System.Type.forName('String')}, new List {value}); + } + + public void add(String[] value) + { + mocks.mockVoidMethod(this, 'add', new List {System.Type.forName('String[]')}, new List {value}); + } + + public String get(Integer index) + { + return (String) mocks.mockNonVoidMethod(this, 'get', new List {System.Type.forName('Integer')}, new List {index}); + } + + public String get2(Integer index, String value) + { + return (String) mocks.mockNonVoidMethod(this, 'get2', new List {System.Type.forName('Integer'), System.Type.forName('String')}, new List {index, value}); + } + + public void clear() + { + mocks.mockVoidMethod(this, 'clear', new List {}, new List {}); + } + + public Boolean isEmpty() + { + return (Boolean) mocks.mockNonVoidMethod(this, 'isEmpty', new List {}, new List {}); + } + + public void set(Integer index, Object value) + { + mocks.mockVoidMethod(this, 'set', new List {System.Type.forName('Integer'), System.Type.forName('Object')}, new List {index, value}); + } + } + +} diff --git a/sfdx-source/apex-mocks/tests/mocks/fflib_Mocks.cls-meta.xml b/sfdx-source/apex-mocks/tests/mocks/fflib_Mocks.cls-meta.xml new file mode 100644 index 00000000000..dd61d1f917e --- /dev/null +++ b/sfdx-source/apex-mocks/tests/mocks/fflib_Mocks.cls-meta.xml @@ -0,0 +1,5 @@ + + + 52.0 + Active + diff --git a/sfdx-source/fflib-apex-extensions/default/classes/criteria/fflib_Comparator.cls b/sfdx-source/fflib-apex-extensions/default/classes/criteria/fflib_Comparator.cls new file mode 100644 index 00000000000..f37c7cd55da --- /dev/null +++ b/sfdx-source/fflib-apex-extensions/default/classes/criteria/fflib_Comparator.cls @@ -0,0 +1,186 @@ +/** + * File Name: fflib_Comparator + * @description Comparator for primitive values + * + * @author architect ir. Wilhelmus G.J. Velzeboer + * + * Copyright (c), W.G.J. Velzeboer, + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above author notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the author nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +public with sharing class fflib_Comparator +{ + public interface Comparable + { + Integer compare( Object otherValue ); + } + + public static Boolean compareTo(Object object1, fflib_Operator operator, Object object2) + { + Integer result = compare(object1, object2); + + if (operator == fflib_Operator.EQUALS && result == 0) + return true; + else if (operator == fflib_Operator.NOT_EQUALS && result != 0) + return true; + else if (operator == fflib_Operator.LESS_THAN && result == -1) + return true; + else if (operator == fflib_Operator.LESS_THAN_OR_EQUAL_TO && result != 1) + return true; + else if (operator == fflib_Operator.GREATER_THAN && result == 1) + return true; + else if (operator == fflib_Operator.GREATER_THAN_OR_EQUAL_TO && result != -1) + return true; + else + return false; + } + + public static Integer compare(Object object1, Object object2) + { + if ( object2 instanceOf Comparable ) { + return ((Comparable)object2).compare( object1 ); + } + + if (object1 == null && object2 == null) + return 0; + else if (object1 == null) + return -1; + else if (object2 == null) + return 1; + else if (object1 instanceof Boolean && object2 instanceof Boolean) + return compare((Boolean) object1, (Boolean) object2); + else if (object1 instanceof Date && object2 instanceof Date) + return compare((Date) object1, (Date) object2); + else if (object1 instanceof Datetime && object2 instanceof Datetime) + return compare((Datetime) object1, (Datetime) object2); + else if (object1 instanceof Integer && object2 instanceof Integer) + return compare((Integer) object1, (Integer) object2); + else if (object1 instanceof Long && object2 instanceof Long) + return compare((Long) object1, (Long) object2); + else if (object1 instanceof Double && object2 instanceof Double) + return compare((Double) object1, (Double) object2); + else if (object1 instanceof Time && object2 instanceof Time) + return compare((Time) object1, (Time) object2); + else if (object1 instanceof String && object2 instanceof String) + return compare((String) object1, (String) object2); + else + throw new IllegalArgumentException( + 'Both arguments must be type Boolean, Date, Datetime, Decimal, Double, ID, Integer, Long, Time, or String'); + } + + public static Integer compare(Boolean b1, Boolean b2) + { + if (!b1 && b2) return -1; + else if (b1 == b2) return 0; + else return 1; + } + + public static Integer compare(Date d1, Date d2) + { + if (d1 < d2) return -1; + else if (d1 == d2) return 0; + else return 1; + } + + public static Integer compare(Datetime d1, Datetime d2) + { + if (d1 < d2) return -1; + else if (d1 == d2) return 0; + else return 1; + } + + public static Integer compare(Double d1, Double d2) + { + if (d1 < d2) return -1; + else if (d1 == d2) return 0; + else return 1; + } + + public static Integer compare(Integer i1, Integer i2) + { + if (i1 < i2) return -1; + else if (i1 == i2) return 0; + else return 1; + } + + public static Integer compare(Long l1, Long l2) + { + if (l1 < l2) return -1; + else if (l1 == l2) return 0; + else return 1; + } + + public static Integer compare(String s1, String s2) + { + if (s1 < s2) return -1; + else if (s1 == s2) return 0; + else return 1; + } + + public static Integer compare(Time t1, Time t2) + { + return compare('' + t1, '' + t2); + } + + + /** + * @description check if a Set contains a value + * @author architect ir. Wim G.J. Velzeboer + * @param object1 - Object - a Set of objects + * @param object2 - Object - the object to check if its a member of the Set + * @return Boolean - TRUE if the object is a member of the Set + */ + public static Boolean contains(Object object1, Object object2) + { + if (object1 == null && object2 == null) + return true; + else if (object1 == null) + return false; + else if (object2 == null) + return false; + else if (object1 instanceof Set && object2 instanceof Date) + return ((Set) object1).contains((Date) object2); + else if (object1 instanceof Set && object2 instanceof Datetime) + return ((Set) object1).contains((Datetime) object2); + else if (object1 instanceof Set && object2 instanceof Double) + return ((Set) object1).contains((Double) object2); + else if (object1 instanceof Set && object2 instanceof Id) + return ((Set) object1).contains((Id) object2); + else if (object1 instanceof Set && object2 instanceof Integer) + return ((Set) object1).contains((Integer) object2); + else if (object1 instanceof Set && object2 instanceof Long) + return ((Set) object1).contains((Long) object2); + else if (object1 instanceof Set) + return ((Set) object1).contains((Object) object2); + else if (object1 instanceof Set