diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000000..d65816bdc15 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,6 @@ +root = true + +# Exists only to define tab spacing in github pull request rendering +[*] +indent_style = tab +indent_size = 4 \ No newline at end of file diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 00000000000..5f7b681a362 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,16 @@ +**/lwc/**/*.css +**/lwc/**/*.html +**/lwc/**/*.json +**/lwc/**/*.svg +**/lwc/**/*.xml +**/aura/**/*.auradoc +**/aura/**/*.cmp +**/aura/**/*.css +**/aura/**/*.design +**/aura/**/*.evt +**/aura/**/*.json +**/aura/**/*.svg +**/aura/**/*.tokens +**/aura/**/*.xml +**/aura/**/*.app +.sfdx diff --git a/.forceignore b/.forceignore new file mode 100755 index 00000000000..7b5b5a71fd2 --- /dev/null +++ b/.forceignore @@ -0,0 +1,12 @@ +# List files or directories below to ignore them when running force:source:push, force:source:pull, and force:source:status +# More information: https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_exclude_source.htm +# + +package.xml + +# LWC configuration files +**/jsconfig.json +**/.eslintrc.json + +# LWC Jest +**/__tests__/** \ No newline at end of file diff --git a/.github/workflows/create-org-and-deploy.yaml b/.github/workflows/create-org-and-deploy.yaml new file mode 100644 index 00000000000..36353591e48 --- /dev/null +++ b/.github/workflows/create-org-and-deploy.yaml @@ -0,0 +1,87 @@ +name: Deploy and Run all Unit Tests +on: + push: + branches: + - '**/*' + - main + pull_request: + types: [ opened ] + branches: + - '**/*' + - main +jobs: + create-org-and-deploy: + runs-on: ubuntu-latest + env: + DEVHUB_ORG_ALIAS: OrtooISV + ORG_ALIAS_PREFIX: 'FTST' + steps: + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v2 + + - name: Salesforce SFDX CLI Action + uses: sfdx-actions/setup-sfdx@v1 + + # Update ./config/project-scratch-def.json + + - name: Update project-scratch-def.json orgName + uses: jossef/action-set-json-field@6e6d7e639f24b3955ef682815317b5613ac6ca12 + with: + file: ./config/project-scratch-def.json + field: orgName + value: "${{env.ORG_ALIAS_PREFIX}}${{github.run_number}}" + + - name: Update project-scratch-def.json description + uses: jossef/action-set-json-field@6e6d7e639f24b3955ef682815317b5613ac6ca12 + with: + file: ./config/project-scratch-def.json + field: description + value: "${{ github.repository }} , Org Alias ${{env.ORG_ALIAS_PREFIX}}${{github.run_number}}. Created by ${{ github.actor }}" + + - name: Show project-scratch-def.json + run: 'cat ./config/project-scratch-def.json' + + # sfdx-project.json file should be ok as is + + - name: Show sfdx-project.json + run: 'cat ./sfdx-project.json' + + # Dev Hub needed to create scratch org + + - name: Create DevHub Auth File + run: echo "${{ secrets.AUTH_SECRET_ISV }}" > devhub_auth_file.txt + + - name: Authorise Dev Hub Org + run: sfdx auth:sfdxurl:store -f devhub_auth_file.txt -d -a ${{ env.DEVHUB_ORG_ALIAS }} + + # Create Scratch Org + + - name: Run script CreateScratchOrgWithNamespaceOnlyHeadless.sh + run: ./scripts/std_batch/CreateScratchOrgWithNamespaceOnlyHeadless.sh "${{env.ORG_ALIAS_PREFIX}}${{github.run_number}}" + shell: sh + + # Push code to org + + - name: Deploy framework to Org + run: sfdx force:source:deploy -p framework --targetusername "${{env.ORG_ALIAS_PREFIX}}${{github.run_number}}" + + # Run Apex Unit Tests + + - name: Run Apex Unit Tests + run: sfdx force:apex:test:run -r human -u "${{env.ORG_ALIAS_PREFIX}}${{github.run_number}}" --wait 20 | grep -v ' Pass '; test ${PIPESTATUS[0]} -eq 0 + + # Prepare Jest Modules + + - name: Prepare Jest Modules + run: npm install + + # Run Jest Unit Tests + + - name: Run Jest Unit Tests + run: npm test + + # Delete Scratch Org + + - name: Delete Scratch Org + if: ${{ always() }} + run: sfdx force:org:delete --noprompt --targetusername "${{env.ORG_ALIAS_PREFIX}}${{github.run_number}}" \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000000..fbd1fe4e174 --- /dev/null +++ b/.gitignore @@ -0,0 +1,45 @@ +# This file is used for Git repositories to specify intentionally untracked files that Git should ignore. +# If you are not using git, you can delete this file. For more information see: https://git-scm.com/docs/gitignore +# For useful gitignore templates see: https://github.com/github/gitignore + +# Salesforce cache +.sf/ +.sfdx/ +.localdevserver/ +deploy-options.json + +# LWC VSCode autocomplete +**/lwc/jsconfig.json + +# LWC Jest coverage reports +coverage/ + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Dependency directories +node_modules/ + +# NPM package lock +package-lock.json + +# Eslint cache +.eslintcache + +# MacOS system files +.DS_Store + +# Windows system files +Thumbs.db +ehthumbs.db +[Dd]esktop.ini +$RECYCLE.BIN/ + +# Local environment variables +.env +.pmdCache +test-results diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 00000000000..6700f512821 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,2 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" diff --git a/.prettierignore b/.prettierignore new file mode 100755 index 00000000000..f3720b23722 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,10 @@ +# List files or directories below to ignore them when running prettier +# More information: https://prettier.io/docs/en/ignore.html +# + +**/staticresources/** +.localdevserver +.sfdx +.vscode + +coverage/ \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100755 index 00000000000..15683b6928a --- /dev/null +++ b/.prettierrc @@ -0,0 +1,13 @@ +{ + "trailingComma": "none", + "overrides": [ + { + "files": "**/lwc/**/*.html", + "options": { "parser": "lwc" } + }, + { + "files": "*.{cmp,page,component}", + "options": { "parser": "html" } + } + ] +} diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 00000000000..7e6cb105e63 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "salesforce.salesforcedx-vscode", + "redhat.vscode-xml", + "dbaeumer.vscode-eslint", + "esbenp.prettier-vscode", + "financialforce.lana" + ] +} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000000..e07e3917374 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,16 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Launch Apex Replay Debugger", + "type": "apex-replay", + "request": "launch", + "logFile": "${command:AskForLogFileName}", + "stopOnEntry": true, + "trace": true + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000000..76decfbdf3d --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "search.exclude": { + "**/node_modules": true, + "**/bower_components": true, + "**/.sfdx": true + } +} diff --git a/README.md b/README.md new file mode 100644 index 00000000000..afcda4a667c --- /dev/null +++ b/README.md @@ -0,0 +1,18 @@ +# Salesforce DX Project: Next Steps + +Now that you’ve created a Salesforce DX project, what’s next? Here are some documentation resources to get you started. + +## How Do You Plan to Deploy Your Changes? + +Do you want to deploy a set of changes, or create a self-contained application? Choose a [development model](https://developer.salesforce.com/tools/vscode/en/user-guide/development-models). + +## Configure Your Salesforce DX Project + +The `sfdx-project.json` file contains useful configuration information for your project. See [Salesforce DX Project Configuration](https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_ws_config.htm) in the _Salesforce DX Developer Guide_ for details about this file. + +## Read All About It + +- [Salesforce Extensions Documentation](https://developer.salesforce.com/tools/vscode/) +- [Salesforce CLI Setup Guide](https://developer.salesforce.com/docs/atlas.en-us.sfdx_setup.meta/sfdx_setup/sfdx_setup_intro.htm) +- [Salesforce DX Developer Guide](https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_intro.htm) +- [Salesforce CLI Command Reference](https://developer.salesforce.com/docs/atlas.en-us.sfdx_cli_reference.meta/sfdx_cli_reference/cli_reference.htm) diff --git a/TODO.txt b/TODO.txt new file mode 100644 index 00000000000..da8c2196583 --- /dev/null +++ b/TODO.txt @@ -0,0 +1,105 @@ +Framework Catch-up +* Start-up performance might need looking at - looks like it has a linear startup time based on the number of services / domains / etc configured. + +Licenses that are needed with the source code and binary: +* fflib - https://github.com/apex-enterprise-patterns/fflib-apex-common/blob/master/LICENSE +* fflib apex extensions - https://github.com/wimvelzeboer/fflib-apex-extensions/blob/main/LICENSE +* Amoss - https://github.com/bobalicious/amoss/blob/main/LICENSE +* SObject Fabricator - https://github.com/bobalicious/SObjectFabricator/blob/master/LICENSE + +* Move all the Jest LWc tests over to create the component in the before + + +Documentation - using lazy loader for service instantiation + + + +Look at the use of 'MockDatabase' in fflib +Look at: + SobjectUtils.getSobjectName + + +* LWCs + * Standards for: + * Filter and Results Page + * Apex format + * Inner Class for the search parameters + * Offset + * Order By - single column + * Order By Direction + + * Edit Forms / Validation + * Edit Forms with Child Records + +Add to documentation + * Query builder - add it to the architectural diagram - after more investigation + + DmlServices documentation: + * Show how to tie the validator into an action + * Note that the actions should + * Actions can reference Validators + * Cannot reference SOQL - they are excuted in a loop + * Do not do domain logic in them + + * Using the Mock Registarar + +From Utilities, things that may be useful: + * getReferenceObjectAPIName + * getObjName - get the object name from an Id + * getLabel / getObjectLabel - get the label for an sobject + * getFieldLabel + * delimitedStringToSet and reverse + * escaping single quotes - in both directions? + * unitsBetweenDateTime + * emailAddressIsValid / emailAddressListIsValid + * sObjectIsCustom / sObjectIsCustomfromAPIName + * IsfieldFilterable + * isFieldCustom + * idIsValid + * getCrossObjectAPIName + * objectFieldExist + * sortSelectOptions - complete re-write + +Write tests for the SOQL generation in the criteria library + +Amoss_Asserts.assertContains improvement into the OS lib +New Amoss_Asserts into the OS Lib + +Bring in Stack from Q-assign? +Review Utilities in q-assign - is anything else useful? + + + +* Document a few of the oddities on the Assignment Group and Action that need to be resolved. + + +Bad Smells - strung out calls to describe methods - put them into SobjectUtils + + +* Build some example unit tests... +* Criteria extension + * Build a simple SearchFilter SOQL generator / Selector + +* Question: How do we generically tie in the QueryHandler? Should we? How often is this currently used? +* Question: Standard - never instantiate a Domain within a loop + +* Question: How do we handle Constants - where are they defined? +* Question: How do we handle Exceptions: + * Do we handle individual types of exception in the Service? + * Do we pass any of the domain exceptions back + +* Question: Multiple Apps... maybe + +* Question: Can we generate a class diagram from a body of code +* Question: All or nothing on fflib + +* Question: Do you need Heap size management rules - + +* Produce test-case for lack of clickthrough and raise bug on VSCode + +* How do we handle constants? + +* Dynamic building of the Unit Of Work for processing data + +Notes: +* Services should always be designed by seniors - at least which services exist \ No newline at end of file diff --git a/config/project-scratch-def.json b/config/project-scratch-def.json new file mode 100644 index 00000000000..5d27a9cdfb1 --- /dev/null +++ b/config/project-scratch-def.json @@ -0,0 +1,13 @@ +{ + "orgName": "robertbaillie company", + "edition": "Developer", + "features": ["EnableSetPasswordInApi"], + "settings": { + "lightningExperienceSettings": { + "enableS1DesktopEnabled": true + }, + "mobileSettings": { + "enableS1EncryptedStoragePref2": false + } + } +} diff --git a/force-app/main/default/aura/.eslintrc.json b/force-app/main/default/aura/.eslintrc.json new file mode 100644 index 00000000000..226a5a27b8a --- /dev/null +++ b/force-app/main/default/aura/.eslintrc.json @@ -0,0 +1,8 @@ +{ + "plugins": ["@salesforce/eslint-plugin-aura"], + "extends": ["plugin:@salesforce/eslint-plugin-aura/recommended"], + "rules": { + "vars-on-top": "off", + "no-unused-expressions": "off" + } +} diff --git a/force-app/main/default/lwc/.eslintrc.json b/force-app/main/default/lwc/.eslintrc.json new file mode 100644 index 00000000000..49ca97d4beb --- /dev/null +++ b/force-app/main/default/lwc/.eslintrc.json @@ -0,0 +1,11 @@ +{ + "extends": ["@salesforce/eslint-config-lwc/recommended"], + "overrides": [ + { + "files": ["*.test.js"], + "rules": { + "@lwc/lwc/no-unexpected-wire-adapter-usages": "off" + } + } + ] +} diff --git a/framework/default/amoss_main/default/classes/Amoss_Asserts.cls b/framework/default/amoss_main/default/classes/Amoss_Asserts.cls new file mode 100644 index 00000000000..7fd0bfa8451 --- /dev/null +++ b/framework/default/amoss_main/default/classes/Amoss_Asserts.cls @@ -0,0 +1,209 @@ +/* +MIT License + +Copyright (c) 2020 Robert Baillie + +https://github.com/bobalicious/amoss + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +/** +* Provides assertion methods for Amoss_Instance to use. Primarily so that the assertion mechanism +* can be overridden in the unit tests - otherwise we would not be able to test when failure assertions +* are issued. +* +* However, also provides 'assertContains' and 'assertDoesNotContain', which are useful additions +* to the assertion suite. +* +*/ +@isTest +public with sharing class Amoss_Asserts { + + /** + * Amoss specific version of assertEquals, which is intended to only be used by Amoss itself. + * + * Has the added capability of explictly checking for matching datatypes before issuing the equality assertion. + * This helps in ensuring that datatype mis-match messages to the console are clear - rather then simply + * throwing an exception, as would be the case without this check. + * + * Is a non-static method so that it can be mocked in the test for Amoss_Instance + */ + public void assertEquals( Object expectedObject, Object actualObject, String assertionMessage ) { + + if ( expectedObject != null && actualObject != null ) { + System.assertEquals( getType( expectedObject ), getType( actualObject ), assertionMessage + ' - Types do not match' ); + } + + System.assertEquals( expectedObject, actualObject, assertionMessage ); + } + + /** + * Amoss specific version of assert, which is intended to only be used by Amoss itself. + * + * Is a non-static method so that it can be mocked in the test for Amoss_Instance + */ + public void assert( Boolean result, String assertionMessage ) { + System.assert( result, assertionMessage ); + } + + /** + * Assert that the actualString contains the expectedString + * + * Provided as this is a common assertion case, and usually requires the building + * of an assertion message for clarity when the assertion fails. + * + * @param String - The string to search for (expected) + * @param Object - The string to search within (actual) + * @param String - The assertion message + */ + public static void assertContains( String expectedString, Object actualString, String assertionMessage ) { + if ( actualString == null && expectedString != null ) { + System.assert(false, assertionMessage + ' (looking for: ' + expectedString + ', got: ' + actualString + ')' ); + } + System.assert( ((String)actualString).contains( expectedString ), assertionMessage + ' (looking for: ' + expectedString + ', got: ' + actualString + ')' ); + } + + /** + * Assert that the actualString does not contain the expectedString + * + * Provided as this is a common assertion case, and usually requires the building + * of an assertion message for clarity when the assertion fails. + * + * @param String - The string to search for (expected) + * @param String - The string to search within (actual) + * @param String - The assertion message + */ + public static void assertDoesNotContain( String expectedString, String actualString, String assertionMessage ) { + System.assert( ! actualString.contains( expectedString ), assertionMessage + ' (making sure does not contain: ' + expectedString + ', got: ' + actualString + ')' ); + } + + /** + * Assert that the actualString ends with the expectedString + * + * Provided as this is a common assertion case, and usually requires the building + * of an assertion message for clarity when the assertion fails. + * + * @param String - The string to search for (expected) + * @param Object - The string to search within (actual) + * @param String - The assertion message + */ + public static void assertEndsWith( String expectedString, Object actualString, String assertionMessage ) { + if ( actualString == null && expectedString != null ) { + System.assert(false, assertionMessage + ' (looking for ending with: ' + expectedString + ', got: ' + actualString + ')' ); + } + System.assert( ((String)actualString).endsWith( expectedString ), assertionMessage + ' (looking for ending with: ' + expectedString + ', got: ' + actualString + ')' ); + } + + /** + * Assert that the actualString starts with with the expectedString + * + * Provided as this is a common assertion case, and usually requires the building + * of an assertion message for clarity when the assertion fails. + * + * @param String - The string to search for (expected) + * @param Object - The string to search within (actual) + * @param String - The assertion message + */ + public static void assertStartsWith( String expectedString, Object actualString, String assertionMessage ) { + if ( actualString == null && expectedString != null ) { + System.assert(false, assertionMessage + ' (looking for starting with: ' + expectedString + ', got: ' + actualString + ')' ); + } + System.assert( ((String)actualString).startsWith( expectedString ), assertionMessage + ' (looking for starting with: ' + expectedString + ', got: ' + actualString + ')' ); + } + + /** + * A simple 'getType' implementation that helps with checking that expected and actual + * are of the same datatype. + * + * Is not exhaustive and is bound by Salesforce limits, but will reduce the number + * of exceptions thrown by 'assertEquals' due to mismatching types. + * + * @param Object - The object to return the type of + * @return String - The type of the passed in object + */ + public static string getType( Object o ) { + if( o instanceof SObject ) { + try { + return String.valueOf(((SObject)o).getSObjectType().getDescribe().getName()); + } catch ( Exception e ) {} // not actually an Sobject: There's a bug in Apex where List will return true for "instanceOf Sobject" when it shouldn't + } + if( o instanceof Boolean ) { + return 'Boolean'; + } + if( o instanceof Id ) { + return 'Id'; + } + if( o instanceof String ) { + return 'String'; + } + if( o instanceof Blob ) { + return 'Blob'; + } + if( o instanceof Date ) { + return 'Date'; + } + if( o instanceof Datetime ) { + return 'DateTime'; + } + if( o instanceof Time ) { + return 'Time'; + } + if( o instanceof String ) { + return 'String'; + } + if( o instanceof Integer ) { + return 'Integer'; + } + if( o instanceof Long ) { + return 'Long'; + } + if( o instanceof Double ) { + return 'Decimal / Double'; // Cannot reliably distinguish between a decimal and a double + } + if( o instanceof Decimal ) { + return 'Decimal / Double'; + } + if( o == null ) return 'Unknown (null)'; // If it's null, we can't tell what it is + + // Having covered the primitives and special cases, we now try to cast the object + // into something we know it can't be in order to get what Apex thinks it is from + // the Exception. + // Is a bit of a hack, but the test for this method should check that it continues + // to work as Apex changes behaviour. + try { + DateTime impossibleCast = (DateTime)o; + } catch ( Exception e ) { + + Matcher dataTypeMatcher = pattern.compile( 'Invalid conversion from runtime type (.*) to Datetime' ).matcher( e.getMessage() ); + if ( dataTypeMatcher.matches() ) { + String dataType = dataTypeMatcher.group( 1 ); + // Don't like 'ANY' as a response - this exception is the only place you ever see it, + // so we replace it with Object - but since an object type could have 'ANY' in it, + // we have to only do it for places where it can appear and we are certain it's + // Salesforce's doing, not the developer's + return dataType.replace( ',ANY>', ',Object>' ).replace( '', '' ); + } + } + + // I can't think of any reason why would reach here, + // but just in case... + return 'Object'; + } +} \ No newline at end of file diff --git a/framework/default/amoss_main/default/classes/Amoss_Asserts.cls-meta.xml b/framework/default/amoss_main/default/classes/Amoss_Asserts.cls-meta.xml new file mode 100644 index 00000000000..dd61d1f917e --- /dev/null +++ b/framework/default/amoss_main/default/classes/Amoss_Asserts.cls-meta.xml @@ -0,0 +1,5 @@ + + + 52.0 + Active + diff --git a/framework/default/amoss_main/default/classes/Amoss_HttpCalloutMock.cls b/framework/default/amoss_main/default/classes/Amoss_HttpCalloutMock.cls new file mode 100644 index 00000000000..89b3742e498 --- /dev/null +++ b/framework/default/amoss_main/default/classes/Amoss_HttpCalloutMock.cls @@ -0,0 +1,6 @@ +@isTest +public with sharing class Amoss_HttpCalloutMock implements HttpCalloutMock { + public HttpResponse respond( HttpRequest request ) { + return null; + } +} \ No newline at end of file diff --git a/framework/default/amoss_main/default/classes/Amoss_HttpCalloutMock.cls-meta.xml b/framework/default/amoss_main/default/classes/Amoss_HttpCalloutMock.cls-meta.xml new file mode 100644 index 00000000000..dd61d1f917e --- /dev/null +++ b/framework/default/amoss_main/default/classes/Amoss_HttpCalloutMock.cls-meta.xml @@ -0,0 +1,5 @@ + + + 52.0 + Active + diff --git a/framework/default/amoss_main/default/classes/Amoss_HttpRequestVerifier.cls b/framework/default/amoss_main/default/classes/Amoss_HttpRequestVerifier.cls new file mode 100644 index 00000000000..ea30eecb718 --- /dev/null +++ b/framework/default/amoss_main/default/classes/Amoss_HttpRequestVerifier.cls @@ -0,0 +1,69 @@ +/* +MIT License + +Copyright (c) 2020 Robert Baillie + +https://github.com/bobalicious/amoss + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +/** +* Public interface for the specification of a mechanism for checking the value of an HttpRequest. +* +* Can be used to extend the functionality of the HttpCalloutMock Amoss framework and allow for complex parameter checking +* to take place that is specific to a particular system's implementation. +* +* However, it is generally not required to create custom verifiers. +*/ +public interface Amoss_HttpRequestVerifier { + + /** + * Describes the verification that this object to configured to make in a way that will be easy to + * interpret when reported in a failed assertion message. + * + * @return String - A clear description of the value's verification + */ + String toString(); + + /** + * Should check that the given value (the parameter, or fragment of the parameter) passes verification, reporting any + * failures by throwing an exception of the following types: + * * Amoss_Instance.Amoss_AssertionFailureException + * * Amoss_Instance.Amoss_EqualsAssertionFailureException + * + * In both cases, setAssertionMessage should be called to clearly define the failure. + * + * When using Amoss_EqualsAssertionFailureException, setExpected and setActual should also be set, with the values + * being relevant within the context of the stated assertionMessage. + * + * If other verifiers are used within a custom verifier, any Amoss_AssertionFailureExceptions can be caught and + * have context added to the failure by calling addContextToMessage against the exception before re-throwing. + * + * Care should be taken to ensure that no exceptions other than Amoss_AssertionFailureExceptions and its subclasses are + * thrown. This ensures that failures are clearly reported to the user. + * + * In addition, no calls to System.assert or its variations should be made directly in this method as it is also called + * without the intention of reporting assertion failures to the user, and unexpected behaviours may result, particularly when using + * the 'when' and 'allows' syntax. + * + * @param Object - The value to verify + */ + void verify( HttpRequest value ); +} diff --git a/framework/default/amoss_main/default/classes/Amoss_HttpRequestVerifier.cls-meta.xml b/framework/default/amoss_main/default/classes/Amoss_HttpRequestVerifier.cls-meta.xml new file mode 100644 index 00000000000..dd61d1f917e --- /dev/null +++ b/framework/default/amoss_main/default/classes/Amoss_HttpRequestVerifier.cls-meta.xml @@ -0,0 +1,5 @@ + + + 52.0 + Active + diff --git a/framework/default/amoss_main/default/classes/Amoss_Instance.cls b/framework/default/amoss_main/default/classes/Amoss_Instance.cls new file mode 100644 index 00000000000..a53482eb6bd --- /dev/null +++ b/framework/default/amoss_main/default/classes/Amoss_Instance.cls @@ -0,0 +1,5707 @@ +/* +MIT License + +Copyright (c) 2020 Robert Baillie + +https://github.com/bobalicious/amoss + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +/** +* The entry point for all creations of Amoss Test Doubles, Spies and Mocks. +* +* Is instantiated with a class Type, then configured with combinations of 'expect', 'when' and 'allows'. +* +* Once configured, is used to 'getDouble', which can then be used in place of the class being doubled. +* +* On completion, a call-log can be spied for parameter values, and mock the expected call stack can be +* checked using 'verify'. +* +* Can be used to create a test double for any class that can be stubbed using the standard Salesforce +* 'StubProvider' interface (which this class implements), and has the same limitations as that functionality. +* +*/ +@isTest +public without sharing class Amoss_Instance implements StubProvider { //NOPMD - Is a single class library, hence the requirement for a significant number of public methods + + public class Amoss_ClassTypeNotDefinedException extends Exception {} + public class Amoss_ClassTypeAlreadyDefinedException extends Exception {} + public class Amoss_CallLogException extends Exception {} + public class Amoss_ExpectsNoCallsAndCallsConfiguredException extends Exception {} + public class Amoss_ExpectedObjectCannotBeNullException extends Exception {} + public class Amoss_NotAnHttpCalloutMockException extends Exception {} + + private Amoss_Expectations expectations = new Amoss_Expectations(); + private Amoss_Expectations whens = new Amoss_Expectations(); + private Amoss_CallLog callLog = new Amoss_CallLog(); + + private Object defaultMethodReturn; + private Boolean isFluent = false; + + private Type classType; + + private Amoss_Asserts assertionMechanism = new Amoss_Asserts(); + + private Boolean allowsAnyCall = true; + private Boolean allowsAnyCallExplicitlyDefined = false; + private Boolean expectsNoCalls = false; + + private String moreCallsThanExpectedMessage = '{0}.{1} was called more times than was expected'; + private String expectedNoCallsMessage = '{0} did not expect any methods to be called'; + private String expectedEmptyCallStackMessage = 'Expected call stack for {0} should be empty, and it is not'; + + public static Amoss_ValueVerifierBuilder valueVerifierBuilder = new Amoss_ValueVerifierBuilder(); + + public enum Value { NOT_NULL, ANY_VALUE, NOT_EMPTY } + + public Amoss_Instance() { + } + + public Amoss_Instance( Type classType ) { + this.classType = classType; + } + + private Amoss_Instance( Amoss_Instance toClone ) { + + this.latestGeneratedDouble = null; + + this.expectations = toClone.expectations.createClone().setAmossInstance( this ); + this.whens = toClone.whens.createClone().setAmossInstance( this ); + + this.classType = toClone.classType; + this.allowsAnyCall = toClone.allowsAnyCall; + this.allowsAnyCallExplicitlyDefined = toClone.allowsAnyCallExplicitlyDefined; + this.expectsNoCalls = toClone.expectsNoCalls; + this.defaultMethodReturn = toClone.defaultMethodReturn; + + if ( toClone.isFluent ) { + this.isFluent(); + } + } + + /** + * Allows the setting of the class that this Amoss Instance should generate test doubles for.s + * + * In general, the constructor that takes a class Type should be used over this, unless you particularly require + * the need to defer the setting of the class type (e.g. you have a templated instance that can be used for different types) + * + * @param Type - The class type that this instance should generate test doubles for. + * @return Amoss_Instance - The instance to use in your test + */ + public Amoss_Instance setClass( Type classType ) { + if ( this.classType != null ) { + throw new Amoss_ClassTypeAlreadyDefinedException( 'The class type on this Amoss_Instance has already been set to ' + this.classType ); + } + this.classType = classType; + return this; + } + + /** + * Returns the instance of the class that you use in the application under test. + * + * @return Object - The instance to use in your test + */ + private Object latestGeneratedDouble; + public Object getDouble() { + if ( latestGeneratedDouble == null ) { + return generateDouble(); + } + return latestGeneratedDouble; + } + + /** + * States that this controller is an HttpCalloutMock, registering it as the mock callout and + * switching the grammar to that required for specifying expected callouts and their responses. + * + * Example usage: + * new Amoss_Instance() + * .isACalloutMock() + * .when() + * .method( 'GET' ) + * .respondsWith() + * .body( '{some JSON}' ) + */ + public Amoss_CalloutMockDefiner isACalloutMock() { + if ( this.classType == null ) { + setClass( Amoss_HttpCalloutMock.class ); + setMoreCallsThanExpectedMessage( 'More HTTP Callouts were made than expected' ); + setExpectedNoCallsMessage( 'Did not expect any HTTP Callouts to be made' ); + setExpectedEmptyCallStackMessage( 'Expected more HTTP Callouts to be made' ); + byDefaultMethodsReturn( new HttpResponse() ); + } + return new Amoss_CalloutMockDefiner( this ); + } + + /** + * Generates a new instance of the class that you use in the application under tests. + * Used to generate multiple objects that all behave in the same way when you do not require + * access to the conroller. + * + * Will clone the Amoss_Instance, although will not provide access to it. + * + * Is generally not required, and getDouble should be used in most cases. + * + * @return Object - The instance to use in your test + */ + public Object generateDouble() { + if ( classType == null ) { + throw new Amoss_ClassTypeNotDefinedException( 'The class type on this Amoss_Instance has not been defined. Either set it on construction, or by calling setClass' ); + } + latestGeneratedDouble = Test.createStub( classType, this ); + return latestGeneratedDouble; + } + + /** + * Creates a clone of this instance, allowing multiple versions of the object being doubled to + * be created, along with access to the controller (as opposed to the behaviour of generateNewDouble) + * + * Is generally not required. + * + * @return Amoss_Instance - The new instance of the controller + */ + public Amoss_Instance createClone() { + return new Amoss_Instance( this ); + } + + /** + * States that the next method definition is expected to be called in the order it appears. + * + * If a method is subsequently called out of order, will fail the test. + * + * Calling 'verify' will check that all expectations have been met, failing the test if not. + * + * 'Expectations' take lower priority than 'whens' (I.E. if a matching 'when' is defined at call time, then it will match that over the next 'expect'). + * + * Define in the format: + * expects() + * .method( 'methodName' ) + * .withParameter( 'parameterValue' ) + * .returning( 'theValueItShouldReturn' ) + */ + public Amoss_MethodDefiner expects() { + + if ( expectsNoCalls ) { + throw new Amoss_ExpectsNoCallsAndCallsConfiguredException( 'Cannot state the double expects a call when it has been stated that it expectsNoCalls' ); + } + + if ( ! allowsAnyCallExplicitlyDefined ) { + allowsAnyCall = false; + } + Amoss_Expectation newExpectation = new Amoss_Expectation( this ); + this.expectations.add( newExpectation ); + return new Amoss_MethodDefiner( newExpectation ); + } + + /** + * States that the next method definition is expected to be called in the order it appears. + * + * If a method is subsequently called out of order, will fail the test. + * + * Calling 'verify' will check that all expectations have been met, failing the test if not. + * + * 'Expectations' take lower priority than 'whens' (I.E. if a matching 'when' is defined at call time, then it will match that over the next 'expect'). + * + * Define in the format: + * expects( 'methodName' ) + * .withParameter( 'parameterValue' ) + * .returning( 'theValueItShouldReturn' ) + */ + public Amoss_ParametersDefiner expects( String method ) { + return expects().method( method ); + } + + /** + * States that when a method is called that matches the definition, the stated behaviour will + * be exhibited. + * + * Call order is not checked, and 'verify' will not check that 'whens' have been met. + * + * 'Whens' take higher priority than 'expectations' (I.E. if a matching 'when' is defined at call time, then it + * will match that over the next 'expect'). + * + * Define in the format: + * when() + * .method( 'methodName' ) + * .withParameter( 'parameterValue' ) + * .willReturn( 'theValueItShouldReturn' ) + */ + public Amoss_MethodDefiner when() { + + if ( expectsNoCalls ) { + throw new Amoss_ExpectsNoCallsAndCallsConfiguredException( 'Cannot state the when on a double when it has been stated that it expectsNoCalls' ); + } + + Amoss_Expectation newExpectation = new Amoss_Expectation( this ); + this.whens.add( newExpectation ); + return new Amoss_MethodDefiner( newExpectation ); + } + + /** + * States that when a method is called that matches the definition, the stated behaviour will + * be exhibited. + * + * Call order is not checked, and 'verify' will not check that 'whens' have been met. + * + * 'Whens' take higher priority than 'expectations' (I.E. if a matching 'when' is defined at call time, then it + * will match that over the next 'expect'). + * + * Define in the format: + * when( 'methodName' ) + * .withParameter( 'parameterValue' ) + * .willReturn( 'theValueItShouldReturn' ) + */ + public Amoss_ParametersDefiner when( String method ) { + return when().method( method ); + } + + /** + * States that when a method is called that matches the definition, the stated behaviour will + * be exhibited. Very similar to 'when' + * + * Call order is not checked, and 'verify' will not check that 'whens' have been met. + * + * 'Whens' take higher priority than 'expectations' (I.E. if a matching 'when' is defined at call time, then it + * will match that over the next 'expect'). + * + * Define in the format: + * allows() + * .method( 'methodName' ) + * .withParameter( 'parameterValue' ) + * .willReturn( 'theValueItShouldReturn' ) + */ + public Amoss_MethodDefiner allows() { + + if ( expectsNoCalls ) { + throw new Amoss_ExpectsNoCallsAndCallsConfiguredException( 'Cannot state the double allows a call when it has been stated that it expectsNoCalls' ); + } + + if ( ! allowsAnyCallExplicitlyDefined ) { + allowsAnyCall = false; + } + return when(); + } + + /** + * States that when a method is called that matches the definition, the stated behaviour will + * be exhibited. Very similar to 'when' + * + * Call order is not checked, and 'verify' will not check that 'whens' have been met. + * + * 'Whens' take higher priority than 'expectations' (I.E. if a matching 'when' is defined at call time, then it + * will match that over the next 'expect'). + * + * Define in the format: + * allows( 'methodName' ) + * .withParameter( 'parameterValue' ) + * .willReturn( 'theValueItShouldReturn' ) + */ + public Amoss_ParametersDefiner allows( String method ) { + return allows().method( method ); + } + + /** + * States that whatever mechanism is used to defined the 'expectations' or 'whens' against this + * object, it will either: + * * True - Allow any call to a method to be successful and for those that are not defined to return null. + * * False - Fail the test + * + * Example usage: + * allowsAnyCall( false ) + * .when( 'methodName' ) + * .method( 'methodName' ) + * .withParameter( 'parameterValue' ) + * .willReturn( 'theValueItShouldReturn' ) + */ + public Amoss_Instance allowsAnyCall( Boolean allowsAnyCall ) { + + if ( expectsNoCalls ) { + throw new Amoss_ExpectsNoCallsAndCallsConfiguredException( 'Cannot state the double allowsAnyCall when it has been stated that it expectsNoCalls' ); + } + + this.allowsAnyCallExplicitlyDefined = true; + this.allowsAnyCall = allowsAnyCall; + return this; + } + + /** + * States that when a method is called that does not have a return type defined, it will return the stated object + * + * Example usage: + * setDefaultMethodReturn( new Contact( Name='The default contact' ) ) + */ + public Amoss_Instance byDefaultMethodsReturn( Object defaultMethodReturn ) { + this.defaultMethodReturn = defaultMethodReturn; + return this; + } + + /** + * States that when a method is called that does not have a return type defined, it will return itself. + * + * I.E. the interface of the object being doubled implements a fluent interface + * + * Example usage: + * isFluent() + */ + public Amoss_Instance isFluent() { + this.isFluent = true; + return this; + } + + /** + * States that whatever mechanism is used to defined the 'expectations' or 'whens' against this + * object, it will allow any call to a method to be successful and for those that are not defined + * to return null. + * + * Example usage: + * allowsAnyCall() + * .when( 'methodName' ) + * .withParameter( 'parameterValue' ) + * .willReturn( 'theValueItShouldReturn' ) + */ + public Amoss_Instance allowsAnyCall( ) { + return allowsAnyCall( true ); + } + + /** + * States that this double does not expect any calls against it, and that when a call is made, the test should fail. + * + * Example usage: + * expectsNoCalls() + */ + public void expectsNoCalls() { + + if ( !expectations.isEmpty() || !whens.isEmpty() ) { + throw new Amoss_ExpectsNoCallsAndCallsConfiguredException( 'Cannot state the double expectsNoCalls when expectations or whens have been defined' ); + } + + if ( allowsAnyCallExplicitlyDefined && allowsAnyCall ) { + throw new Amoss_ExpectsNoCallsAndCallsConfiguredException( 'Cannot state the double expectsNoCalls when allowsAnyCall has been set to true' ); + } + + this.expectsNoCalls = true; + } + + /** + * Checks that all configured 'expectations' have been met, failing the test if they have not. + * + * Generally called immediately prior to the test's assertions. + */ + public void verify() { + assertionMechanism.assertEquals( new List(), expectations.getRemainingCallsDescriptions(), String.format( expectedEmptyCallStackMessage, new List{ getClassName() } ) ); + } + + /** + * The start of a request for information on a call in the mock's call stack. + * + * Allows the object to be used as a Test Spy. + * + * Use in the following formats: + * get().call( 2 ).of( 'methodName' ).parameter( 1 ) + * get().latestCallOf( 'methodName' ).parameter( 'parameterName' ) + * + */ + public Amoss_CallChecker get() { + return new Amoss_CallChecker( callLog ); + } + + /** + * The start of a request for information on a call in the mock's call stack. + * + * Allows the object to be used as a Test Spy. + * + * Use in the format: + * call( 2 ).of( 'methodName' ).parameter( 1 ) + * call( 2 ).of( 'methodName' ).parameter( 'parameterName' ) + * + * @param Integer - The number of the call of the stated method. Indexed from 0. Can be negative, -1 returning the last call. + */ + public Amoss_CallChecker call( Integer callNumber ) { + return new Amoss_CallChecker( callLog ).call( callNumber ); + } + + /** + * The start of a request for information on the last call of a given method in the mock's call stack. + * + * Allows the object to be used as a Test Spy. + * + * Use in the format: + * latestCallOf( 'methodName' ).parameter( 1 ) + * + * @param String - The method to get the last call of + */ + public Amoss_CallChecker latestCallOf( String method ) { + return new Amoss_CallChecker( callLog ).latestCallOf( method ); + } + + /** + * Returns the count of the number of calls that were made of the stated method. + * + * Allows the object to be used as a Test Spy. + * + * @param String - The method to get the count of calls of + * @return Integer - The count of the number calls that were made + */ + public Integer countOf( String method ) { + return callLog.numberOfCalls( method ); + } + + /** + * Defines the base content of the assertion message to use when more, or unspecified calls + * are made against a generated Test Double. + * + * @param String - The message + * @return Amoss_Instance - Itself, allowing for a fluent interface + */ + private Amoss_Instance setMoreCallsThanExpectedMessage( String message ) { + this.moreCallsThanExpectedMessage = message; + return this; + } + + /** + * Defines the base content of the assertion message to use when more, or unspecified calls + * are made against a generated Test Double. + * + * @param String - The message + * @return Amoss_Instance - Itself, allowing for a fluent interface + */ + private Amoss_Instance setExpectedNoCallsMessage( String message ) { + this.expectedNoCallsMessage = message; + return this; + } + + /** + * Defines the base content of the assertion message to use when verify is called and + * the expected call stack is not empty + * + * @param String - The message + * @return Amoss_Instance - Itself, allowing for a fluent interface + */ + private Amoss_Instance setExpectedEmptyCallStackMessage( String message ) { + this.expectedEmptyCallStackMessage = message; + return this; + } + + /** + * Internal method that should not be called directly in tests. + * + * Is the StubProvider.handleMethodCall method that handles the resulting method calls to the mock object. + */ + public Object handleMethodCall( Object mockedObject, //NOPMD - matches spec of StubProvider.handleMethodCall + String mockedMethod, + Type returnType, + List parameterTypes, + List parameterNames, + List parameters ) { + + String callDescription = new Amoss_CallDescriber().getCallDescription( mockedMethod, parameters ); + + callLog.addEntry( new Amoss_CallLogEntry().setMethod( mockedMethod ).setParameters( parameterTypes, parameterNames, parameters ) ); + + if ( expectsNoCalls ) { + assertionMechanism.assertEquals( '', callDescription, String.format( expectedNoCallsMessage, new List{ getClassName() } ) ); + } + + if ( whens.hasExpectations() ) { + Amoss_Expectation whenExpectation = whens.getMatchingExpectation( mockedMethod, parameterNames, parameters ); + if ( whenExpectation != null ) { + // If this throws a System.TypeException exception, it is likely you have misconfigured your + // return type in your when or allows. See the note below for more context. + return whenExpectation.getReturnValue( mockedObject, mockedMethod, returnType, parameterTypes, parameterNames, parameters ); + } + } + + if ( allowsAnyCall == false && expectations.isEmpty() ) { + assertionMechanism.assert( false, String.format( moreCallsThanExpectedMessage + ', and no matching "when" or "allows" exists. Was: {2}', new List{ getClassName(), mockedMethod, callDescription } ) ); + } + + if ( allowsAnyCall == false || expectations.matchesNext( mockedMethod, parameterNames, parameters ) ) { + Amoss_Expectation thisExpectation = expectations.unshift(); + + try { + thisExpectation.verify( assertionMechanism, mockedMethod, parameterNames, parameters ); + } catch ( Amoss_Instance.Amoss_AssertionFailureException assertionFailure ) { + assertionFailure.issueAsserton( assertionMechanism ); + } + // If this throws a System.TypeException exception, it is likely you have misconfigured your + // return type in your expectation. See the note below for more context. + return thisExpectation.getReturnValue( mockedObject, mockedMethod, returnType, parameterTypes, parameterNames, parameters ); + } + + // NOTE: + // Would love to be able to guard against invalid returns being configured, but that would need + // Salesforce to implement a dynamic 'instanceOf' method that uses Type, or similar to do that comparison. + // At that point we could check "defaultMethodReturn instanceOf returnType", and similar above. + // Until then, we're stuck with a System.TypeException that cannot be caught by the framework. Sorry! + if ( isFluent ) { + return mockedObject; + } + return defaultMethodReturn; + } + + /** + * Internal method that should not be called directly in tests. + * + * Provides the name of the class that this mock is mimicing. + * + * @return - String - The name of the class + */ + private String getClassName() { + return String.valueOf( this.classType ); + } + + /** + * Internal method that should not be called directly in tests. + * + * Set the assertion mechanism for this mock object implementation. + * Exists to that the assertion mechanism can be mocked when unit + * testing the framework itself. + * + * Is private and testVisible in the hope that auto-complete will not + * pick the method up in normal usage. + * + * @param Amoss_Asserts - The assertionMechanism + * @return Amoss_Instance - Itself, allowing for a fluent interface + */ + @testVisible + private Amoss_Instance setAsserts( Amoss_Asserts assertionMechanism ) { + this.assertionMechanism = assertionMechanism; + return this; + } + + /** + * Internal method that should not be called directly in tests. + * + * Get the assertion mechanism for this mock object implementation. + * + * @return Amoss_Asserts - The assertionMechanism + */ + private Amoss_Asserts getAssertionMechanism() { + return assertionMechanism; + } + + /** + * Internal class that provides a mechanism for checking if a call of a method + * was made against a given call log. + * + * Should not be referenced directly in tests, only via the return from + * methods on other classes. + */ + public class Amoss_CallChecker { + + String method; + Integer callNumber; + Integer parameterNumber; + String parameterName; + + Amoss_CallLog callLog; + + public Amoss_CallChecker( Amoss_CallLog callLog ) { + this.callLog = callLog; + } + + /** + * Defines the name of the method that is being checked. + * + * Use in the format: + * call( 2 ).of( 'methodName' ).parameter( 1 ) + * + * @param String - The method to check + */ + public Amoss_CallChecker of( String method ) { + this.method = method; + return this; + } + + /** + * The start of a request for information on a call in the mock's call stack. + * + * Allows the object to be used as a Test Spy. + * + * Use in the format: + * get().call( 2 ).of( 'methodName' ).parameter( 1 ) + * get().call( 2 ).of( 'methodName' ).parameter( 'parameterName' ) + * + * @param Integer - The number of the call of the stated method. Indexed from 0. Can be negative, -1 returning the last call. + */ + public Amoss_CallChecker call( Integer callNumber ) { + this.callNumber = callNumber; + return this; + } + + /** + * The start of a request for information on the last call of a given method in the mock's call stack. + * + * Allows the object to be used as a Test Spy. + * + * Use in the format: + * get().latestCallOf( 'methodName' ).parameter( 1 ) + * + * @param String - The method to get the last call of + */ + public Amoss_CallChecker latestCallOf( String method ) { + this.method = method; + this.callNumber = -1; + return this; + } + + /** + * Requests the parameter at the stated position (zero-indexed), for the + * current method and call count context. + * + * Allows the object to be used as a Test Spy. + * + * Use in the formats: + * call( 1 ).of( 'methodName' ).parameter( 1 ) + * latestCallOf( 'methodName' ).parameter( 1 ) + * + * @param Object - The parameter that was passed in when the stated method was called + */ + public Object parameter( Integer parameterNumber ) { + this.parameterNumber = parameterNumber; + this.parameterName = null; + return callLog.getCallLogParameter( this.method, this.callNumber, this.parameterNumber ); + } + + /** + * Requests the parameter with the stated name, for the + * current method and call count context. + * + * Allows the object to be used as a Test Spy. + * + * Use in the formats: + * call( 1 ).of( 'methodName' ).parameter( 'parameterName' ) + * latestCallOf( 'methodName' ).parameter( 'parameterName' ) + * + * @param Object - The parameter that was passed in when the stated method was called + */ + public Object parameter( String parameterName ) { + this.parameterName = parameterName; + this.parameterNumber = null; + return callLog.getCallLogParameter( this.method, this.callNumber, this.parameterName ); + } + + /** + * Requests a list of the parameters for the current method and call count context. + * + * Allows the object to be used as a Test Spy. + * + * Use in the formats: + * call( 1 ).of( 'methodName' ).parameters() + * latestCallOf( 'methodName' ).parameters() + * + * @param List - The parameters that were passed in when the stated method was called + */ + public List parameters() { + return callLog.getCallLogParameters( this.method, this.callNumber ); + } + + /** + * Requests a map of the parameters for the current method and call count context, indexed by their name. + * + * Allows the object to be used as a Test Spy. + * + * Use in the formats: + * call( 1 ).of( 'methodName' ).parametersByName() + * latestCallOf( 'methodName' ).parametersByName() + * + * @param Map - The parameters that were passed in when the stated method was called + */ + public Map parametersByName() { + return callLog.getCallLogParametersByName( this.method, this.callNumber ); + } + } + + /** + * Internal class that provides a mechanism for representing a call to a given method + * with a given list of parameter values and means of retrieving some or all of those + * parameters. + * + * Should not be referenced directly in tests, only via the return from + * methods on other classes. + */ + private class Amoss_CallLogEntry { + + String method; + List parameterValues; + List parameterNames; + List parameterTypes; + + Map parameterValuesByName; + + /** + * Internal method that should not be called directly in tests. + * + * Sets the name of the method that this stack represents. + * + * @param String - The method name. + * @return Amoss_CallLogEntry - Itself, allowing for a fluent interface + */ + public Amoss_CallLogEntry setMethod( String method ) { + this.method = method; + return this; + } + + /** + * Internal method that should not be called directly in tests. + * + * Gets the name of the method that this stack represents. + * + * @return String - The name of the method + */ + public String getMethod() { + return this.method; + } + + /** + * Internal method that should not be called directly in tests. + * + * Sets the parameters that were passed in on this method call. + * + * @param List - The types of the parameters that were passed + * @param List - The names of the parameters that were passed + * @param List - The values of the parameters that were passed + * @return Amoss_CallLogEntry - Itself, allowing for a fluent interface + */ + public Amoss_CallLogEntry setParameters( List parameterTypes, List parameterNames, List parameterValues ) { + this.parameterNames = parameterNames; + this.parameterTypes = parameterTypes; + this.parameterValues = parameterValues; + parameterValuesByName = new Map(); + + for ( Integer i=0; i - The parameter values that were passed. + */ + public List getParameters() { + return parameterValues; + } + + /** + * Internal method that should not be called directly in tests. + * + * Gets the parameters for this call, indexed by their name. + * + * If the parameters do not exist then an exception is thrown. + * + * @return Map - The parameter values that were passed, indexed by their name. + */ + public Map getParametersByName() { + return parameterValuesByName; + } + } + + /** + * Internal class that provides a mechanism for representing a stack of calls + * with a means of getting a reference to a particular call of a particular method. + * + * Should not be referenced directly in tests, only via the return from + * methods on other classes. + */ + private class Amoss_CallLog { + + Map> callLogEntries = new Map>(); + + /** + * Internal method that should not be called directly in tests. + * + * Adds the given call log entry to the call stack. + * + * @param Amoss_CallLogEntry - The call log entry to add. + */ + public Amoss_CallLog addEntry( Amoss_CallLogEntry entry ) { + if ( ! callLogEntries.containsKey( entry.getMethod() ) ) { + callLogEntries.put( entry.getMethod(), new List() ); + } + callLogEntries.get( entry.getMethod() ).add( entry ); + return this; + } + + /** + * Internal method that should not be called directly in tests. + * + * Gets the call log entry for the given method, at the given position on the stack. + * + * If the method wasn't called, or called enough times, will throw an exception + * + * @param String - The method to retrieve the call log entry for. + * @param Integer - The number of the call of the stated method. Indexed from 0. Can be negative, -1 returning the last call. + */ + public Amoss_CallLogEntry getCallLog( String method, Integer callNumber ) { + + if ( ! callLogEntries.containsKey( method ) ) { + throw new Amoss_CallLogException( method + ' was never called' ); + } + if ( callNumber >= callLogEntries.get( method ).size() ) { + throw new Amoss_CallLogException( method + ' was not called ' + ( callNumber + 1 ) + ' times' ); + } + if ( ( callLogEntries.get( method ).size() + callNumber ) < 0 ) { + throw new Amoss_CallLogException( method + ' was not called ' + ( Math.abs( callNumber ) ) + ' times' ); + } + if ( callNumber < 0 ) { + callNumber = callLogEntries.get( method ).size() + callNumber; + } + + return callLogEntries.get( method )[ callNumber ]; + } + + /** + * Internal method that should not be called directly in tests. + * + * Gets the number of times the stated method appears in the call stack. + * + * @param String - The method to retrieve the call count for. + * @return Integer - The number of times the stated method was called. + */ + public Integer numberOfCalls( String method ) { + + if ( ! callLogEntries.containsKey( method ) ) { + return 0; + } + + return callLogEntries.get( method ).size(); + } + + /** + * Internal method that should not be called directly in tests. + * + * Gets the parameter at the stated position that was passed into the stated method at the stated call number in the stack. + * + * @param String - The method to retrieve the parameters for. + * @param Integer - The position in the call stack to get the parameters for. Indexed from 0. Can be negative, -1 returning the last call. + * @param Integer - The position of the parameter in the call. Indexed from 0. + * @return Object - The requested parameter value. + */ + public Object getCallLogParameter( String method, Integer callNumber, Integer parameterNumber ) { + return getCallLog( method, callNumber ).getParameter( parameterNumber ); + } + + /** + * Internal method that should not be called directly in tests. + * + * Gets the parameter at the stated position that was passed into the stated method at the stated call number in the stack. + * + * @param String - The method to retrieve the parameters for. + * @param Integer - The position in the call stack to get the parameters for. Indexed from 0. Can be negative, -1 returning the last call. + * @param Integer - The position of the parameter in the call. Indexed from 0. + * @return Object - The requested parameter value. + */ + public Object getCallLogParameter( String method, Integer callNumber, String parameterName ) { + return getCallLog( method, callNumber ).getParameter( parameterName ); + } + + /** + * Internal method that should not be called directly in tests. + * + * Gets the parameters that were passed into the stated method at the stated call number in the stack. + * + * @param String - The method to retrieve the parameters for. + * @return Integer - The position in the call stack to get the parameters for. Indexed from 0. Can be negative, -1 returning the last call. + * @return List - The requested parameter values. + */ + public List getCallLogParameters( String method, Integer callNumber ) { + return getCallLog( method, callNumber ).getParameters(); + } + + /** + * Internal method that should not be called directly in tests. + * + * Gets the parameters that were passed into the stated method at the stated call number in the stack. + * + * @param String - The method to retrieve the parameters for. + * @return Integer - The position in the call stack to get the parameters for. Indexed from 0. Can be negative, -1 returning the last call. + * @return Map - The requested parameter values, indexed by their name. + */ + public Map getCallLogParametersByName( String method, Integer callNumber ) { + return getCallLog( method, callNumber ).getParametersByName(); + } + } + + /** + * Entirely Internal class that should not be referenced at all in a test + * + * Describes the definition of a mock object's 'expectations' or 'whens' within a test and allows the interrogation of that definition. + * + */ + private class Amoss_Expectations { + + private List expectations = new List(); + + public Amoss_Expectations() { + } + + private Amoss_Expectations( Amoss_Expectations toClone ) { + for ( Amoss_Expectation thisExpectation : toClone.expectations ) { + // individual Amoss_Expectation elements should not change once created + // This means we shouldn't need to clone them + this.add( thisExpectation ); + } + } + + /** + * Creates a clone of this 'expectations' description + * + * @return Amoss_Expectations - The new Expectations + */ + public Amoss_Expectations createClone() { + return new Amoss_Expectations( this ); + } + + /** + * Set the Amoss Instance that there Exceptions are for + * + * @param Amoss_Instance - The Amoss Instance + * @return Amoss_Expectations - Itself, allowing for a fluent interface + */ + public Amoss_Expectations setAmossInstance( Amoss_Instance amossInstance ) { + for ( Amoss_Expectation thisExpectation : expectations ) { + thisExpectation.setAmossInstance( amossInstance ); + } + return this; + } + + /** + * Adds an expectation to the list of expectations + * + * @param Amoss_Expectation - The expectation to add + * @return Amoss_Expectations - Itself, allowing for a fluent interface + */ + public Amoss_Expectations add( Amoss_Expectation expectation ) { + expectations.add( expectation ); + return this; + } + + /** + * Checks if the given method and parameterValues combination matches the next expection. + * + * Returns false if no more expections exist. + * + * @param String - The name of the method to match against + * @param List - The parameter names to match against + * @param List - The parameter values to match against + * @return Boolean - States if the given method and parameterValues match the next expectation. + */ + public Boolean matchesNext( String method, List parameterNames, List parameterValues ) { + return expectations.size() > 0 && expectations[0].matches( method, parameterNames, parameterValues ); + } + + /** + * Removes the first expectation in the queue and then returns it. + * + * @return Amoss_Expectation - The first expectation from the queue. + */ + public Amoss_Expectation unshift() { + return expectations.remove(0); + } + + /** + * States if the current expectation queue is empty. + * + * @return Boolean - Is the current expectation queue empty? + */ + public Boolean isEmpty() { + return expectations.isEmpty(); + } + + /** + * States if the current expectation queue has expectations in it. + * + * @return Boolean - Are there any expectations in the current queue? + */ + public Boolean hasExpectations() { + return !isEmpty(); + } + + /** + * Generates and returns a description of the expectations that remain in the current queue. + * + * @return List - A list of the descriptions of the remaining expectations. + */ + public List getRemainingCallsDescriptions() { + List remainingCallDescriptions = new List(); + for ( Amoss_Expectation thisExpectation : expectations ) { + remainingCallDescriptions.add( thisExpectation.getCallDescription() ); + } + return remainingCallDescriptions; + } + + /** + * Given the name of a method, and a list of parameters, checks the queue to see if any expectations + * match that definition. + * + * Returns the *first* expectation that does, or null if non do. + * + * @param String - The name of the method to match against + * @param List - The parameter names to match against + * @param List - The parameter values to match against + * @return Amoss_Expectation - The matching expectation + */ + public Amoss_Expectation getMatchingExpectation( String mockedMethod, List parameterNames, List parameters ) { + + for ( Amoss_Expectation thisExpectation : expectations ) { + if ( thisExpectation.matches( mockedMethod, parameterNames, parameters ) ) { + return thisExpectation; + } + } + return null; + } + } + + /** + * The base class for all Expectation Definers - being the classes that embody the grammar for specifying expectations + */ + public virtual class Amoss_ExpectationDefiner { + protected Amoss_Expectation expectation; + + public Amoss_ExpectationDefiner( Amoss_Expectation expectation ) { + this.expectation = expectation; + } + } + + /** + * The main entry point of the grammar - the specification of a method + */ + public class Amoss_MethodDefiner extends Amoss_ExpectationDefiner { + public Amoss_MethodDefiner( Amoss_Expectation expectation ) { + super( expectation ); + } + + /** + * Allows the definition of the method that this 'expectation' or 'when' is for + * + * For example: + * .expects() + * .method( 'methodName' ) + * .withParameter( 'parameterValue' ) + * .returning( 'theValueItShouldReturn' ) + * + * @param String - The method that this expectation is for + */ + public Amoss_ParametersDefiner method( String method ) { + expectation.setMethod( method ); + return new Amoss_ParametersDefiner( expectation ); + } + } + + /** + * The linking point of the grammar - stating that another expectation is to be configured + * Effectively ends this instance of the configuration and hands control back to the + * Amoss_Instance, which can start a new expectation + */ + public virtual class Amoss_NextExpectationDefiner extends Amoss_ExpectationDefiner { + public Amoss_NextExpectationDefiner( Amoss_Expectation expectation ) { + super( expectation ); + } + + /** + * Allows another 'expectation' or 'when' to be defined against the Amoss_Instance + * + * For example: + * .then().expects() + * .method( 'methodName' ) + * .withParameter( 'parameterValue' ) + * .returning( 'theValueItShouldReturn' ) + */ + public Amoss_Instance then() { + return expectation.getAmossInstance(); + } + + /** + * Allows another 'expectation' or 'when' to be defined against the Amoss_Instance. + * + * Is a synonym for 'then'. + * + * For example: + * .also().when() + * .method( 'methodName' ) + * .withParameter( 'parameterValue' ) + * .willReturn( 'theValueItShouldReturn' ) + */ + public Amoss_Instance also() { + return then(); + } + } + + /** + * The part of the grammar that allows the specification of the return behaviour. + * + * Allows returns or the throwing of exceptions to be specified. + * + * Extends Next Expectation, as a method does not have to return anything. + */ + public virtual class Amoss_ReturnsDefiner extends Amoss_NextExpectationDefiner { + public Amoss_ReturnsDefiner( Amoss_Expectation expectation ) { + super( expectation ); + } + + /** + * States the value that should be returned when this 'expectation' or 'when' is met. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .returns( 'theValueItShouldReturn' ) + * + * Has 'returning' and 'willReturn' as synonyms, and these methods are entirely interchangeable based on preference. + * + * @param Object - The value to return when this expectation is met. + */ + public Amoss_NextExpectationDefiner returns( Object returnValue ) { + expectation.setExpectionReturns( new Amoss_ExpectationValueReturn().setReturnValue( returnValue ) ); + return new Amoss_NextExpectationDefiner( expectation ); + } + + /** + * States the value that should be returned when this 'expectation' or 'when' is met. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .willReturn( 'theValueItShouldReturn' ) + * + * Has 'returning' and 'returns' as synonyms, and these methods are entirely interchangeable based on preference. + * + * @param Object - The value to return when this expectation is met. + */ + public Amoss_NextExpectationDefiner willReturn( Object returnValue ) { + return this.returns( returnValue ); + } + + /** + * States the value that should be returned when this 'expectation' or 'when' is met. + * + * For example, to specify: + * .expects() + * .method( 'methodName' ) + * .returning( 'theValueItShouldReturn' ) + * + * Has 'returns' and 'willReturn' as synonyms, and these methods are entirely interchangeable based on preference. + * + * @param Object - The value to return when this expectation is met. + */ + public Amoss_NextExpectationDefiner returning( Object returnValue ) { + return this.returns( returnValue ); + } + + /** + * States that when this 'expectation' or 'when' is met, the method show throw the given exception. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .throws( new DmlException( 'The exception' ) ) + * + * Has 'throwing' and 'willThrow' as synonyms, and these methods are entirely interchangeable based on preference. + * + * @param Exception - The Exception to throw when this expectation is met. + */ + public Amoss_NextExpectationDefiner throws( Exception exceptionToThrow ) { + expectation.setExpectionReturns( new Amoss_ExpectationExceptionThrower().setExceptionToThrow( exceptionToThrow ) ); + return new Amoss_NextExpectationDefiner( expectation ); + } + + /** + * States that when this 'expectation' or 'when' is met, the method show throw the given exception. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .willThrow( new DmlException( 'The exception' ) ) + * + * Has 'throwing' and 'throws' as synonyms, and these methods are entirely interchangeable based on preference. + * + * @param Exception - The Exception to throw when this expectation is met. + */ + public Amoss_NextExpectationDefiner willThrow( Exception exceptionToThrow ) { + return this.throws( exceptionToThrow ); + } + + /** + * States that when this 'expectation' or 'when' is met, the method show throw the given exception. + * + * For example, to specify: + * .expects() + * .method( 'methodName' ) + * .throwing( new DmlException( 'The exception' ) ) + * + * Has 'throws'and 'willThrow' as synonyms, and these methods are entirely interchangeable based on preference. + * + * @param Exception - The Exception to throw when this expectation is met. + */ + public Amoss_NextExpectationDefiner throwing( Exception exceptionToThrow ) { + return this.throws( exceptionToThrow ); + } + + /** + * States that when this 'expectation' or 'when' is met, the method will be handled by the provided StubProvider + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .handleBy( new StubProviderImplementation() ) + * + * @param StubProvider - The handler to use when returning from the doubled method + */ + public Amoss_NextExpectationDefiner handledBy( StubProvider returnHandler ) { + expectation.setExpectionReturns( new Amoss_ExpectationStubProviderHandledReturn().setReturnHandler( returnHandler ) ); + return new Amoss_NextExpectationDefiner( expectation ); + } + + /** + * States that when this 'expectation' or 'when' is met, the method will be handled by the provided Amoss_MethodHandler + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .handleBy( new Amoss_MethodHandlerImplementation() ) + * + * @param Amoss_MethodHandler - The handler to use when returning from the doubled method + */ + public Amoss_NextExpectationDefiner handledBy( Amoss_MethodHandler returnHandler ) { + expectation.setExpectionReturns( new Amoss_ExpectationAmossHandlerHandledReturn().setReturnHandler( returnHandler ) ); + return new Amoss_NextExpectationDefiner( expectation ); + } + } + + /** + * The part of the grammar that allows the specification of parameters based on their + * position. I.E. expected parameters are defined in order. + */ + public virtual class Amoss_PositionalParametersDefiner extends Amoss_ReturnsDefiner { + + public Amoss_PositionalParametersDefiner( Amoss_Expectation expectation ) { + super( expectation ); + } + + /** + * Adds a parameter value to the list of parameters that are valid for this 'expection' or 'when'. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .withParameter( 'Parameter1' ) + * .thenParameter( 4 ) + * .thenParameter( false ) + * .willReturn( 'theValueItShouldReturn' ) + * + * @param Object - The parameter value to add to the list of expected ones + */ + public Amoss_PositionalParametersDefiner thenParameter( Object parameterValue ) { + return thenParameter().setTo( parameterValue ); + } + + /** + * Adds a parameter value to the list of parameters that are valid for this 'expection' or 'when', leading + * to the ability to specify its shape in ways other than its precise value. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .withParameter().setTo( 'Parameter1' ) + * .thenParameter().setTo( 4 ) + * .thenParameter().setTo( false ) + * .willReturn( 'theValueItShouldReturn' ) + * + */ + public Amoss_PositionalParameterValueDefiner thenParameter() { + ((Amoss_ExpectationPositionalParameters)expectation.getExpectationParameters()) + .moveToNextParameter(); + return new Amoss_PositionalParameterValueDefiner( expectation ); + } + + /** + * Adds a parameter value to the list of parameters that are valid for this 'expection' or 'when', it being allowed to have any value. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .withParameter( 'Parameter1' ) + * .thenAnyParameter() + * .thenParameter( false ) + * .willReturn( 'theValueItShouldReturn' ) + * + */ + public Amoss_PositionalParametersDefiner thenAnyParameter() { + return thenParameter().setTo( Amoss_Instance.Value.ANY_VALUE ); + } + } + + /** + * The part of the grammar that allows the specification of the value of a positional parameter. + * + * At this point in the syntax, the only valid thing to do is define the parameter value. + */ + public class Amoss_PositionalParameterValueDefiner extends Amoss_ExpectationDefiner { + + public Amoss_PositionalParameterValueDefiner( Amoss_Expectation expectation ) { + super( expectation ); + } + + /** + * States the parameter value that is expected for the currently specified parameter. + * + * In order to match an object, the passed parameter must: + * * Be the same instance as that specified. + * * Implement the method 'equals', and return true when called with the specified parameter. + * + * For primitives, it is sufficient that they are the same value. + * + * Is used in combination with 'withParameter'. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .withParameter().setTo( 'Parameter1' ) + * .andParameter().withAnyElement().setTo( 'Parameter2' ) + * .willReturn( 'theValueItShouldReturn' ) + * + * @param Object - The value that the parameter should be set to + */ + public Amoss_PositionalParametersDefiner setTo( Object parameterValue ) { + return setParameterVerifier( valueVerifierBuilder.buildEqualsVerifier( parameterValue ) ); + } + + /** + * States that a parameter is expected to be set to a not null value. + * + * Is used in combination with 'withParameter'. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .withParameter().set() + * .andParameter().withAnyElement().set() + * .willReturn( 'theValueItShouldReturn' ) + */ + public Amoss_PositionalParametersDefiner set() { + return setTo( Amoss_Instance.Value.NOT_NULL ); + } + + /** + * States the parameter value that is expected for the currently specified parameter. + * + * In order to match an object, the passed parameter only needs to equate when serialised as a JSON string. + * + * Is used in combination with 'withParameter'. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .withParameter().setToTheSameValueAs( 'Parameter1' ) + * .andParameter().withAnyElement().setToTheSameValueAs( 'Parameter2' ) + * .willReturn( 'theValueItShouldReturn' ) + * + * @param Object - The value that the parameter should be evaluated against + */ + public Amoss_PositionalParametersDefiner setToTheSameValueAs( Object parameterValue ) { + return setParameterVerifier( valueVerifierBuilder.buildSameValueAsVerifier( parameterValue ) ); + } + + /** + * States the parameter value that is expected for the currently specified parameter. + * + * In order to match an object, the passed parameter needs to be a String that contains the specified value + * + * Is used in combination with 'withParameter'. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .withParameter().containing( 'a string' ) + * .andParameter().withAnyElement().containing( 'a string' ) + * .willReturn( 'theValueItShouldReturn' ) + * + * @param String - The String to search for in the parameter + */ + public Amoss_PositionalParametersDefiner containing( String searchString ) { + return setParameterVerifier( valueVerifierBuilder.buildContainingStringVerifier( searchString ) ); + } + + /** + * States the parameter value that is expected for the currently specified parameter. + * + * In order to match an object, the passed parameter needs to be a String that matches the specified regular expression + * + * Is used in combination with 'withParameter'. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .withParameter().matching( 'a*b' ) + * .andParameter().withAnyElement().matching( 'a*b' ) + * .willReturn( 'theValueItShouldReturn' ) + * + * @param String - The Regular Expression to match against the parameter + */ + public Amoss_PositionalParametersDefiner matching( String searchString ) { + try { + return setParameterVerifier( valueVerifierBuilder.buildMatchingExpressionVerifier( searchString ) ); + } catch ( Amoss_Instance.Amoss_AssertionFailureException assertionFailure ) { + assertionFailure.issueAsserton( expectation.getAssertionMechanism() ); + } + return null; + } + + /** + * States the parameter value that is expected for the currently specified parameter. + * + * In order to match, the passed parameter needs to be a List of the specified length + * + * Is used in combination with 'withParameter'. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .withParameter().aListOfLength( 3 ) + * .andParameter().withAnyElement().aListOfLength( 4 ) + * .willReturn( 'theValueItShouldReturn' ) + * + * @param Integer - The expected length + */ + public Amoss_PositionalParameterAdditionalCollectionValueDefiner aListOfLength( Integer expectedLength ) { + setParameterVerifier( valueVerifierBuilder.buildListOfLengthVerifier( expectedLength ) ); + return new Amoss_PositionalParameterAdditionalCollectionValueDefiner( expectation ); + } + + /** + * States the shape of the SObject parameter value that is expected for the currently specified parameter. + * + * In order to match, the ultimately passed parameter must have the properties that are populated on the + * specified sobject set to the same value. + * + * The ultimate parameter may have more properties set. + * + * Is used in combination with 'withParameterNamed'. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .withParameter().withFieldsSetLike( contact ) + * .andParameter().withAnyElement().withFieldsSetLike( account ) + * .willReturn( 'theValueItShouldReturn' ) + * + * @param Sobject - The SObject that the parameter should be checked against + */ + public Amoss_PositionalParametersDefiner withFieldsSetLike( Sobject parameterValue ) { + return setParameterVerifier( valueVerifierBuilder.buildFieldsSetLikeVerifier( parameterValue ) ); + } + + /** + * States the shape of the sobject parameter value that is expected for the currently specified parameter. + * + * In order to match, the ultimately passed parameter must have the properties that are populated in the + * specified map set to the same value. + * + * The ultimate parameter may have more properties set. + * + * Is used in combination with 'withParameter'. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .withParameter().withFieldsSetTo( contactFieldsMap ) + * .andParameter().withAnyElement().withFieldsSetTo( accountFieldsMap ) + * .willReturn( 'theValueItShouldReturn' ) + * + * @param Map - The Map of values that the parameter should be checked against + */ + public Amoss_PositionalParametersDefiner withFieldsSetTo( Map parameterValue ) { + return setParameterVerifier( valueVerifierBuilder.buildFieldsSetToVerifier( parameterValue ) ); + } + + /** + * States the verifier that should be used to check the currently specified parameter. + * + * Is used in combination with 'withParameter'. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .withParameter().verifiedBy( customVerifier ) + * .willReturn( 'theValueItShouldReturn' ) + * + * @param Amoss_ValueVerifier - The verifier that should be used to check the parameter + */ + public Amoss_PositionalParametersDefiner verifiedBy( Amoss_ValueVerifier verifier ) { + return setParameterVerifier( verifier ); + } + + /** + * States the shape of a List parameter value that is expected for the next parameter. + * + * In order to match, the ultimately passed parameter be a list where EVERY element matches the next specification. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .withParameter().withAllElements().withFieldsSetTo( contactFieldsMap ) + * .withParameter().withAllElements().setTo( true ) + * .willReturn( 'theValueItShouldReturn' ) + * + */ + public Amoss_PositionalAllElementsInCollectionValueDefiner withAllElements() { + return new Amoss_PositionalAllElementsInCollectionValueDefiner( expectation ); + } + + /** + * States the shape of a List parameter value that is expected for the next parameter. + * + * In order to match, the ultimately passed parameter be a list where ANY element matches the next specification. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .withParameter().withAnyElement().withFieldsSetTo( contactFieldsMap ) + * .withParameter().withAnyElement().setTo( true ) + * .willReturn( 'theValueItShouldReturn' ) + * + */ + public Amoss_PositionalAnyElementInCollectionValueDefiner withAnyElement() { + return new Amoss_PositionalAnyElementInCollectionValueDefiner( expectation ); + } + + /** + * States the shape of a List parameter value that is expected for the next parameter. + * + * In order to match, the ultimately passed parameter be a list where the specified element matches the next specification. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .withParameter().withElementAt( 1 ).withFieldsSetTo( contactFieldsMap ) + * .withParameter().withElementAt( 2 ).setTo( true ) + * .willReturn( 'theValueItShouldReturn' ) + * + * @param Integer - The position in the list to add the specification against + */ + public Amoss_PositionalElementAtPositionInCollectionValueDefiner withElementAt( Integer elementPosition ) { + return new Amoss_PositionalElementAtPositionInCollectionValueDefiner( expectation, elementPosition ); + } + + /** + * Internal method that sets the next parameter's verifier on the expectation to that specified + * and then returns the next definer in the allowed syntax + * + * @param Amoss_ValueVerifier - The verifier to set on the expectation + * @return Amoss_PositionalParametersDefiner - The next definer + */ + private Amoss_PositionalParametersDefiner setParameterVerifier( Amoss_ValueVerifier verifier ) { + ((Amoss_ExpectationPositionalParameters)expectation.getExpectationParameters()).addVerifierToCurrentParameter( verifier ); + return new Amoss_PositionalParametersDefiner( expectation ); + } + } + + /** + * The part of the grammar that allows the specification of the value of a positional parameter once it is + * defined as being a list. + * + * At this point in the syntax, the only valid thing to do is define the parameter value via verifiers. + * + * Defines the core of the behaviour - can be applied in different ways by overriding 'setParameterVerifier' + */ + public abstract class Amoss_AbstractPositionalCollectionParameterValueDefiner extends Amoss_ExpectationDefiner { + + public Amoss_AbstractPositionalCollectionParameterValueDefiner( Amoss_Expectation expectation ) { + super( expectation ); + } + + /** + * Internal method that sets the next parameter on the expectation to that specified + * and then returns the next definer in the allowed syntax + * + * @param Amoss_ValueVerifier - The parameter to set on the expectation + * @return Amoss_PositionalParameterAdditionalCollectionValueDefiner - The next definer + */ + protected abstract Amoss_PositionalParameterAdditionalCollectionValueDefiner setParameterVerifier( Amoss_ValueVerifier verifier ); + + /** + * States the parameter value that is expected for the currently specified parameter. + * + * In order to match, the passed parameter must: + * * Be the same instance as that specified. + * * Implement the method 'equals', and return true when called with the specified parameter. + * + * For primitives, it is sufficient that they are the same value. + * + * Is used in combination with 'withParameter'. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .withParameter().setTo( 'Parameter1' ) + * .andParameter().withAnyElement().setTo( 'Parameter2' ) + * .willReturn( 'theValueItShouldReturn' ) + * + * @param Object - The value that the parameter should be set to + */ + public Amoss_PositionalParameterAdditionalCollectionValueDefiner setTo( Object parameterValue ) { + return setParameterVerifier( valueVerifierBuilder.buildEqualsVerifier( parameterValue ) ); + } + + /** + * States that a parameter is expected to be set to a not null value. + * + * Is used in combination with 'withParameter'. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .withParameter().set() + * .andParameter().withAnyElement().set() + * .willReturn( 'theValueItShouldReturn' ) + */ + public Amoss_PositionalParametersDefiner set() { + return setTo( Amoss_Instance.Value.NOT_NULL ); + } + + /** + * States the parameter value that is expected for the currently specified parameter. + * + * In order to match, the passed parameter only needs to equate when serialised as a JSON string. + * + * Is used in combination with 'withParameter'. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .withParameter().setToTheSameValueAs( 'Parameter1' ) + * .andParameter().withAnyElement().setToTheSameValueAs( 'Parameter2' ) + * .willReturn( 'theValueItShouldReturn' ) + * + * @param Object - The value that the parameter should be evaluated against + */ + public Amoss_PositionalParameterAdditionalCollectionValueDefiner setToTheSameValueAs( Object parameterValue ) { + return setParameterVerifier( valueVerifierBuilder.buildSameValueAsVerifier( parameterValue ) ); + } + + /** + * States the parameter value that is expected for the currently specified parameter. + * + * In order to match, the passed parameter needs to be a String that contains the specified value + * + * Is used in combination with 'withParameter'. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .withParameter().containing( 'a string' ) + * .andParameter().withAnyElement().containing( 'a string' ) + * .willReturn( 'theValueItShouldReturn' ) + * + * @param String - The String to search for in the parameter + */ + public Amoss_PositionalParameterAdditionalCollectionValueDefiner containing( String searchString ) { + return setParameterVerifier( valueVerifierBuilder.buildContainingStringVerifier( searchString ) ); + } + + /** + * States the parameter value that is expected for the currently specified parameter. + * + * In order to match, the passed parameter needs to be a String that matches the specified regular expression + * + * Is used in combination with 'withParameter'. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .withParameter().matching( 'a*b' ) + * .andParameter().withAnyElement().matching( 'a*b' ) + * .willReturn( 'theValueItShouldReturn' ) + * + * @param String - The Regular Expression to match against the parameter + */ + public Amoss_PositionalParameterAdditionalCollectionValueDefiner matching( String regularExpression ) { + return setParameterVerifier( valueVerifierBuilder.buildMatchingExpressionVerifier( regularExpression ) ); + } + + /** + * States the parameter value that is expected for the currently specified parameter. + * + * In order to match, the passed parameter needs to be a List of the specified length + * + * Is used in combination with 'withParameter'. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .withParameter().aListOfLength( 3 ) + * .andParameter().withAnyElement().aListOfLength( 4 ) + * .willReturn( 'theValueItShouldReturn' ) + * + * @param Integer - The expected length + */ + public Amoss_PositionalParameterAdditionalCollectionValueDefiner aListOfLength( Integer expectedLength ) { + return setParameterVerifier( valueVerifierBuilder.buildListOfLengthVerifier( expectedLength ) ); + } + + /** + * States the shape of the SObject parameter value that is expected for the currently specified parameter. + * + * In order to match, the ultimately passed parameter must have the properties that are populated on the + * specified sobject set to the same value. + * + * The ultimate parameter may have more properties set. + * + * Is used in combination with 'withParameterNamed'. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .withParameter().withFieldsSetLike( contact ) + * .andParameter().withAnyElement().withFieldsSetLike( account ) + * .willReturn( 'theValueItShouldReturn' ) + * + * @param Sobject - The SObject that the parameter should be checked against + */ + public Amoss_PositionalParameterAdditionalCollectionValueDefiner withFieldsSetLike( Sobject parameterValue ) { + return setParameterVerifier( valueVerifierBuilder.buildFieldsSetLikeVerifier( parameterValue ) ); + } + + /** + * States the shape of the sobject parameter value that is expected for the currently specified parameter. + * + * In order to match, the ultimately passed parameter must have the properties that are populated in the + * specified map set to the same value. + * + * The ultimate parameter may have more properties set. + * + * Is used in combination with 'withParameter'. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .withParameter().withFieldsSetTo( contactFieldsMap ) + * .andParameter().withAnyElement().withFieldsSetTo( accountFieldsMap ) + * .willReturn( 'theValueItShouldReturn' ) + * + * @param Map - The Map of values that the parameter should be checked against + */ + public Amoss_PositionalParameterAdditionalCollectionValueDefiner withFieldsSetTo( Map parameterValue ) { + return setParameterVerifier( valueVerifierBuilder.buildFieldsSetToVerifier( parameterValue ) ); + } + + /** + * States the verifier that should be used to check the currently specified parameter. + * + * Is used in combination with 'withParameter'. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .withParameter().verifiedBy( customVerifier ) + * .willReturn( 'theValueItShouldReturn' ) + * + * @param Amoss_ValueVerifier - The verifier that should be used to check the parameter + */ + public Amoss_PositionalParameterAdditionalCollectionValueDefiner verifiedBy( Amoss_ValueVerifier verifier ) { + return setParameterVerifier( verifier ); + } + } + + /** + * The part of the grammar that allows the specification of positional collection parameters + * based on *every* element in the list matching the specified verifier + */ + public class Amoss_PositionalAllElementsInCollectionValueDefiner extends Amoss_AbstractPositionalCollectionParameterValueDefiner { + + public Amoss_PositionalAllElementsInCollectionValueDefiner( Amoss_Expectation expectation ) { + super( expectation ); + } + + /** + * Internal method that sets the next parameter on the expectation to be checked that every element matches the specified + * verifier and then returns the next definer in the allowed syntax + * + * @param Amoss_ValueVerifier - The verifier to set on the expectation + * @return Amoss_PositionalParameterAdditionalCollectionValueDefiner - The next definer + */ + protected override Amoss_PositionalParameterAdditionalCollectionValueDefiner setParameterVerifier( Amoss_ValueVerifier verifier ) { + ((Amoss_ExpectationPositionalParameters)expectation.getExpectationParameters()).addVerifierToCurrentParameter( + valueVerifierBuilder.buildAllElementsVerifier( verifier ) + ); + return new Amoss_PositionalParameterAdditionalCollectionValueDefiner( expectation ); + } + } + + /** + * The part of the grammar that allows the specification of positional collection parameters + * based on any element in the list matching the specified verifier + */ + public class Amoss_PositionalAnyElementInCollectionValueDefiner extends Amoss_AbstractPositionalCollectionParameterValueDefiner { + + public Amoss_PositionalAnyElementInCollectionValueDefiner( Amoss_Expectation expectation ) { + super( expectation ); + } + + /** + * Internal method that sets the next parameter on the expectation to be checked that any element matches the specified + * verifier and then returns the next definer in the allowed syntax + * + * @param Amoss_ValueVerifier - The verifier to set on the expectation + * @return Amoss_PositionalParameterAdditionalCollectionValueDefiner - The next definer + */ + + protected override Amoss_PositionalParameterAdditionalCollectionValueDefiner setParameterVerifier( Amoss_ValueVerifier verifier ) { + ((Amoss_ExpectationPositionalParameters)expectation.getExpectationParameters()).addVerifierToCurrentParameter( + valueVerifierBuilder.buildAnyElementVerifier( verifier ) + ); + return new Amoss_PositionalParameterAdditionalCollectionValueDefiner( expectation ); + } + } + + /** + * The part of the grammar that allows the specification of positional collection parameters + * based on a given element in the list matching the specified verifier + */ + public class Amoss_PositionalElementAtPositionInCollectionValueDefiner extends Amoss_AbstractPositionalCollectionParameterValueDefiner { + + Integer elementPosition; + + public Amoss_PositionalElementAtPositionInCollectionValueDefiner( Amoss_Expectation expectation, Integer elementPosition ) { + super( expectation ); + this.elementPosition = elementPosition; + } + + /** + * Internal method that sets the next parameter on the expectation to be checked that any element matches the specified + * verifier and then returns the next definer in the allowed syntax + * + * @param Amoss_ValueVerifier - The verifier to set on the expectation + * @return Amoss_PositionalParameterAdditionalCollectionValueDefiner - The next definer + */ + + protected override Amoss_PositionalParameterAdditionalCollectionValueDefiner setParameterVerifier( Amoss_ValueVerifier verifier ) { + ((Amoss_ExpectationPositionalParameters)expectation.getExpectationParameters()).addVerifierToCurrentParameter( + valueVerifierBuilder.buildElementAtVerifier( verifier, elementPosition ) + ); + return new Amoss_PositionalParameterAdditionalCollectionValueDefiner( expectation ); + } + } + + /** + * The part of the grammar that allows the additional specification of the shape of a value of a named parameter once + * the parameter is defined as being a list. + * + * The name must have already been specified. + * + */ + public class Amoss_PositionalParameterAdditionalCollectionValueDefiner extends Amoss_PositionalParametersDefiner { + + public Amoss_PositionalParameterAdditionalCollectionValueDefiner( Amoss_Expectation expectation ) { + super( expectation ); + } + + /** + * States the shape of a List parameter value that is expected for the next parameter. + * + * In order to match, the ultimately passed parameter be a list where EVERY element matches the next specification. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .withParameter().withAllElements().withFieldsSetTo( contactFieldsMap ) + * .withParameter().withAllElements().setTo( true ) + * .willReturn( 'theValueItShouldReturn' ) + * + * @param Object - The value of the parameter to add to the specification + */ + public Amoss_PositionalAllElementsInCollectionValueDefiner withAllElements() { + return new Amoss_PositionalAllElementsInCollectionValueDefiner( expectation ); + } + + /** + * States the shape of a List parameter value that is expected for the next parameter. + * + * In order to match, the ultimately passed parameter be a list where ANY element matches the next specification. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .withParameter().withAnyElement().withFieldsSetTo( contactFieldsMap ) + * .withParameter().withAnyElement().setTo( true ) + * .willReturn( 'theValueItShouldReturn' ) + * + * @param Object - The value of the parameter to add to the specification + */ + public Amoss_PositionalAnyElementInCollectionValueDefiner withAnyElement() { + return new Amoss_PositionalAnyElementInCollectionValueDefiner( expectation ); + } + + /** + * States the shape of a List parameter value that is expected for the next parameter. + * + * In order to match, the ultimately passed parameter be a list where the specified element matches the next specification. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .withParameter().withElementAt( 1 ).withFieldsSetTo( contactFieldsMap ) + * .withParameter().withElementAt( 2 ).setTo( true ) + * .willReturn( 'theValueItShouldReturn' ) + * + * @param Integer - The position in the list to add the specification against + */ + public Amoss_PositionalElementAtPositionInCollectionValueDefiner withElementAt( Integer elementPosition ) { + return new Amoss_PositionalElementAtPositionInCollectionValueDefiner( expectation, elementPosition ); + } + } + + /** + * The part of the grammar that allows the specification of parameters based on their + * names. I.E. expected parameters are defined in a name and value pairing. + */ + public virtual class Amoss_NamedParametersDefiner extends Amoss_ReturnsDefiner implements Amoss_NamedParametersMethodDefiner { + public Amoss_NamedParametersDefiner( Amoss_Expectation expectation ) { + super( expectation ); + } + + /** + * Adds the stated parameter to the list of parameters that are valid for this 'expection' or 'when'. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .withParameterNamed( 'parmeterName1' ).setTo( 'Parameter1' ) + * .andParameterNamed( 'parmeterName2' ).setTo( 'Parameter2' ) + * .willReturn( 'theValueItShouldReturn' ) + * + * Has 'withParameterNamed' as a synonym, and these methods are entirely interchangeable based on preference. + * + * @param Object - The name of the parameter to add to the specification + */ + public Amoss_NamedParameterValueDefiner andParameterNamed( String parameterName ) { + ((Amoss_ExpectationNamedParameters)expectation.getExpectationParameters()).moveToParameter( parameterName ); + return new Amoss_NamedParameterValueDefiner( expectation ); + } + + public Amoss_NamedParameterValueDefiner withParameterNamed( String parameterName ) { + return andParameterNamed( parameterName ); + } + } + + /** + * The part of the grammar that allows the specification of the shape of a value of a named parameter. + * The name must have already been specified. + * + * At this point in the syntax, the only valid thing to do is define the parameter value. + */ + public class Amoss_NamedParameterValueDefiner extends Amoss_ExpectationDefiner { + + public Amoss_NamedParameterValueDefiner( Amoss_Expectation expectation ) { + super( expectation ); + } + + /** + * States the parameter value that is expected for the currently specified parameter. + * + * In order to match an object, the passed parameter must: + * * Be the same instance as that specified. + * * Implement the method 'equals', and return true when called with the specified parameter. + * + * For primitives, it is sufficient that they are the same value. + * + * Is used in combination with 'withParameterNamed'. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .withParameterNamed( 'parmeterName1' ).setTo( 'Parameter1' ) + * .andParameterNamed( 'parmeterName2' ).withAnyElement().setTo( 'Parameter2' ) + * .willReturn( 'theValueItShouldReturn' ) + * + * @param Object - The value that the parameter should be set to + */ + public Amoss_NamedParametersDefiner setTo( Object parameterValue ) { + return setParameterVerifier( valueVerifierBuilder.buildEqualsVerifier( parameterValue ) ); + } + + /** + * States that a parameter is expected to be set to a not null value. + * + * Is used in combination with 'withParameter'. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .withParameter().set() + * .andParameter().withAnyElement().set() + * .willReturn( 'theValueItShouldReturn' ) + */ + public Amoss_NamedParametersDefiner set() { + return setTo( Amoss_Instance.Value.NOT_NULL ); + } + + /** + * States the parameter value that is expected for the currently specified parameter. + * + * In order to match an object, the passed parameter only needs to equate when serialised as a JSON string. + * + * Is used in combination with 'withParameterNamed'. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .withParameterNamed( 'parmeterName1' ).setToTheSameValueAs( 'Parameter1' ) + * .andParameterNamed( 'parmeterName2' ).withAnyElement().setToTheSameValueAs( 'Parameter2' ) + * .willReturn( 'theValueItShouldReturn' ) + * + * @param Object - The value that the parameter should be evaluated against + */ + public Amoss_NamedParametersDefiner setToTheSameValueAs( Object parameterValue ) { + return setParameterVerifier( valueVerifierBuilder.buildSameValueAsVerifier( parameterValue ) ); + } + + /** + * States the parameter value that is expected for the currently specified parameter. + * + * In order to match an object, the passed parameter needs to be a String that contains the specified value + * + * Is used in combination with 'withParameterNamed'. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .withParameterNamed( 'parmeterName1' ).containing( 'a string' ) + * .withParameterNamed( 'parmeterName2' ).withAnyElement().containing( 'a string' ) + * .willReturn( 'theValueItShouldReturn' ) + * + * @param String - The String to search for in the parameter + */ + public Amoss_NamedParametersDefiner containing( String searchString ) { + return setParameterVerifier( valueVerifierBuilder.buildContainingStringVerifier( searchString ) ); + } + + /** + * States the parameter value that is expected for the currently specified parameter. + * + * In order to match an object, the passed parameter needs to be a String that matches the specified regular expression + * + * Is used in combination with 'withParameterNamed'. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .withParameterNamed( 'parmeterName1' ).matching( 'a*b' ) + * .withParameterNamed( 'parmeterName2' ).withAnyElement().matching( 'a*b' ) + * .willReturn( 'theValueItShouldReturn' ) + * + * @param String - The Regular Expression to match against the parameter + */ + public Amoss_NamedParametersDefiner matching( String regularExpression ) { + return setParameterVerifier( valueVerifierBuilder.buildMatchingExpressionVerifier( regularExpression ) ); + } + + /** + * States the parameter value that is expected for the currently specified parameter. + * + * In order to match, the passed parameter needs to be a List of the specified length + * + * Is used in combination with 'withParameter'. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .withParameterNamed( 'parmeterName1' ).aListOfLength( 3 ) + * .andParameterNamed( 'parmeterName2' ).withAnyElement().aListOfLength( 4 ) + * .willReturn( 'theValueItShouldReturn' ) + * + * @param Integer - The expected length + */ + public Amoss_NamedParameterAdditionalCollectionValueDefiner aListOfLength( Integer expectedLength ) { + setParameterVerifier( valueVerifierBuilder.buildListOfLengthVerifier( expectedLength ) ); + return new Amoss_NamedParameterAdditionalCollectionValueDefiner( expectation ); + } + + /** + * States the shape of the SObject parameter value that is expected for the currently specified parameter. + * + * In order to match, the ultimately passed parameter must have the properties that are populated on the + * specified sobject set to the same value. + * + * The ultimate parameter may have more properties set. + * + * Is used in combination with 'withParameterNamed'. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .withParameterNamed( 'parmeterName1' ).withFieldsSetLike( contact ) + * .andParameterNamed( 'parmeterName2' ).withAnyElement().withFieldsSetLike( account ) + * .willReturn( 'theValueItShouldReturn' ) + * + * @param Sobject - The SObject that the parameter should be checked against + */ + public Amoss_NamedParametersDefiner withFieldsSetLike( Sobject parameterValue ) { + return setParameterVerifier( valueVerifierBuilder.buildFieldsSetLikeVerifier( parameterValue ) ); + } + + /** + * States the shape of the SObject parameter value that is expected for the currently specified parameter. + * + * In order to match, the ultimately passed parameter must have the properties that are populated in the + * specified map set to the same value. + * + * The ultimate parameter may have more properties set. + * + * Is used in combination with 'withParameterNamed'. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .withParameterNamed( 'parmeterName1' ).withFieldsSetTo( contactFieldsMap ) + * .andParameterNamed( 'parmeterName2' ).withAnyElement().withFieldsSetTo( accountFieldsMap ) + * .willReturn( 'theValueItShouldReturn' ) + * + * @param Map - The Map of values that the parameter should be checked against + */ + public Amoss_NamedParametersDefiner withFieldsSetTo( Map parameterValue ) { + return setParameterVerifier( valueVerifierBuilder.buildFieldsSetToVerifier( parameterValue ) ); + } + + /** + * States the verifier that should be used to check the currently specified parameter. + * + * Is used in combination with 'withParameterNamed'. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .withParameterNamed( 'parameterName1' ).verifiedBy( customVerifier ) + * .willReturn( 'theValueItShouldReturn' ) + * + * @param Amoss_ValueVerifier - The verifier that should be used to check the parameter + */ + public Amoss_NamedParametersDefiner verifiedBy( Amoss_ValueVerifier verifier ) { + return setParameterVerifier( verifier ); + } + + /** + * States the shape of a List parameter value that is expected for the currently specified parameter. + * + * In order to match, the ultimately passed parameter be a list where EVERY element matches the next specification. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .withParameterNamed( 'parmeterName1' ).withAllElements().withFieldsSetTo( contactFieldsMap ) + * .andParameterNamed( 'parmeterName2' ).withAllElements().setTo( true ) + * .willReturn( 'theValueItShouldReturn' ) + * + */ + public Amoss_NamedAllElementsInCollectionValueDefiner withAllElements() { + return new Amoss_NamedAllElementsInCollectionValueDefiner( expectation ); + } + + /** + * States the shape of a List parameter value that is expected for the currently specified parameter. + * + * In order to match, the ultimately passed parameter be a list where ANY element matches the next specification. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .withParameterNamed( 'parmeterName1' ).withAnyElement().withFieldsSetTo( contactFieldsMap ) + * .andParameterNamed( 'parmeterName2' ).withAnyElement().setTo( true ) + * .willReturn( 'theValueItShouldReturn' ) + * + */ + public Amoss_NamedAnyElementInCollectionValueDefiner withAnyElement() { + return new Amoss_NamedAnyElementInCollectionValueDefiner( expectation ); + } + + /** + * States the shape of a List parameter value that is expected for the currently specified parameter. + * + * In order to match, the ultimately passed parameter be a list where the element at the specified position matches the next specification. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .withParameterNamed( 'parmeterName1' ).withElementAt( 2 ).withFieldsSetTo( contactFieldsMap ) + * .andParameterNamed( 'parmeterName2' ).withElementAt( 2 ).setTo( true ) + * .willReturn( 'theValueItShouldReturn' ) + * + * @param Integer - The position in the list to add the specification against + */ + public Amoss_NamedElementAtPositionInCollectionValueDefiner withElementAt( Integer elementPosition ) { + return new Amoss_NamedElementAtPositionInCollectionValueDefiner( expectation, elementPosition ); + } + + /** + * Internal method that sets the next parameter on the expectation to the specified verifier + * and then returns the next definer in the allowed syntax + * + * @param Amoss_ValueVerifier - The parameter to set on the expectation + * @return Amoss_NamedParametersDefiner - The next definer + */ + private Amoss_NamedParametersDefiner setParameterVerifier( Amoss_ValueVerifier verifier ) { + ((Amoss_ExpectationNamedParameters)expectation.getExpectationParameters()).addVerifierToCurrentParameter( verifier ); + return new Amoss_NamedParametersDefiner( expectation ); + } + } + + /** + * The part of the grammar that allows the specification of the shape of a value of a named parameter that + * has been defined as a list. + * + * The name must have already been specified. + * + * At this point in the syntax, the only valid thing to do is define the parameter value. + * + * Defines the core of the behaviour - can be applied in different ways by overriding 'setParameterVerifier' + */ + public abstract class Amoss_AbstractNamedParameterCollectionValueDefiner extends Amoss_ExpectationDefiner { + + public Amoss_AbstractNamedParameterCollectionValueDefiner( Amoss_Expectation expectation ) { + super( expectation ); + } + + /** + * Internal method that sets the parameter on the expectation to be expected to match the specified + * verifier and then returns the next definer in the allowed syntax + * + * @param Amoss_ValueVerifier - The parameter to set on the expectation + * @return Amoss_NamedParameterAdditionalCollectionValueDefiner - The next definer + */ + protected abstract Amoss_NamedParameterAdditionalCollectionValueDefiner setParameterVerifier( Amoss_ValueVerifier parameter ); + + /** + * States the parameter value that is expected for the currently specified parameter. + * + * In order to match an object, the passed parameter must: + * * Be the same instance as that specified. + * * Implement the method 'equals', and return true when called with the specified parameter. + * + * For primitives, it is sufficient that they are the same value. + * + * Is used in combination with 'withParameterNamed'. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .withParameterNamed( 'parmeterName1' ).setTo( 'Parameter1' ) + * .andParameterNamed( 'parmeterName2' ).withAnyElement().setTo( 'Parameter2' ) + * .willReturn( 'theValueItShouldReturn' ) + * + * @param Object - The value that the parameter should be set to + */ + public Amoss_NamedParameterAdditionalCollectionValueDefiner setTo( Object parameterValue ) { + return setParameterVerifier( valueVerifierBuilder.buildEqualsVerifier( parameterValue ) ); + } + + /** + * States that a parameter is expected to be set to a not null value. + * + * Is used in combination with 'withParameter'. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .withParameterNamed( 'parmeterName1' ).set() + * .andParameterNamed( 'parmeterName1' ).withAnyElement().set() + * .willReturn( 'theValueItShouldReturn' ) + */ + public Amoss_NamedParameterAdditionalCollectionValueDefiner set() { + return setTo( Amoss_Instance.Value.NOT_NULL ); + } + + /** + * States the parameter value that is expected for the currently specified parameter. + * + * In order to match an object, the passed parameter only needs to equate when serialised as a JSON string. + * + * Is used in combination with 'withParameterNamed'. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .withParameterNamed( 'parmeterName1' ).setToTheSameValueAs( 'Parameter1' ) + * .andParameterNamed( 'parmeterName2' ).withAnyElement().setToTheSameValueAs( 'Parameter2' ) + * .willReturn( 'theValueItShouldReturn' ) + * + * @param Object - The value that the parameter should be evaluated against + */ + public Amoss_NamedParameterAdditionalCollectionValueDefiner setToTheSameValueAs( Object parameterValue ) { + return setParameterVerifier( valueVerifierBuilder.buildSameValueAsVerifier( parameterValue ) ); + } + + /** + * States the parameter value that is expected for the currently specified parameter. + * + * In order to match an object, the passed parameter needs to be a String that contains the specified value + * + * Is used in combination with 'withParameterNamed'. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .withParameterNamed( 'parmeterName1' ).containing( 'a string' ) + * .withParameterNamed( 'parmeterName2' ).withAnyElement().containing( 'a string' ) + * .willReturn( 'theValueItShouldReturn' ) + * + * @param String - The String to search for in the parameter + */ + public Amoss_NamedParameterAdditionalCollectionValueDefiner containing( String searchString ) { + return setParameterVerifier( valueVerifierBuilder.buildContainingStringVerifier( searchString ) ); + } + + /** + * States the parameter value that is expected for the currently specified parameter. + * + * In order to match an object, the passed parameter needs to be a String that matches the specified regular expression + * + * Is used in combination with 'withParameterNamed'. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .withParameterNamed( 'parmeterName1' ).matching( 'a*b' ) + * .withParameterNamed( 'parmeterName2' ).withAnyElement().matching( 'a*b' ) + * .willReturn( 'theValueItShouldReturn' ) + * + * @param String - The String to search for in the parameter + */ + public Amoss_NamedParameterAdditionalCollectionValueDefiner matching( String regularExpression ) { + return setParameterVerifier( valueVerifierBuilder.buildMatchingExpressionVerifier( regularExpression ) ); + } + + /** + * States the parameter value that is expected for the currently specified parameter. + * + * In order to match, the passed parameter needs to be a List of the specified length + * + * Is used in combination with 'withParameter'. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .withParameter().aListOfLength( 3 ) + * .andParameter().withAnyElement().aListOfLength( 4 ) + * .willReturn( 'theValueItShouldReturn' ) + * + * @param Integer - The expected length + */ + public Amoss_NamedParameterAdditionalCollectionValueDefiner aListOfLength( Integer expectedLength ) { + return setParameterVerifier( valueVerifierBuilder.buildListOfLengthVerifier( expectedLength ) ); + } + + /** + * States the shape of the SObject parameter value that is expected for the currently specified parameter. + * + * In order to match, the ultimately passed parameter must have the properties that are populated on the + * specified sobject set to the same value. + * + * The ultimate parameter may have more properties set. + * + * Is used in combination with 'withParameterNamed'. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .withParameterNamed( 'parmeterName1' ).withFieldsSetLike( contact ) + * .andParameterNamed( 'parmeterName2' ).withAnyElement().withFieldsSetLike( account ) + * .willReturn( 'theValueItShouldReturn' ) + * + * @param Sobject - The SObject that the parameter should be checked against + */ + public Amoss_NamedParameterAdditionalCollectionValueDefiner withFieldsSetLike( Sobject parameterValue ) { + return setParameterVerifier( valueVerifierBuilder.buildFieldsSetLikeVerifier( parameterValue ) ); + } + + /** + * States the shape of the SObject parameter value that is expected for the currently specified parameter. + * + * In order to match, the ultimately passed parameter must have the properties that are populated in the + * specified map set to the same value. + * + * The ultimate parameter may have more properties set. + * + * Is used in combination with 'withParameterNamed'. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .withParameterNamed( 'parmeterName1' ).withFieldsSetTo( contactFieldsMap ) + * .andParameterNamed( 'parmeterName2' ).withAnyElement().withFieldsSetTo( accountFieldsMap ) + * .willReturn( 'theValueItShouldReturn' ) + * + * @param Map - The Map of values that the parameter should be checked against + */ + public Amoss_NamedParameterAdditionalCollectionValueDefiner withFieldsSetTo( Map parameterValue ) { + return setParameterVerifier( valueVerifierBuilder.buildFieldsSetToVerifier( parameterValue ) ); + } + + /** + * States the verifier that should be used to check the currently specified parameter. + * + * Is used in combination with 'withParameterNamed'. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .withParameterNamed( 'parameterName1' ).verifiedBy( customVerifier ) + * .willReturn( 'theValueItShouldReturn' ) + * + * @param Amoss_ValueVerifier - The verifier that should be used to check the parameter + */ + public Amoss_NamedParameterAdditionalCollectionValueDefiner verifiedBy( Amoss_ValueVerifier verifier ) { + return setParameterVerifier( verifier ); + } + } + + /** + * The part of the grammar that allows the specification of named collection parameters + * based on *every* element in the list matching the specified verifier + */ + public class Amoss_NamedAllElementsInCollectionValueDefiner extends Amoss_AbstractNamedParameterCollectionValueDefiner { + + public Amoss_NamedAllElementsInCollectionValueDefiner( Amoss_Expectation expectation ) { + super( expectation ); + } + + /** + * Internal method that sets the current parameter on the expectation to be checked that every element matches the + * specified verifier and then returns the next definer in the allowed syntax + * + * @param Amoss_ValueVerifier - The verifier to set on the expectation + * @return Amoss_NamedParameterAdditionalCollectionValueDefiner - The next definer + */ + protected override Amoss_NamedParameterAdditionalCollectionValueDefiner setParameterVerifier( Amoss_ValueVerifier verifier ) { + ((Amoss_ExpectationNamedParameters)expectation.getExpectationParameters()).addVerifierToCurrentParameter( + valueVerifierBuilder.buildAllElementsVerifier( verifier ) + ); + return new Amoss_NamedParameterAdditionalCollectionValueDefiner( expectation ); + } + } + + /** + * The part of the grammar that allows the specification of named collection parameters + * based on *any* element in the list matching the specified verifier + */ + public class Amoss_NamedAnyElementInCollectionValueDefiner extends Amoss_AbstractNamedParameterCollectionValueDefiner { + + public Amoss_NamedAnyElementInCollectionValueDefiner( Amoss_Expectation expectation ) { + super( expectation ); + } + + /** + * Internal method that sets the current parameter on the expectation to be checked if any element matches the verifier + * specified and then returns the next definer in the allowed syntax + * + * @param Amoss_ValueVerifier - The verifier to set on the expectation + * @return Amoss_NamedParameterAdditionalCollectionValueDefiner - The next definer + */ + protected override Amoss_NamedParameterAdditionalCollectionValueDefiner setParameterVerifier( Amoss_ValueVerifier verifier ) { + ((Amoss_ExpectationNamedParameters)expectation.getExpectationParameters()).addVerifierToCurrentParameter( + valueVerifierBuilder.buildAnyElementVerifier( verifier ) + ); + return new Amoss_NamedParameterAdditionalCollectionValueDefiner( expectation ); + } + } + + /** + * The part of the grammar that allows the specification of named collection parameters + * based on the element at the specified position in the list matching the specified verifier + */ + public class Amoss_NamedElementAtPositionInCollectionValueDefiner extends Amoss_AbstractNamedParameterCollectionValueDefiner { + + Integer elementPosition; + + public Amoss_NamedElementAtPositionInCollectionValueDefiner( Amoss_Expectation expectation, Integer elementPosition ) { + super( expectation ); + this.elementPosition = elementPosition; + } + + /** + * Internal method that sets the current parameter on the expectation to be checked if any element matches the verifier + * specified and then returns the next definer in the allowed syntax + * + * @param Amoss_ValueVerifier - The verifier to set on the expectation + * @return Amoss_NamedParameterAdditionalCollectionValueDefiner - The next definer + */ + protected override Amoss_NamedParameterAdditionalCollectionValueDefiner setParameterVerifier( Amoss_ValueVerifier verifier ) { + ((Amoss_ExpectationNamedParameters)expectation.getExpectationParameters()).addVerifierToCurrentParameter( + valueVerifierBuilder.buildElementAtVerifier( verifier, elementPosition ) + ); + return new Amoss_NamedParameterAdditionalCollectionValueDefiner( expectation ); + } + } + + /** + * The part of the grammar that allows the additional specification of the shape of a value of a named parameter once + * the parameter is defined as being a list. + * + * The name must have already been specified. + * + */ + public class Amoss_NamedParameterAdditionalCollectionValueDefiner extends Amoss_NamedParametersDefiner { + + public Amoss_NamedParameterAdditionalCollectionValueDefiner( Amoss_Expectation expectation ) { + super( expectation ); + } + + /** + * States the shape of a List parameter value that is expected for the currently specified parameter. + * + * In order to match, the ultimately passed parameter be a list where EVERY element matches the next specification. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .withParameterNamed( 'parmeterName1' ).withAllElements().withFieldsSetTo( contactFieldsMap ) + * .andParameterNamed( 'parmeterName2' ).withAllElements().setTo( true ) + * .willReturn( 'theValueItShouldReturn' ) + * + * @param Object - The value of the parameter to add to the specification + */ + public Amoss_NamedAllElementsInCollectionValueDefiner withAllElements() { + return new Amoss_NamedAllElementsInCollectionValueDefiner( expectation ); + } + + /** + * States the shape of a List parameter value that is expected for the currently specified parameter. + * + * In order to match, the ultimately passed parameter be a list where ANY element matches the next specification. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .withParameterNamed( 'parmeterName1' ).withAnyElement().withFieldsSetTo( contactFieldsMap ) + * .andParameterNamed( 'parmeterName2' ).withAnyElement().setTo( true ) + * .willReturn( 'theValueItShouldReturn' ) + * + * @param Object - The value of the parameter to add to the specification + */ + public Amoss_NamedAnyElementInCollectionValueDefiner withAnyElement() { + return new Amoss_NamedAnyElementInCollectionValueDefiner( expectation ); + } + + /** + * States the shape of a List parameter value that is expected for the currently specified parameter. + * + * In order to match, the ultimately passed parameter be a list where the element at the specified position matches the next specification. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .withParameterNamed( 'parmeterName1' ).withElementAt( 2 ).withFieldsSetTo( contactFieldsMap ) + * .andParameterNamed( 'parmeterName2' ).withElementAt( 2 ).setTo( true ) + * .willReturn( 'theValueItShouldReturn' ) + * + * @param Integer - The position in the list to add the specification against + */ + public Amoss_NamedElementAtPositionInCollectionValueDefiner withElementAt( Integer elementPosition ) { + return new Amoss_NamedElementAtPositionInCollectionValueDefiner( expectation, elementPosition ); + } + } + + public interface Amoss_Thenable { + Amoss_Instance then(); + } + + public interface Amoss_NamedParametersMethodDefiner extends Amoss_Thenable { + Amoss_NamedParameterValueDefiner withParameterNamed( String parameterName ); + Amoss_NextExpectationDefiner returns( Object returnValue ); + Amoss_NextExpectationDefiner handledBy( StubProvider returnHandler ); + Amoss_NextExpectationDefiner handledBy( Amoss_MethodHandler returnHandler ); + Amoss_NextExpectationDefiner throws( Exception exceptionToThrow ); + } + + /** + * The part of the grammar that allows the first specification of a parameter. + * + * The choice of parameter specification method at this point defines the syntax that will + * be used for the remainder of the specification - Any, Positional or Named. + */ + public class Amoss_ParametersDefiner extends Amoss_ReturnsDefiner implements Amoss_NamedParametersMethodDefiner { + public Amoss_ParametersDefiner( Amoss_Expectation expectation ) { + super( expectation ); + } + + /** + * States that this 'expection' or 'when' is valid for any combination of parameters. + * + * Is optional, as omitting parameter definitions will result in any parameter being valid. + * + * Generally used for stubbing methods that are not the primary focus of a given test. + * + * For example: + * .when() + * .method( 'methodName' ) + * .withAnyParameters() + * .willReturn( 'theValueItShouldReturn' ) + * + */ + public Amoss_ReturnsDefiner withAnyParameters() { + expectation.setExpectationParameters( new Amoss_ExpectationAnyParameters() ); + return new Amoss_ReturnsDefiner( expectation ); + } + + /** + * States that this 'expection' or 'when' is valid for this given list of parameters. + * + * For example: + * .when() + * .method( 'methodName' ) + * .withParameters( Object[]{ 'Parameter1' 4, false }) + * .willReturn( 'theValueItShouldReturn' ) + * + * @param Object[] - The list of parameter values that are expected + */ + public Amoss_ReturnsDefiner withParameters( Object[] parameterValues ) { + + Amoss_PositionalParametersDefiner parameterDefiner; + // Yep, the difference between the first call (withParameter) and later ones (thenParameter) came back to bite me + for ( Object thisParameterValue : parameterValues ) { + if ( parameterDefiner == null ) { + parameterDefiner = withParameter().setTo( thisParameterValue ); + } else { + parameterDefiner = parameterDefiner.thenParameter().setTo( thisParameterValue ); + } + } + return new Amoss_ReturnsDefiner( expectation ); + } + + /** + * States that this 'expection' or 'when' is valid for this given map of parameters, indexed by the parameter name. + * + * For example: + * .when() + * .method( 'methodName' ) + * .withParameters( new Map{ + * 'parameterName1' => 'The Parameter Value', + * 'parameterName2' => 15, + * }) + * .willReturn( 'theValueItShouldReturn' ) + * + * @param Map - The map of parameter values that are expected + */ + public Amoss_ReturnsDefiner withParameters( Map parameters ) { + + Amoss_NamedParametersDefiner parameterDefiner; + + for ( String thisParameterName : parameters.keySet() ) { + + // TODO: is there a way of removing the if from here. Feels uncomfortable + if ( parameterDefiner == null ) { + parameterDefiner = withParameterNamed( thisParameterName ).setTo( parameters.get( thisParameterName ) ); + } else { + parameterDefiner = parameterDefiner.withParameterNamed( thisParameterName ).setTo( parameters.get( thisParameterName ) ); + } + } + return new Amoss_ReturnsDefiner( expectation ); + } + + /** + * Sets the expected value of the first parameter in the list of parameters that are valid for this 'expection' or 'when'. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .withParameter( 'Parameter1' ) + * .thenParameter( 4 ) + * .thenParameter( false ) + * .willReturn( 'theValueItShouldReturn' ) + * + * Once called, allows 'thenParameter' and similar to be called (positional syntax), to add further parameters + * + * @param Object - The parameter value to add to the list of expected ones + */ + public Amoss_PositionalParametersDefiner withParameter( Object parameterValue ) { + return withParameter().setTo( parameterValue ); + } + + /** + * Sets the expected value of the first parameter in the list of parameters that are valid for this 'expection' or 'when'. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .withParameter().setTo( 'Parameter1' ) + * .thenParameter().setTo( 4 ) + * .thenParameter().setTo( false ) + * .willReturn( 'theValueItShouldReturn' ) + * + * Once called, allows 'thenParameter' and similar to be called (positional syntax), to add further parameters + * + * @param Object - The parameter value to add to the list of expected ones + */ + public Amoss_PositionalParameterValueDefiner withParameter() { + + Amoss_ExpectationPositionalParameters expectedParameters = new Amoss_ExpectationPositionalParameters().moveToNextParameter(); + expectation.setExpectationParameters( expectedParameters ); + return new Amoss_PositionalParameterValueDefiner( expectation ); + } + + /** + * Sets the expected value of the first parameter in the list of parameters to be 'any value' for this 'expection' or 'when'. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .withAnyParameter() + * .thenParameter( false ) + * .willReturn( 'theValueItShouldReturn' ) + * + * Once called, allows 'thenParameter' and similar to be called (positional parameter syntax), to add further parameters. + * + */ + public Amoss_PositionalParametersDefiner withAnyParameter() { + return withParameter().setTo( Amoss_Instance.Value.ANY_VALUE ); + } + + /** + * Sets the name of an expected parameter, to be followed by 'setTo' in order to specify the value. + * + * For example, to specify: + * .when() + * .method( 'methodName' ) + * .withParameterNamed( 'parmeterName1' ).setTo( 'Parameter1' ) + * .andParameterNamed( 'parmeterName2' ).setTo( 'Parameter2' ) + * .willReturn( 'theValueItShouldReturn' ) + * + * Once used, defines the parameter specifications as using 'named parameter' syntax, and 'andParameterNamed' can be called to add further parameters. + * + * @param String - The name of the parameter to add to the specification + */ + public Amoss_NamedParameterValueDefiner withParameterNamed( String parameterName ) { + Amoss_ExpectationNamedParameters expectedParameters = new Amoss_ExpectationNamedParameters().moveToParameter( parameterName ); + expectation.setExpectationParameters( expectedParameters ); + + return new Amoss_NamedParameterValueDefiner( expectation ); + } + } + + /** + * Internal class that should not be referenced directly in a test + * + * Describes the definition and behaviour of an 'expectation' or 'when' that is defined within a test. + * + * Is configured using 'Expectation Definers' + * + */ + public class Amoss_Expectation { + + private String expectedMethod; + + private Amoss_ExpectationParameters expectedParameters = new Amoss_ExpectationAnyParameters(); + private Amoss_ExpectationReturnMechanism returnMechanism = new Amoss_ExpectationValueReturn(); + + private Amoss_Instance amossInstance; + + private String className { + get { + return amossInstance.getClassName(); + } + } + + public Amoss_Expectation( Amoss_Instance amossInstance ) { + this.amossInstance = amossInstance; + } + + private Amoss_Expectation( Amoss_Expectation expectation ) { + this.expectedMethod = expectation.expectedMethod; + this.expectedParameters = expectation.expectedParameters; + this.returnMechanism = expectation.returnMechanism; + } + + /** + * Creates a clone of this instance. + * + * @return Amoss_Expectation - The new instance of the exectation + */ + public Amoss_Expectation createClone( Amoss_Expectation expectation ) { + return new Amoss_Expectation( expectation ); + } + + /** + * Get the Amoss Instance that this Exception is for + * + * @return Amoss_Instance - The Amoss Instance + */ + public Amoss_Instance getAmossInstance() { + return this.amossInstance; + } + + /** + * Set the Amoss Instance that this Exception is for + * + * @param Amoss_Instance - The Amoss Instance + * @return Amoss_Expectation - Itself, allowing for a fluent interface + */ + public Amoss_Expectation setAmossInstance( Amoss_Instance amossInstance ) { + this.amossInstance = amossInstance; + return this; + } + + /** + * Internal method that sets the expectation parameter object for this expectation. + * In essence, defines the behaviour of the parameter matching. + * + * @param Amoss_ExpectationParameters - The expectation parameters object to use + * @return Amoss_Expectation - Itself, allowing for a fluent interface + */ + public Amoss_Expectation setExpectationParameters( Amoss_ExpectationParameters expectationParameters ) { + this.expectedParameters = expectationParameters; + return this; + } + + /** + * Internal method that retrieves the expectation parameter object for this expectation. + * + * @return Amoss_ExpectationParameters - The expectation parameters object that is in use + */ + public Amoss_ExpectationParameters getExpectationParameters() { + return expectedParameters; + } + + /** + * Internal method that sets the expectation parameter object for this expectation. + * In essence, defines the behaviour of the parameter matching. + * + * @param Amoss_ExpectationReturnMechanism - The expectation return mechanism object to use + * @return Amoss_Expectation - Itself, allowing for a fluent interface + */ + public Amoss_Expectation setExpectionReturns( Amoss_ExpectationReturnMechanism returnMechanism ) { + this.returnMechanism = returnMechanism; + return this; + } + + /** + * Internal method that assigns the methods that this expectation is for. + * + * @param String - The method that this expectation is for + * @return Amoss_Expectation - Itself, allowing for a fluent interface + */ + public Amoss_Expectation setMethod( String method ) { + this.expectedMethod = method; + return this; + } + + /** + * Internal method that should not be called directly in tests. + * + * Returns the value that this expection is defined to. If stated that it should throw an exception, will do so. + * + * @return Object - The value that this expectation is configured to return + */ + public Object getReturnValue( Object mockedObject , String mockedMethod , Type returnType, //NOPMD - matches spec of StubProvider.handleMethodCall + List parameterTypes, List parameterNames, List parameters ) { + + return returnMechanism.getReturn( mockedObject, mockedMethod, returnType, parameterTypes, parameterNames, parameters ); + } + + /** + * Internal method that should not be called directly in tests. + * + * Get the assertion mechanism for this mock object implementation. + * + * @return Amoss_Asserts - The assertionMechanism + */ + public Amoss_Asserts getAssertionMechanism() { + return amossInstance.getAssertionMechanism(); + } + + /** + * Internal method that should not be called directly in tests. + * + * Verifies that the given method and parameter values matches the current expectation, giving a clear description of what is wrong if the verification fails. + * + * @param Amoss_Asserts - The assertion mechanism that should be used for issuing assertions + * @param String - The method to check this expectation against. + * @param List - The parameter namess to check this expectation against. + * @param List - The parameters to check this expectation against. + */ + public void verify( Amoss_Asserts assertionMechanism, String method, List parameterNames, List parameterValues ) { + assertionMechanism.assertEquals( expectedMethod, method, String.format( '{0}.{1} was expected to be called', new List{ className, expectedMethod } ) ); + expectedParameters.verify( className, method, parameterNames, parameterValues ); + } + + /** + * Internal method that should not be called directly in tests. + * + * Returns a description of the current expectation's call + * + * @return String - A description of the current expection's call. + */ + public String getCallDescription() { + String callDescription = this.expectedMethod; + String parametersDescription = expectedParameters.getParametersDescription(); + return this.expectedMethod + '( ' + parametersDescription + ' )'; + } + + /** + * Internal method that should not be called directly in tests. + * + * Checks if the stated call and parameters matches the current expectation's definition. + * + * @param String - The method to check this expectation against. + * @param List - The names of the parameters to check this expectation against. + * @param List - The parameters to check this expectation against. + * @return Boolean - States if this expectation matches that passed passed in. + */ + public Boolean matches( String method, List parameterNames, List parameterValues ) { + if ( method != this.expectedMethod ) { + return false; + } + return expectedParameters.matches( method, parameterNames, parameterValues ); + } + } + + /** + * Internal interface that allows for the specification of the return behaviour that an Expectation should have + * + * An implementing class should provide a mechanism to return a value, or perform an action (e.g. throw an exception) + * when a return should have been made + */ + private interface Amoss_ExpectationReturnMechanism { + Object getReturn( Object mockedObject , String mockedMethod , Type returnType, //NOPMD - matches spec of StubProvider.handleMethodCall + List parameterTypes, List parameterNames, List parameters ); + } + + /** + * Internal class that allows for the specification that an Expectation will return a value + */ + private class Amoss_ExpectationValueReturn implements Amoss_ExpectationReturnMechanism { + + private Object returnValue; + + /** + * Internal method that sets the value for this return mechanism + * + * @param Object - The return value that this mechanism should return + * @return Amoss_ExpectationValueReturn - This object, allowing for a fluent interface + */ + public Amoss_ExpectationValueReturn setReturnValue( Object returnValue ) { + this.returnValue = returnValue; + return this; + } + + /** + * Internal method that returns the specified value of this return mechanism + * + * @return Object - The return value that was specified + */ + public Object getReturn( Object mockedObject , String mockedMethod , Type returnType, //NOPMD - matches spec of StubProvider.handleMethodCall + List parameterTypes, List parameterNames, List parameters ) { + return returnValue; + } + } + + /** + * Internal class that allows for the specification that an Expectation will throw an exception + */ + private class Amoss_ExpectationExceptionThrower implements Amoss_ExpectationReturnMechanism { + + private Exception exceptionToThrow; + + /** + * Internal method that sets the exception for this return mechanism to throw + * + * @param Object - The exception that this mechanism should throw + * @return Amoss_ExpectationValueReturn - This object, allowing for a fluent interface + */ + public Amoss_ExpectationExceptionThrower setExceptionToThrow( Exception exceptionToThrow ) { + this.exceptionToThrow = exceptionToThrow; + return this; + } + + /** + * Internal method that throws the configured exception + */ + public Object getReturn( Object mockedObject , String mockedMethod , Type returnType, //NOPMD - matches spec of StubProvider.handleMethodCall + List parameterTypes, List parameterNames, List parameters ) { + throw exceptionToThrow; + } + } + + /** + * Internal class that allows for the specification that an Expectation's return will be handled by a StubProvider + */ + private class Amoss_ExpectationStubProviderHandledReturn implements Amoss_ExpectationReturnMechanism { + + private StubProvider returnHandler; + + /** + * Internal method that sets the handler for this return mechanism to use + * + * @param StubProvider - The return handler that this mechanism should use + * @return Amoss_ExpectationStubProviderHandledReturn - This object, allowing for a fluent interface + */ + public Amoss_ExpectationStubProviderHandledReturn setReturnHandler( StubProvider returnHandler ) { + this.returnHandler = returnHandler; + return this; + } + + /** + * Internal method that calls the exception handler + */ + public Object getReturn( Object mockedObject , String mockedMethod , Type returnType, //NOPMD - matches spec of StubProvider.handleMethodCall + List parameterTypes, List parameterNames, List parameters ) { + return returnHandler.handleMethodCall( mockedObject, mockedMethod, returnType, parameterTypes, parameterNames, parameters ); + } + } + + /** + * Internal class that allows for the specification that an Expectation's return will be handled by an Amoss_MethodHandler + */ + private class Amoss_ExpectationAmossHandlerHandledReturn implements Amoss_ExpectationReturnMechanism { + + private Amoss_MethodHandler returnHandler; + + /** + * Internal method that sets the handler for this return mechanism to use + * + * @param Amoss_MethodHandler - The return handler that this mechanism should use + * @return Amoss_ExpectationAmossHandlerHandledReturn - This object, allowing for a fluent interface + */ + public Amoss_ExpectationAmossHandlerHandledReturn setReturnHandler( Amoss_MethodHandler returnHandler ) { + this.returnHandler = returnHandler; + return this; + } + + /** + * Internal method that calls the exception handler + */ + public Object getReturn( Object mockedObject , String mockedMethod , Type returnType, //NOPMD - matches spec of StubProvider.handleMethodCall + List parameterTypes, List parameterNames, List parameters ) { + return returnHandler.handleMethodCall( parameters ); + } + } + + /** + * Internal interface that allows for the specification of parameters that an Expectation should have + * + * An implementing class should provide a mechanism to get a String description, verify passed parameters are as stated (reporting via assertions) + * and checking passed parameters are as stated (returning a Boolean) + */ + private abstract class Amoss_ExpectationParameters { + + /** + * Is the collection of ValueVerifiers for the parameter that is in the process of being specified. + * + * Allows us to add new verifiers to the current parameter without having to keep track of which parameter + * it is we are specifying. + */ + Amoss_CompositeValueVerifier currentParameterVerifier; + + /** + * Build a new 'current parameter verifier' and apply that as the new current one so we can add + * new value verifiers to it. + */ + public Amoss_CompositeValueVerifier buildCurrentParameterVerifier() { + currentParameterVerifier = new Amoss_CompositeValueVerifier(); + return currentParameterVerifier; + } + + /** + * Sets the 'current parameter verifier' to be the one specified so we can add new value verifiers to it. + */ + public Amoss_ExpectationParameters setCurrentParameterVerifier( Amoss_ValueVerifier currentParameterVerifier ) { + this.currentParameterVerifier = (Amoss_CompositeValueVerifier)currentParameterVerifier; + return this; + } + + /** + * Add a new value verifier to the current parameter + */ + public Amoss_ExpectationParameters addVerifierToCurrentParameter( Amoss_ValueVerifier verifier ) { + currentParameterVerifier.addVerifier( verifier ); + return this; + } + + public abstract String getParametersDescription(); + public abstract void verify( String className, String method, List parameterNames, List parameterValues ); + public abstract Boolean matches( String method, List parameterNames, List parameterValues ); + } + + /** + * Internal class that allows for the specification that an Expectation can have any parameters + * + * Always assumes that any parameters will match (since 'any' should) + */ + private class Amoss_ExpectationAnyParameters extends Amoss_ExpectationParameters { + + /** + * Internal method that returns a String description of the parameter values that are expected. + * + * Always returns 'any' + * + * @return String - The description of the expected parameter values + */ + public override String getParametersDescription() { + return 'any'; + } + + /** + * Internal method that checks if the passed parameters match those that are configured, reporting via Assertions. + * + * Does nothing. + * + * @param String - The method that is being called, and therefore checked + * @param List - The names of the parameters being passed, and thereforce checked + * @param List - The values of the parameters being passed, and thereforce checked + */ + public override void verify( String className, String method, List parameterNames, List parameterValues ){ + return; + } + + /** + * Internal method that should not be called directly in tests. + * + * Checks if the stated call and parameters matches the current expectation's definition. + * + * @param String - The method to check this expectation against. + * @param List - The names of the parameters to check this expectation against. + * @param List - The parameters to check this expectation against. + * @return Boolean - States if this expectation matches that passed passed in. + */ + public override Boolean matches( String method, List parameterNames, List parameterValues ) { + return true; + } + } + + /** + * Internal interface that allows for the specification of parameters that an Expectation should have using 'positional' notation. + * That is, they are specified in order. + */ + private class Amoss_ExpectationPositionalParameters extends Amoss_ExpectationParameters { + + private List expectedParameters = new List(); + + /** + * Internal method that moves the current specification on to the next parameter so that any further + * parameter specifications apply to that parameter. + */ + Amoss_ExpectationPositionalParameters moveToNextParameter() { + expectedParameters.add( buildCurrentParameterVerifier() ); + return this; + } + + /** + * Internal method that returns a String description of the parameter values that are expected, as a comma seperated list. + * + * @return String - The description of the expected parameter values + */ + public override String getParametersDescription() { + List stringParameters = new List(); + for( Object thisParameter : expectedParameters ) { + stringParameters.add( String.valueOf( thisParameter ) ); + } + return String.join( stringParameters, ', ' ); + } + + /** + * Internal method that checks if the passed parameters match those that are configured, reporting via Assertions. + * + * @param String - The method that is being called, and therefore checked + * @param List - The names of the parameters being passed, and thereforce checked + * @param List - The values of the parameters being passed, and thereforce checked + */ + public override void verify( String className, String method, List parameterNames, List parameterValues ){ + + if ( expectedParameters.size() != parameterValues.size() ) { + throw new Amoss_EqualsAssertionFailureException() + .setExpected( expectedParameters.size() ) + .setActual( parameterValues.size() ) + .setAssertionMessage( String.format( '{0}.{1} was called with an unexpected number of parameters', new List{ className, method } ) ); + } + + for ( Integer parameterNumber = 0; parameterNumber < expectedParameters.size(); parameterNumber++ ) { + try { + expectedParameters[ parameterNumber ].verify( parameterValues[ parameterNumber ] ); + } catch ( Amoss_AssertionFailureException e ) { + throw e.addContextToMessage( String.format( '{0}.{1} had a problem with the parameter value in position {2}: \'{0}\'', new List{ className, method, parameterNumber } ) ); + } + } + } + + /** + * Internal method that should not be called directly in tests. + * + * Checks if the stated call and parameters matches the current expectation's definition. + * + * @param String - The method to check this expectation against. + * @param List - The names of the parameters to check this expectation against. + * @param List - The parameters to check this expectation against. + * @return Boolean - States if this expectation matches that passed passed in. + */ + public override Boolean matches( String method, List parameterNames, List parameterValues ) { + try { + verify( 'class', method, parameterNames, parameterValues ); + } catch ( Exception e ) { + return false; + } + return true; + } + } + + /** + * Internal interface that allows for the specification of parameters that an Expectation should have using 'positional' notation. + * That is, they are specified using their names + */ + private class Amoss_ExpectationNamedParameters extends Amoss_ExpectationParameters { + + private Map expectedParameters = new Map(); + + /** + * Internal method that moves the current specification on to the parameter named, so that any further + * parameter specifications apply to that parameter. + * + * If that parameter has previously been specified, will return the existing parameter verifier so more verifications can be + * added to it. Otherwise ensures a new one is created. + */ + Amoss_ExpectationNamedParameters moveToParameter( String parameterName ) { + + if ( !expectedParameters.containsKey( parameterName ) ) { + expectedParameters.put( parameterName, buildCurrentParameterVerifier() ); + } else { + setCurrentParameterVerifier( expectedParameters.get( parameterName ) ); + } + + return this; + } + + /** + * Internal method that returns a String description of the parameter values that are expected, rendered in a similar + * format to a Map. + * + * @return String - The description of the expected parameter values + */ + public override String getParametersDescription() { + List stringParameters = new List(); + for( String thisParameterName : expectedParameters.keySet() ) { + stringParameters.add( thisParameterName + ' => ' + String.valueOf( expectedParameters.get( thisParameterName ) ) ); + } + return String.join( stringParameters, ', ' ); + } + + /** + * Internal method that checks if the passed parameters match those that are configured, reporting via Assertions. + * + * @param String - The method that is being called, and therefore checked + * @param List - The names of the parameters being passed, and thereforce checked + * @param List - The values of the parameters being passed, and thereforce checked + */ + public override void verify( String className, String method, List parameterNames, List parameterValues ){ + + Set expectedParameterNames = expectedParameters.keySet(); + Set actualParameterNames = new Set( parameterNames ); + + if ( ! actualParameterNames.containsAll( expectedParameterNames ) ) { + throw new Amoss_EqualsAssertionFailureException() + .setExpected( new List( expectedParameterNames ) ) + .setActual( new List( actualParameterNames ) ) + .setAssertionMessage( String.format( '{0}.{1} was called with different parameters to those specified', new List{ className, method } ) ); + } + + for ( Integer parameterNumber = 0; parameterNumber < parameterValues.size(); parameterNumber++ ) { + + String actualParameterName = parameterNames[ parameterNumber ]; + Object actualParameterValue = parameterValues[ parameterNumber ]; + Amoss_ValueVerifier expectationParameter = expectedParameters.get( actualParameterName ); + + if ( expectationParameter != null ) { + try { + expectationParameter.verify( actualParameterValue ); + } catch ( Amoss_AssertionFailureException e ) { + throw e.addContextToMessage( String.format( '{0}.{1} had a problem with the parameter value for "{2}": \'{0}\'', new List{ className, method, actualParameterName } ) ); + } + } + } + } + + /** + * Internal method that should not be called directly in tests. + * + * Checks if the stated call and parameters matches the current expectation's definition. + * + * @param String - The method to check this expectation against. + * @param List - The names of the parameters to check this expectation against. + * @param List - The parameters to check this expectation against. + * @return Boolean - States if this expectation matches that passed passed in. + */ + public override Boolean matches( String method, List parameterNames, List parameterValues ) { + try { + verify( 'class', method, parameterNames, parameterValues ); + } catch ( Exception e ) { + return false; + } + return true; + } + } + + /** + * Internal class that should not be referenced directly in a test + * + * Allows for multiple ValueVerifier implementations to be evaluated against a single value + */ + class Amoss_CompositeValueVerifier implements Amoss_ValueVerifier { + + List valueVerifiers = new List(); + + public Amoss_CompositeValueVerifier addVerifier( Amoss_ValueVerifier verifier ) { + valueVerifiers.add( verifier ); + return this; + } + + /** + * Internal method that should not be called directly in tests. + * + * Describes the verification that this object to configured to make + * + * @return String - A clear description of the value's verification + */ + public override String toString() { + switch on valueVerifiers.size() { + when 0 { + return 'ERROR: No specification of the parameter shape was made (e.g. setTo)'; + } + when 1 { + return String.valueOf( valueVerifiers[0] ); + } + when else { + List descriptions = new List(); + for( Amoss_ValueVerifier thisVerifier : valueVerifiers ) { + descriptions.add( String.valueOf( thisVerifier ) ); + } + return 'Should match all of [ ' + String.join( descriptions, ', ' ) + ' ]'; + } + } + } + + /** + * Internal method that should not be called directly in tests. + * + * Checks if this parameter is correct for the value that is passed into it, throwing an assertion failure expectation if not. + * + * @param Object - The value to check + */ + public void verify( Object actualValue ) { + if ( valueVerifiers.size() == 0 ) { + throw new Amoss_AssertionFailureException() + .setAssertionMessage( 'The specification of the expected parameter value is incompletely set up' ); + } + + for( Amoss_ValueVerifier thisValueVerifier : valueVerifiers ) { + thisValueVerifier.verify( actualValue ); + } + } + } + + /** + * Internal class that should not be referenced directly in a test + * + * Describes the specification of 'any' parameter value that is expected to be passed into a mocked method call + * + */ + class Amoss_AnyValueVerifier implements Amoss_ValueVerifier { + + /** + * Internal method that should not be called directly in tests. + * + * Checks if this parameter is correct for the value that is passed into it, throwing an assertion failure expectation if not. + * + * @param Object - The value to check + */ + public void verify( Object actualValue ) { //NOPMD - Amoss_ExpectationAnyParameter is a special case that behaves predominantly like a 'Null Object' + } + + /** + * Internal method that should not be called directly in tests. + * + * Describes the verification that this object to configured to make + * + * @return String - A clear description of the value's verification + */ + public override String toString() { + return 'Any value'; + } + } + + /** + * Internal class that should not be referenced directly in a test + * + * Describes the specification of 'non empty String' parameter value that is expected to be passed into a mocked method call + * + */ + class Amoss_NotEmptyStringValueVerifier implements Amoss_ValueVerifier { + + /** + * Internal method that should not be called directly in tests. + * + * Checks if this parameter is correct for the value that is passed into it, throwing an assertion failure expectation if not. + * + * @param Object - The value to check + */ + public void verify( Object actualValue ) { + if ( String.isEmpty( (String)actualValue ) ) { + throw new Amoss_EqualsAssertionFailureException() + .setExpected( 'Non Empty' ) + .setActual( actualValue ) + .setAssertionMessage( 'The value was "empty" when it was expected not be be' ); + } + } + } + + /** + * Internal class that should not be referenced directly in a test + * + * Describes the specification of 'not null' parameter value that is expected to be passed into a mocked method call + * + */ + class Amoss_NotNullValueVerifier implements Amoss_ValueVerifier { + + /** + * Internal method that should not be called directly in tests. + * + * Checks if this parameter is correct for the value that is passed into it, throwing an assertion failure expectation if not. + * + * @param Object - The value to check + */ + public void verify( Object actualValue ) { + if ( actualValue == null ) { + throw new Amoss_EqualsAssertionFailureException() + .setExpected( 'Not Null' ) + .setActual( actualValue ) + .setAssertionMessage( 'The value was "null" when it was expected not be be' ); + } + } + + /** + * Internal method that should not be called directly in tests. + * + * Describes the verification that this object to configured to make + * + * @return String - A clear description of the value's verification + */ + public override String toString() { + return 'Any value'; + } + } + + /** + * Internal class that should not be referenced directly in a test + * + * Describes the specification of a 'Same Instance As' Parameter that is expected to be passed into a mocked method call + */ + class Amoss_EqualsValueVerifier implements Amoss_ValueVerifier { + + Object value; + + /** + * Internal method that should not be called directly in tests. + * + * Sets the value that this parameter is expected to be + * + * @param Object - The value that this parameter is expected to be + * @return Amoss_EqualsValueVerifier - Itself, allowing for a fluent interface + */ + public Amoss_EqualsValueVerifier setValueToVerifyAgainst( Object value ) { + this.value = value; + return this; + } + + /** + * Internal method that should not be called directly in tests. + * + * Describes the verification that this object to configured to make + * + * @return String - A clear description of the value's verification + */ + public override String toString() { + return String.valueOf( this.value ); + } + + /** + * Internal method that should not be called directly in tests. + * + * Checks if this parameter is correct for the value that is passed into it, throwing an assertion failure expectation if not. + * + * @param Object - The value to check + */ + public void verify( Object actualValue ) { + if ( this.value != actualValue ) { + if ( Amoss_Asserts.getType( this.value ) != Amoss_Asserts.getType( actualValue ) ) { + throw new Amoss_EqualsAssertionFailureException() + .setExpected( Amoss_Asserts.getType( this.value ) ) + .setActual( Amoss_Asserts.getType( actualValue ) ) + .setAssertionMessage( 'The value is not what was expected - The types do not match' ); + + } else { + throw new Amoss_EqualsAssertionFailureException() + .setExpected( this.value ) + .setActual( actualValue ) + .setAssertionMessage( 'The value is not what was expected - If it is an object, it may be the wrong instance' ); + } + } + } + } + + /** + * Internal class that should not be referenced directly in a test + * + * Describes the specification of an Object Parameter that is expected to be the same instance + * when passed into a mocked method call + */ + class Amoss_SameInstanceVerifier implements Amoss_ValueVerifier { + + Object value; + + /** + * Internal method that should not be called directly in tests. + * + * Sets the value that this parameter is expected to be + * + * @param Object - The value that this parameter is expected to be (actually an Sobject) + * @return Amoss_SameInstanceVerifier - Itself, allowing for a fluent interface + */ + public Amoss_SameInstanceVerifier setValueToVerifyAgainst( Object value ) { + this.value = value; + return this; + } + + /** + * Internal method that should not be called directly in tests. + * + * Describes the verification that this object to configured to make + * + * @return String - A clear description of the value's verification + */ + public override String toString() { + return String.format( 'Same instance as {0}', new List{ value } ); + } + + /** + * Internal method that should not be called directly in tests. + * + * Checks if this parameter is correct for the value that is passed into it, throwing an assertion failure expectation if not. + * + * @param Object - The value to check + */ + public void verify( Object actualValue ) { + if ( actualValue !== this.value ) { + if ( actualValue == this.value ) { + throw new Amoss_EqualsAssertionFailureException() + .setExpected( this.value ) + .setActual( actualValue ) + .setAssertionMessage( 'The value is the same, but not the same instance as was expected' ); + } else { + throw new Amoss_EqualsAssertionFailureException() + .setExpected( this.value ) + .setActual( actualValue ) + .setAssertionMessage( 'The value is not what was expected' ); + } + } + } + } + + /** + * Internal class that should not be referenced directly in a test + * + * Describes the specification of String parameter value that is expected to contain the given string + * + */ + class Amoss_ContainingVerifier implements Amoss_ValueVerifier { + + String value; + + /** + * Internal method that should not be called directly in tests. + * + * Sets the value that this parameter is expected to contain + * + * @param String - The value that this parameter is expected to contain + * @return Amoss_ContainingVerifier - Itself, allowing for a fluent interface + */ + public Amoss_ContainingVerifier setValueToVerifyAgainst( String value ) { + this.value = value; + return this; + } + + /** + * Internal method that should not be called directly in tests. + * + * Checks if this parameter is correct for the value that is passed into it, throwing an assertion failure expectation if not. + * + * @param Object - The value to check + */ + public void verify( Object actualValue ) { + + if ( ! (actualValue instanceOf String) ) { + throw new Amoss_EqualsAssertionFailureException() + .setExpected( 'String' ) + .setActual( Amoss_Asserts.getType( actualValue ) ) + .setAssertionMessage( 'The value was expected to be a String, and was not' ); + } + + if ( !((String)actualValue).contains( value ) ) { + throw new Amoss_EqualsAssertionFailureException() + .setExpected( value ) + .setActual( actualValue ) + .setAssertionMessage( 'The value does not contain the expected String' ); + } + } + + /** + * Internal method that should not be called directly in tests. + * + * Describes the verification that this object to configured to make + * + * @return String - A clear description of the value's verification + */ + public override String toString() { + return 'String value containing "' + value + '"'; + } + } + + /** + * Internal class that should not be referenced directly in a test + * + * Describes the specification of String parameter value that is expected to match the given regular expression + * + */ + class Amoss_MatchingVerifier implements Amoss_ValueVerifier { + + String value; + Pattern valuePattern; + + /** + * Internal method that should not be called directly in tests. + * + * Sets the regular expression that this parameter is expected to match + * + * @param String - The pattern that this parameter is expected to match + * @return Amoss_ContainingVerifier - Itself, allowing for a fluent interface + */ + public Amoss_MatchingVerifier setValueToVerifyAgainst( String value ) { + + this.value = value; + try { + valuePattern = Pattern.compile( value ); + } catch ( Exception e ) { + throw new Amoss_EqualsAssertionFailureException() + .setExpected( 'A valid regular expression as per https://docs.oracle.com/javase/1.5.0/docs/api/index.html?java/util/regex/Pattern.html' ) + .setActual( value ) + .setAssertionMessage( 'The regular expression "' + value + '" does not appear to be valid: ' + e.getMessage() ); + } + return this; + } + + /** + * Internal method that should not be called directly in tests. + * + * Checks if this parameter is correct for the value that is passed into it, throwing an assertion failure expectation if not. + * + * @param Object - The value to check + */ + public void verify( Object actualValue ) { + + if ( ! (actualValue instanceOf String) ) { + throw new Amoss_EqualsAssertionFailureException() + .setExpected( 'String' ) + .setActual( Amoss_Asserts.getType( actualValue ) ) + .setAssertionMessage( 'The value was expected to be a String, and was not' ); + } + + if ( ! valuePattern.matcher( (String)actualValue ).matches() ) { + throw new Amoss_EqualsAssertionFailureException() + .setExpected( value ) + .setActual( actualValue ) + .setAssertionMessage( 'The value does not match the expected expression' ); + } + } + + /** + * Internal method that should not be called directly in tests. + * + * Describes the verification that this object to configured to make + * + * @return String - A clear description of the value's verification + */ + public override String toString() { + return 'String value matching "' + value + '"'; + } + } + + /** + * Internal class that should not be referenced directly in a test + * + * Describes the specification of a List Parameter that is expected to be + * the same instance when passed into a mocked method call + */ + class Amoss_ListSameInstanceVerifier implements Amoss_ValueVerifier { + + List value; + + /** + * Internal method that should not be called directly in tests. + * + * Sets the value that this parameter is expected to be + * + * @param List - The value that this parameter is expected to be + * @return Amoss_ValueVerifier - Itself, allowing for a fluent interface + */ + public Amoss_ListSameInstanceVerifier setValueToVerifyAgainst( List value ) { + this.value = value; + return this; + } + + /** + * Internal method that should not be called directly in tests. + * + * Describes the verification that this object to configured to make + * + * @return String - A clear description of the value's verification + */ + public override String toString() { + return String.format( 'Same instance as {0}', new List{ value } ); + } + + /** + * Internal method that should not be called directly in tests. + * + * Checks if this parameter is correct for the value that is passed into it, throwing an assertion failure expectation if not. + * + * @param Object - The value to check + */ + public void verify( Object actualValue ) { + + if ( ! (actualValue instanceOf List) ) { + throw new Amoss_EqualsAssertionFailureException() + .setExpected( Amoss_Asserts.getType( this.value ) ) + .setActual( Amoss_Asserts.getType( actualValue ) ) + .setAssertionMessage( 'The value is not what was expected - The types do not match' ); + } + + if ( actualValue !== this.value ) { + if ( actualValue == this.value ) { + if ( !allElementsMatch( (List)actualValue ) ) { + throw new Amoss_EqualsAssertionFailureException() + .setExpected( this.value ) + .setActual( actualValue ) + .setAssertionMessage( 'The value is the same, but the list and some of its elements are not the same instances as was expected' ); + + } else { + throw new Amoss_EqualsAssertionFailureException() + .setExpected( this.value ) + .setActual( actualValue ) + .setAssertionMessage( 'The value is the same, but not the same instance as was expected' ); + } + } else { + throw new Amoss_EqualsAssertionFailureException() + .setExpected( this.value ) + .setActual( actualValue ) + .setAssertionMessage( 'The value is not what was expected' ); + } + } + } + + /* + * Checks if any of the elements in the list do not '===' the expected ones. + * + * @param List - The value to check + */ + private Boolean allElementsMatch( List actualValue ) { + for ( Integer i=0; i{ value } ); + } + + /** + * Internal method that should not be called directly in tests. + * + * Checks if this parameter is correct for the value that is passed into it, throwing an assertion failure expectation if not. + * + * @param Sobject - The value to check + */ + public void verify( Object actualValue ) { + + if ( actualValue == null ) { + throw new Amoss_EqualsAssertionFailureException() + .setExpected( this.value ) + .setActual( actualValue ) + .setAssertionMessage( 'Expected an sObject, and got NULL' ); + } + Sobject sobjectActualValue; + try { + sobjectActualValue = (SObject)actualValue; + } catch ( Exception e ) { + throw new Amoss_EqualsAssertionFailureException() + .setExpected( this.value ) + .setActual( actualValue ) + .setAssertionMessage( String.format( 'Expected an sObject, and got {0}', new List{ Amoss_Asserts.getType( actualValue ) } ) ); + } + + List fieldsWithProblems = new List(); + + Map expectedPopulatedFields = this.value.getPopulatedFieldsAsMap(); + for ( String fieldName : expectedPopulatedFields.keySet() ) { + if ( sobjectActualValue.get( fieldName ) != expectedPopulatedFields.get( fieldName ) ) { + fieldsWithProblems.add( fieldName ); + } + } + if ( ! fieldsWithProblems.isEmpty() ) { + throw new Amoss_EqualsAssertionFailureException() + .setExpected( this.value ) + .setActual( actualValue ) + .setAssertionMessage( String.format( 'The following fields were not set as expected: {0}', new List{ fieldsWithProblems } ) ); + } + } + } + + /** + * Internal class that should not be referenced directly in a test + * + * Describes the specification of an 'Fields Set To' Parameter that is expected to be passed into a mocked method call + */ + class Amoss_FieldsSetToVerifier implements Amoss_ValueVerifier { + + Map value; + + /** + * Internal method that should not be called directly in tests. + * + * Sets the value that this parameter is expected to be + * + * @param Sobject - The value that this parameter is expected to be + * @return Amoss_FieldsSetToVerifier - Itself, allowing for a fluent interface + */ + public Amoss_FieldsSetToVerifier setValueToVerifyAgainst( Map value ) { + + if ( value == null ) { + throw new Amoss_ExpectedObjectCannotBeNullException( 'Cannot specify NULL for a "FieldsSetTo" expectation' ); + } + + this.value = value; + return this; + } + + /** + * Internal method that should not be called directly in tests. + * + * Describes the verification that this object to configured to make + * + * @return String - A clear description of the value's verification + */ + public override String toString() { + return String.format( 'SObject with at least the same fields set as {0}', new List{ value } ); + } + + /** + * Internal method that should not be called directly in tests. + * + * Checks if this parameter is correct for the value that is passed into it, throwing an assertion failure expectation if not. + * + * @param Sobject - The value to check + */ + public void verify( Object actualValue ) { + + if ( actualValue == null ) { + throw new Amoss_EqualsAssertionFailureException() + .setExpected( this.value ) + .setActual( actualValue ) + .setAssertionMessage( 'Expected an sObject, and got NULL' ); + } + + Sobject sobjectActualValue; + try { + sobjectActualValue = (SObject)actualValue; + } catch ( Exception e ) { + throw new Amoss_EqualsAssertionFailureException() + .setExpected( this.value ) + .setActual( actualValue ) + .setAssertionMessage( String.format( 'Expected an sObject, and got {0}', new List{ Amoss_Asserts.getType( actualValue ) } ) ); + } + + List fieldsWithProblems = new List(); + + for ( String fieldName : value.keySet() ) { + + try { + if ( sobjectActualValue.get( fieldName ) != value.get( fieldName ) ) { + fieldsWithProblems.add( fieldName ); + } + } catch ( SObjectException e ) { + throw new Amoss_AssertionFailureException() + .setAssertionMessage( String.format( 'Problem comparing field "{0}": {1}', new List{ fieldName, e.getMessage() } ) ); + } + } + if ( ! fieldsWithProblems.isEmpty() ) { + throw new Amoss_EqualsAssertionFailureException() + .setExpected( this.value ) + .setActual( actualValue ) + .setAssertionMessage( String.format( 'The following fields were not set as expected: {0}', new List{ fieldsWithProblems } ) ); + } + } + } + + /** + * Internal class that should not be referenced directly in a test + * + * Describes the specification of a List Parameter that must have the specified length + */ + class Amoss_ListOfLengthVerifier implements Amoss_ValueVerifier { + + Integer expectedLength; + + /** + * Internal method that should not be called directly in tests. + * + * Sets the length that the specification expects the list to be. + * + * @param Integer - The length that is expected + * @return Amoss_ListOfLengthVerifier - Itself, allowing for a fluent interface + */ + public Amoss_ListOfLengthVerifier setExpectedLength( Integer expectedLength ) { + this.expectedLength = expectedLength; + return this; + } + + /** + * Internal method that should not be called directly in tests. + * + * Describes the verification that this object to configured to make + * + * @return String - A clear description of the value's verification + */ + public override String toString() { + return String.format( 'List of length {0}', new List{ expectedLength } ); + } + + /** + * Internal method that should not be called directly in tests. + * + * Checks if this parameter is correct for the verifier that is passed into it, throwing an assertion failure expectation if not. + * + * @param Object - The value to check + */ + public void verify( Object actualValue ) { + + if ( ! (actualValue instanceOf List) ) { + throw new Amoss_EqualsAssertionFailureException() + .setExpected( 'List' ) + .setActual( Amoss_Asserts.getType( actualValue ) ) + .setAssertionMessage( 'The value is not what was expected - The types do not match' ); + } + + List actualList = (List)actualValue; + + if ( this.expectedLength != actualList.size() ) { + throw new Amoss_EqualsAssertionFailureException() + .setExpected( expectedLength ) + .setActual( actualList.size() ) + .setAssertionMessage( 'The value is not what was expected - The list is not the expected length' ); + } + } + } + + /** + * Internal class that should not be referenced directly in a test + * + * Describes the specification of a List Parameter that must contain elements that all pass + * the verifier configured + */ + class Amoss_ListElementsAllVerifier implements Amoss_ValueVerifier { + + Amoss_ValueVerifier elementVerifier; + + /** + * Internal method that should not be called directly in tests. + * + * Sets the Element Verifier that the list parameter elements are expected to pass. + * + * @param Amoss_ValueVerifier - The Verifier that the passed value must pass. + * @return Amoss_ListElementsAllVerifier - Itself, allowing for a fluent interface + */ + public Amoss_ListElementsAllVerifier setValueToVerifyAgainst( Amoss_ValueVerifier value ) { + this.elementVerifier = value; + return this; + } + + /** + * Internal method that should not be called directly in tests. + * + * Describes the verification that this object to configured to make + * + * @return String - A clear description of the value's verification + */ + public override String toString() { + return String.format( 'All => {0}', new List{ elementVerifier } ); + } + + /** + * Internal method that should not be called directly in tests. + * + * Checks if this parameter is correct for the verifier that is passed into it, throwing an assertion failure expectation if not. + * + * @param Object - The value to check + */ + public void verify( Object actualValue ) { + + if ( ! (actualValue instanceOf List) ) { + throw new Amoss_EqualsAssertionFailureException() + .setExpected( 'List' ) + .setActual( Amoss_Asserts.getType( actualValue ) ) + .setAssertionMessage( 'The value is not what was expected - The types do not match' ); + } + + List actualList = (List)actualValue; + + for ( Integer index=0; index < actualList.size(); index++ ) { + try { + elementVerifier.verify( actualList[ index ] ); + } catch ( Amoss_AssertionFailureException e ) { + throw e.addContextToMessage( String.format( 'List element {0} does not match what was expected: \'{0}\'', new List{ index } ) ); + } + } + } + } + + /** + * Internal class that should not be referenced directly in a test + * + * Describes the specification of a List Parameter that must contain at least one element that all passes + * the verifier configured + */ + class Amoss_ListElementsAnyVerifier implements Amoss_ValueVerifier { + + Amoss_ValueVerifier elementVerifier; + + /** + * Internal method that should not be called directly in tests. + * + * Sets the Element Verifier that at least one of the list parameter's element is expected to pass + * + * @param Amoss_ValueVerifier - The Verifier that one of the passed elements must pass. + * @return Amoss_ListElementsAnyVerifier - Itself, allowing for a fluent interface + */ + public Amoss_ListElementsAnyVerifier setValueToVerifyAgainst( Amoss_ValueVerifier value ) { + this.elementVerifier = value; + return this; + } + + /** + * Internal method that should not be called directly in tests. + * + * Describes the verification that this object to configured to make + * + * @return String - A clear description of the value's verification + */ + public override String toString() { + return String.format( 'At least one => {0}', new List{ elementVerifier } ); + } + + /** + * Internal method that should not be called directly in tests. + * + * Checks if this parameter is correct for the verifier that is passed into it, throwing an assertion failure expectation if not. + * + * @param Object - The value to check + */ + public void verify( Object actualValue ) { + + if ( ! (actualValue instanceOf List) ) { + throw new Amoss_EqualsAssertionFailureException() + .setExpected( 'List' ) + .setActual( Amoss_Asserts.getType( actualValue ) ) + .setAssertionMessage( 'The value is not what was expected - The types do not match' ); + } + + List actualList = (List)actualValue; + + for ( Integer index=0; index < actualList.size(); index++ ) { + try { + elementVerifier.verify( actualList[ index ] ); + return; + } catch ( Amoss_AssertionFailureException e ) { //NOPMD - Intentionally left empty - verify throws an exception if a value does not pass verification. We just want one element that passes + } + } + + throw new Amoss_EqualsAssertionFailureException() + .setExpected( String.valueOf( elementVerifier ) ) + .setActual( actualValue ) + .setAssertionMessage( 'The value is not what was expected - At least one element in the list should pass the described verification' ); + } + } + + /** + * Internal class that should not be referenced directly in a test + * + * Describes the specification that the element in a particlar position of a List must pass the configured verifier + */ + class Amoss_ListElementAtVerifier implements Amoss_ValueVerifier { + + Integer elementPosition; + Amoss_ValueVerifier elementVerifier; + + /** + * Internal method that should not be called directly in tests. + * + * Sets the position in the list to check. + * + * @param Object - The Verifier that the congigured element must pass. Is actually an Amoss_ValueVerifier. + * @return Amoss_ListElementAtVerifier - Itself, allowing for a fluent interface + */ + public Amoss_ListElementAtVerifier setElementToVerify( Integer elementPosition ) { + this.elementPosition = elementPosition; + return this; + } + + /** + * Internal method that should not be called directly in tests. + * + * Sets the Element Verifier that the element is expected to pass + * + * @param Amoss_ValueVerifier - The Verifier that the congigured element must pass. + * @return Amoss_ListElementAtVerifier - Itself, allowing for a fluent interface + */ + public Amoss_ListElementAtVerifier setValueToVerifyAgainst( Amoss_ValueVerifier value ) { + this.elementVerifier = value; + return this; + } + + /** + * Internal method that should not be called directly in tests. + * + * Describes the verification that this object to configured to make + * + * @return String - A clear description of the value's verification + */ + public override String toString() { + return String.format( '{0} => {1}', new List{ elementPosition, elementVerifier } ); + } + + /** + * Internal method that should not be called directly in tests. + * + * Checks if this value is correct for the verifier that is passed into it, throwing an assertion failure expectation if not. + * + * @param Object - The value to check + */ + public void verify( Object actualValue ) { + + if ( ! (actualValue instanceOf List) ) { + throw new Amoss_EqualsAssertionFailureException() + .setExpected( 'List' ) + .setActual( Amoss_Asserts.getType( actualValue ) ) + .setAssertionMessage( 'The value is not what was expected - The types do not match' ); + } + + List actualList = (List)actualValue; + + if ( actualList.size() < elementPosition + 1 ) { + throw new Amoss_EqualsAssertionFailureException() + .setExpected( elementPosition + 1 ) + .setActual( actualList.size() ) + .setAssertionMessage( 'The value is not what was expected - The list is not as long as required' ); + } + + try { + elementVerifier.verify( actualList[ elementPosition ] ); + } catch ( Amoss_AssertionFailureException e ) { + throw e.addContextToMessage( String.format( 'List element {0} does not match what was expected: \'{0}\'', new List{ elementPosition } ) ); + } + } + } + + /** + * Internal class that should not be refenced in tests. + * + * Constructs instances of Amoss_ValueVerifier. + */ + private class Amoss_ValueVerifierBuilder { + + /** + * Internal method that should not be refenced in tests. + * + * Constructs an instance of Amoss_ValueVerifier based on the passed Object. + * + * @param Object - The value that is expected for the Amoss_ValueVerifier + * @return Amoss_ValueVerifier - The appropriate value verifier instance for this parameter + */ + public Amoss_ValueVerifier buildEqualsVerifier( Object value ) { + + // Not ideal, but since we don't want to implement multiple versions of + // setTo in the grammer, every call to buildEqualsVerifier would resolve to the Object (this) + // version. + // As we only want to distinguish between a couple of types, this seems the + // simplest way. + if ( value == Amoss_Instance.Value.ANY_VALUE ) { + return buildAnyVerifier(); + } + if ( value == Amoss_Instance.Value.NOT_NULL ) { + return buildNotNullVerifier(); + } + if ( value == Amoss_Instance.Value.NOT_EMPTY ) { + return buildNotEmptyStringVerifier(); + } + if ( value instanceOf List ) { + return buildSameInstanceVerifier( (List)value ); + } + if ( Amoss_Asserts.getType( value ).startsWith( 'Map<' ) ) { // because of the way we can't cast Maps with different types of keys, and to avoid loads of instance of checks + return buildSameInstanceVerifier( value ); + } + if ( Amoss_Asserts.getType( value ).startsWith( 'Set<' ) ) { // because of the way we can't cast Set, and to avoid loads of instance of checks + return buildSameInstanceVerifier( value ); + } + if ( value instanceOf Sobject ) { + return buildSameInstanceVerifier( (Sobject)value ); + } + + return new Amoss_EqualsValueVerifier().setValueToVerifyAgainst( value ); + } + + /** + * Internal method that should not be refenced in tests. + * + * Constructs an instance of an Amoss_SameInstanceVerifier + * + * @param Object - The Object value that is expected for the Amoss_ValueVerifier + * @return Amoss_ValueVerifier - The appropriate value verifier instance for this value (actually a Amoss_SameInstanceVerifier) + */ + public Amoss_ValueVerifier buildSameInstanceVerifier( Object value ) { + return new Amoss_SameInstanceVerifier().setValueToVerifyAgainst( value ); + } + + /** + * Internal method that should not be refenced in tests. + * + * Constructs an instance of an Amoss_SameInstanceVerifier + * + * @param Sobject - The Sobject value that is expected for the Amoss_ValueVerifier + * @return Amoss_ValueVerifier - The appropriate value verifier instance for this value (actually a Amoss_SameInstanceVerifier) + */ + public Amoss_ValueVerifier buildSameInstanceVerifier( Sobject value ) { + return new Amoss_SameInstanceVerifier().setValueToVerifyAgainst( value ); + } + + /** + * Internal method that should not be refenced in tests. + * + * Constructs an instance of an Amoss_SameInstanceVerifier + * + * @param List - The List value that is expected for the Amoss_ValueVerifier + * @return Amoss_ValueVerifier - The appropriate value verifier instance for this value (actually a Amoss_SameInstanceVerifier) + */ + public Amoss_ValueVerifier buildSameInstanceVerifier( List value ) { + return new Amoss_ListSameInstanceVerifier().setValueToVerifyAgainst( value ); + } + + /** + * Internal method that should not be refenced in tests. + * + * Constructs an instance of an Amoss_SameValueAsVerifier + * + * @param Object - The Sobject value that is expected for the Amoss_ValueVerifier + * @return Amoss_ValueVerifier - The appropriate value verifier instance for this value (actually a Amoss_SameValueAsVerifier) + */ + public Amoss_ValueVerifier buildSameValueAsVerifier( Object value ) { + return new Amoss_SameValueAsVerifier().setValueToVerifyAgainst( value ); + } + + /** + * Internal method that should not be refenced in tests. + * + * Constructs an instance of an Amoss_ContainingVerifier + * + * @param String - The String value that is expected for the Amoss_ValueVerifier + * @return Amoss_ValueVerifier - The appropriate value verifier instance for this value (actually a Amoss_ContainingVerifier) + */ + public Amoss_ValueVerifier buildContainingStringVerifier( String value ) { + return new Amoss_ContainingVerifier().setValueToVerifyAgainst( value ); + } + + /** + * Internal method that should not be refenced in tests. + * + * Constructs an instance of an Amoss_MatchingVerifier + * + * @param String - The Regular Expression value that is expected for the Amoss_ValueVerifier + * @return Amoss_ValueVerifier - The appropriate value verifier instance for this value (actually a Amoss_MatchingVerifier) + */ + public Amoss_ValueVerifier buildMatchingExpressionVerifier( String value ) { + return new Amoss_MatchingVerifier().setValueToVerifyAgainst( value ); + } + + /** + * Internal method that should not be refenced in tests. + * + * Constructs an instance of an Amoss_FieldsSetLikeVerifier, which checks that Sobjects have the same properties set as the defined 'sobjectSetLike'. + * + * @param Sobject - The Sobject value that is expected for the Amoss_ValueVerifier + * @return Amoss_ValueVerifier - The appropriate value verifier instance for this value (actually a Amoss_FieldsSetLikeVerifier) + */ + public Amoss_ValueVerifier buildFieldsSetLikeVerifier( Sobject sobjectSetLike ) { + return new Amoss_FieldsSetLikeVerifier().setValueToVerifyAgainst( sobjectSetLike ); + } + + /** + * Internal method that should not be refenced in tests. + * + * Constructs an instance of an Amoss_FieldsSetToVerifier, which checks that SObjects have the same fields set as the defined Map. + * + * @param Map - The Map that contains the fields that are expected for the Amoss_ValueVerifier + * @return Amoss_ValueVerifier - The appropriate value verifier instance for this parameter (actually a Amoss_FieldsSetToVerifier) + */ + public Amoss_ValueVerifier buildFieldsSetToVerifier( Map fields ) { + return new Amoss_FieldsSetToVerifier().setValueToVerifyAgainst( fields ); + } + + /** + * Internal method that should not be refenced in tests. + * + * Constructs an instance of an Amoss_ListOfLengthVerifier, which checks that a given value is a list that has the specified + * number of elements + * + * @param Integer - The number of elements that are expected + * @return Amoss_ValueVerifier - The appropriate value verifier instance for this parameter (actually a Amoss_ListOfLengthVerifier) + */ + public Amoss_ValueVerifier buildListOfLengthVerifier( Integer expectedLength ) { + return new Amoss_ListOfLengthVerifier().setExpectedLength( expectedLength ); + } + + /** + * Internal method that should not be refenced in tests. + * + * Constructs an instance of an Amoss_ListElementsAllVerifier, which checks that a given value is a list that has all elements passing + * the provided Amoss_ValueVerifier. + * + * @param Amoss_ValueVerifier - The value verifier that should be used to check all the elements in the later passed value. + * @return Amoss_ValueVerifier - The appropriate value verifier instance for this parameter (actually a Amoss_ListElementsAllVerifier) + */ + public Amoss_ValueVerifier buildAllElementsVerifier( Amoss_ValueVerifier elementVerifier ) { + return new Amoss_ListElementsAllVerifier().setValueToVerifyAgainst( elementVerifier ); + } + + /** + * Internal method that should not be refenced in tests. + * + * Constructs an instance of an Amoss_ListElementsAnyVerifier, which checks that a given value is a list that has any of the elements passing + * the provided Amoss_ValueVerifier. + * + * @param Amoss_ValueVerifier - The value verifier that should be used to check the elements in the later passed value. + * @return Amoss_ValueVerifier - The appropriate value verifier instance for this parameter (actually a Amoss_ListElementsAnyVerifier) + */ + public Amoss_ValueVerifier buildAnyElementVerifier( Amoss_ValueVerifier elementVerifier ) { + return new Amoss_ListElementsAnyVerifier().setValueToVerifyAgainst( elementVerifier ); + } + + /** + * Internal method that should not be refenced in tests. + * + * Constructs an instance of an Amoss_ListElementAtVerifier, which checks that a given value is a list that has the specified element + * the provided Amoss_ValueVerifier. + * + * @param Amoss_ValueVerifier - The value verifier that should be used to check the elements in the later passed value. + * @return Amoss_ValueVerifier - The appropriate value verifier instance for this parameter (actually a Amoss_ListElementAtVerifier) + */ + public Amoss_ValueVerifier buildElementAtVerifier( Amoss_ValueVerifier elementVerifier, Integer elementPosition ) { + return new Amoss_ListElementAtVerifier().setElementToVerify( elementPosition ).setValueToVerifyAgainst( elementVerifier ); + } + + /** + * Internal method that should not be refenced in tests. + * + * Constructs an instance of an Amoss_AnyValueVerifier + * + * @return Amoss_ValueVerifier - The Amoss_AnyValueVerifier + */ + public Amoss_ValueVerifier buildAnyVerifier() { + return new Amoss_AnyValueVerifier(); + } + + /** + * Internal method that should not be refenced in tests. + * + * Constructs an instance of an Amoss_NotNullValueVerifier + * + * @return Amoss_ValueVerifier - The Amoss_NotNullValueVerifier + */ + public Amoss_ValueVerifier buildNotNullVerifier() { + return new Amoss_NotNullValueVerifier(); + } + + /** + * Internal method that should not be refenced in tests. + * + * Constructs an instance of an Amoss_NotEmptyStringValueVerifier + * + * @return Amoss_ValueVerifier - The Amoss_NotEmptyStringValueVerifier + */ + public Amoss_ValueVerifier buildNotEmptyStringVerifier() { + return new Amoss_NotEmptyStringValueVerifier(); + } + } + + public virtual class Amoss_AssertionFailureException extends Exception { + + private String message; + + /** + * Set the message for failed assertion that this exception represents + * + * @param String - The assertion message for this exception. + */ + public Amoss_AssertionFailureException setAssertionMessage( String message ) { + this.message = message; + return this; + } + + /** + * Adds more context to the current message on this exception. + * + * @param String - The context to add to the message, being a string containing {0}, the place the original message should exist + */ + public Amoss_AssertionFailureException addContextToMessage( String context ) { + this.message = String.format( context, new List{ this.message } ); + return this; + } + + /** + * Issue an assertion failure based on the current configuration + */ + public virtual void issueAsserton( Amoss_Asserts assertionMechanism ) { + assertionMechanism.assert( false, message ); + } + } + + public class Amoss_EqualsAssertionFailureException extends Amoss_AssertionFailureException { + + private Object expected; + private Object actual; + + /** + * Set the expected for this exception + * + * @param Object - The expected value + * @return Amoss_EqualsAssertionFailureException - Itself, allowing for a fluent interface + */ + public Amoss_EqualsAssertionFailureException setExpected( Object expected ) { + this.expected = expected; + return this; + } + + /** + * Set the actual for this exception + * + * @param Object - The actual value + * @return Amoss_EqualsAssertionFailureException - Itself, allowing for a fluent interface + */ + public Amoss_EqualsAssertionFailureException setActual( Object actual ) { + this.actual = actual; + return this; + } + + /** + * Issue an assertion failure based on the current configuration + */ + public override void issueAsserton( Amoss_Asserts assertionMechanism ) { + String message = String.format( '{0}.\nExpected: {1}\nActual: {2}', new List{ this.message, expected, actual } ); + assertionMechanism.assert( false, message ); + } + } + + /** + * Internal class that should not be refenced in tests. + * + * Can produce a text description of a method call + */ + private class Amoss_CallDescriber { + + /** + * Internal method that should not be called directly in tests. + * + * Given a method name, and a list of parameters, will return a string representation of it + */ + public String getCallDescription( String method, List parameters ) { + return String.format( '{0}({1})', new List{ method, String.join( parameters, ', ' ) } ); + } + } + + // + // Callout Mock grammer and verifiers + // + /** + * The part of the grammar that allows the specification of the behaviour of a Mock Http Callout instance, + * defining whether requests are expected / allowed, and allow entry into the part of the grammer that + * defines what should happen in what situation. + * + */ + public class Amoss_CalloutMockDefiner { + + Amoss_Instance instance; + + public Amoss_CalloutMockDefiner( Amoss_Instance instance ) { + + this.instance = instance; + try { + Test.setMock( HttpCalloutMock.class, (HttpCalloutMock)this.instance.generateDouble() ); + } catch( Exception e ) { + throw new Amoss_NotAnHttpCalloutMockException( 'Attempted to create an Http Callout Mock when the Amoss_Instance is not a test double of a class or interface that implements HttpCalloutMock\n' + e.getMessage(), e ); + } + } + + /** + * States that a request of the provided shape is 'expected' and that the test should fail + * if it does not happen, or a different request is made. + * + * For example: + * .expects() + * .method( 'GET' ) + * .endpoint().containing( 'account/xxxxx' ) + * .respondsWith() + * .body( '{some JSON}' ) + * + */ + public Amoss_CalloutMockRequestDefiner expects() { + return new Amoss_CalloutMockRequestDefiner( instance.expects( 'respond' ) ); + } + + /** + * States that what should happen 'when' a request of the provided shape is sent. + * + * For example: + * .when() + * .method( 'GET' ) + * .endpoint().containing( 'account/xxxxx' ) + * .respondsWith() + * .body( '{some JSON}' ) + * + */ + public Amoss_CalloutMockRequestDefiner when() { + return new Amoss_CalloutMockRequestDefiner( instance.when( 'respond' ) ); + } + + /** + * States that a request of the provided shape is 'allowed', implying that requests of shapes that are + * not specified are *not* allowed. Allows the definition of what should happen when a request of the provided shape is sent. + * + * For example: + * .allows() + * .method( 'GET' ) + * .endpoint().containing( 'account/xxxxx' ) + * .respondsWith() + * .body( '{some JSON}' ) + * + */ + public Amoss_CalloutMockRequestDefiner allows() { + return new Amoss_CalloutMockRequestDefiner( instance.allows( 'respond' ) ); + } + + /** + * States that this double does not expect any calls against it, and that if a call is made, the test should fail. + * + * Example usage: + * new Amoss_Instance() + * .isACalloutMock() + * .expectsNoCalls(); + * + */ + public void expectsNoCalls() { + instance.expectsNoCalls(); + } + + /** + * States that whatever mechanism is used to defined the 'expectations' or 'whens' against this + * object, it will either: + * * True - Allow any call to a method to be successful and for those that are not defined to return null. + * * False - Fail the test + * + * Example usage: + * new Amoss_Instance() + * .isACalloutMock() + * .allowsAnyCall( false ) + * .when() + * .method( 'GET' ) + * .endpoint().containing( 'account/xxxxx' ) + * .respondsWith() + * .body( '{some JSON}' ) + * + */ + public Amoss_CalloutMockDefiner allowsAnyCall( Boolean allowsAnyCall ) { + instance.allowsAnyCall( allowsAnyCall ); + return this; + } + + /** + * States that when a request is made against this mock, it will return a response in the described state. + * + * Example usage: + * new Amoss_Instance() + * .isACalloutMock() + * .byDefault() + * .respondsWith() + * .statusCode( 404 ) + * .status( 'Not Found' ) + * .also().when() + * .method( 'GET' ) + * .respondsWith() + * .body( '{some JSON}' ) + */ + public Amoss_CalloutMockByDefaultDefiner byDefault() { + return new Amoss_CalloutMockByDefaultDefiner( instance ); + } + } + + /** + * The part of the http mock callout grammar that allows the specification of the default behaviour when calling + * + */ + public class Amoss_CalloutMockByDefaultDefiner { + + Amoss_Instance instance; + + public Amoss_CalloutMockByDefaultDefiner( Amoss_Instance instance ) { + this.instance = instance; + } + + public Amoss_CalloutMockResponseDefiner respondsWith() { + HttpResponse response = new HttpResponse(); + instance.byDefaultMethodsReturn( response ); + return new Amoss_CalloutMockResponseDefiner( instance, response ); + } + } + + /** + * The part of the http mock callout grammar that allows the specification of the expected shape of a callout request + * + */ + public class Amoss_CalloutMockRequestDefiner { + + Amoss_NamedParametersMethodDefiner parametersDefiner; + + public Amoss_CalloutMockRequestDefiner( Amoss_NamedParametersMethodDefiner parametersDefiner ) { + this.parametersDefiner = parametersDefiner; + } + + /** + * States the expected HTTP method of the call that this specification is describing. + * + * For example: + * .when() + * .method( 'GET' ) + * .respondsWith() + * .body( '{some JSON}' ) + * + * @param String - The HTTP method that is expected to be used + */ + public Amoss_CalloutMockRequestDefiner method( String expectedHttpMethod ) { + return new Amoss_CalloutMockRequestDefiner( + parametersDefiner + .withParameterNamed( 'request' ) + .verifiedBy( new Amoss_CalloutMockHttpMethodVerifier().setVerifier( valueVerifierBuilder.buildSameValueAsVerifier( expectedHttpMethod ) ) ) + ); + } + + /** + * States that the given header has the specified expected properties. + * Is immediately followed by a specification of the expected header shape. + * + * For example: + * .when() + * .method( 'GET' ) + * .header( 'Authorisation' ).set() + * .header( 'SessionId' ).matches( '[0-9a-z]*' ) + * .respondsWith() + * .body( '{some JSON}' ) + * + * @param String - The key of the header that is to be specified + */ + public Amoss_CalloutMockRequestHeaderValueDefiner header( String expectedHeaderKey ) { + return new Amoss_CalloutMockRequestHeaderValueDefiner( parametersDefiner, expectedHeaderKey ); + } + + /** + * States that the call's endpoint is expected to be the same value as the given string. + * + * For example: + * .when() + * .method( 'GET' ) + * .endpoint( 'https://api.example.com/account/xxxxxxx' ) + * .respondsWith() + * .body( '{some JSON}' ) + * + * @param String - The endpoint that is expected to be called + */ + public Amoss_CalloutMockRequestDefiner endpoint( String expectedEndpoint ) { + return endpoint().setTo( expectedEndpoint ); + } + + /** + * States that the call's endpoint is expected to have the specified properties. + * Is immediately followed by a specification of the expected endpoint shape. + * + * Multiple specifications can be provided. + * + * For example: + * .when() + * .method( 'GET' ) + * .endpoint().contains( 'https://' ) + * .endpoint().contains( '/account/xxxxxxx' ) + * .respondsWith() + * .body( '{some JSON}' ) + * + */ + public Amoss_CalloutMockRequestEndpointValueDefiner endpoint() { + return new Amoss_CalloutMockRequestEndpointValueDefiner( parametersDefiner ); + } + + /** + * States that the call's body is expected to be the same value as the given string. + * + * For example: + * .when() + * .method( 'GET' ) + * .body( '{some JSON}' ) + * .respondsWith() + * .body( '{some other JSON}' ) + * + * @param String - The String body that is expected to be passed + */ + public Amoss_CalloutMockRequestDefiner body( String expectedBody ) { + return body().setTo( expectedBody ); + } + + /** + * States that the call's body is expected to have the specified properties. + * Is immediately followed by a specification of the expected body shape. + * + * Multiple specifications can be provided. + * + * For example: + * .when() + * .method( 'GET' ) + * .body.contains( '"param1":"value1"' ) + * .body.contains( '"param2":"value2"' ) + * .respondsWith() + * .body( '{some other JSON}' ) + * + */ + public Amoss_CalloutMockRequestBodyValueDefiner body() { + return new Amoss_CalloutMockRequestBodyValueDefiner( parametersDefiner ); + } + + /** + * States that the call's request is expected to be compressed + * + * For example: + * .when() + * .method( 'POST' ) + * .compressed() + * .respondsWith() + * .body( '{some other JSON}' ) + * + */ + public Amoss_CalloutMockRequestDefiner compressed() { + return new Amoss_CalloutMockRequestDefiner( + parametersDefiner + .withParameterNamed( 'request' ) + .verifiedBy( new Amoss_CalloutMockHttpCompressedVerifier().setVerifier( valueVerifierBuilder.buildSameValueAsVerifier( true ) ) ) + ); + } + + /** + * States that the call's request is expected to be not compressed + * + * For example: + * .when() + * .method( 'POST' ) + * .notCompressed() + * .respondsWith() + * .body( '{some other JSON}' ) + * + */ + public Amoss_CalloutMockRequestDefiner notCompressed() { + return new Amoss_CalloutMockRequestDefiner( + parametersDefiner + .withParameterNamed( 'request' ) + .verifiedBy( new Amoss_CalloutMockHttpCompressedVerifier().setVerifier( valueVerifierBuilder.buildSameValueAsVerifier( false ) ) ) + ); + } + + /** + * States that the call's request should be verified by the given verifier + * + * For example: + * .when() + * .verifiedBy( customVerifier ) + * .respondsWith() + * .body( '{some other JSON}' ) + * + */ + public Amoss_CalloutMockRequestDefiner verifiedBy( Amoss_ValueVerifier customVerifier ) { + return new Amoss_CalloutMockRequestDefiner( + parametersDefiner + .withParameterNamed( 'request' ) + .verifiedBy( customVerifier ) + ); + } + + /** + * States that the call's request should be verified by the given verifier + * + * For example: + * .when() + * .verifiedBy( customVerifier ) + * .respondsWith() + * .body( '{some other JSON}' ) + * + */ + public Amoss_CalloutMockRequestDefiner verifiedBy( Amoss_HttpRequestVerifier customVerifier ) { + return new Amoss_CalloutMockRequestDefiner( + parametersDefiner + .withParameterNamed( 'request' ) + .verifiedBy( new Amoss_CalloutMockHttpRequestVerifierAdaptor().setRequestVerifier( customVerifier ) ) + ); + } + + /** + * Switches the mode of the specification over to defining how the call will respond + * + * For example: + * .when() + * .method( 'POST' ) + * .respondsWith() + * .body( '{some other JSON}' ) + * + */ + public Amoss_CalloutMockResponseDefiner respondsWith() { + HttpResponse response = new HttpResponse(); + parametersDefiner.returns( response ); + return new Amoss_CalloutMockResponseDefiner( parametersDefiner, response ); + } + + /** + * Defines the object that should be used to handle the call made in the specification, defining the + * response that will be generated. + * + * Can be passed an implementation of any of the following: + * * Amoss_MethodHandler + * * StubProvider + * * HttpCalloutMock + * + * For example: + * .when() + * .method( 'POST' ) + * .handledBy( new CustomRequestHandler() ) + */ + public Amoss_CalloutMockNextExpectationDefiner handledBy( Amoss_MethodHandler returnHandler ) { + return new Amoss_CalloutMockNextExpectationDefiner( + parametersDefiner.handledBy( returnHandler ) + ); + } + + /** + * Defines the object that should be used to handle the call made in the specification, defining the + * response that will be generated. + * + * Can be passed an implementation of any of the following: + * * Amoss_MethodHandler + * * StubProvider + * * HttpCalloutMock + * + * For example: + * .when() + * .method( 'POST' ) + * .handledBy( new CustomRequestHandler() ) + */ + public Amoss_CalloutMockNextExpectationDefiner handledBy( StubProvider returnHandler ) { + return new Amoss_CalloutMockNextExpectationDefiner( + parametersDefiner.handledBy( returnHandler ) + ); + } + + /** + * Defines the object that should be used to handle the call made in the specification, defining the + * response that will be generated. + * + * Can be passed an implementation of any of the following: + * * Amoss_MethodHandler + * * StubProvider + * * HttpCalloutMock + * + * For example: + * .when() + * .method( 'POST' ) + * .handledBy( new CustomRequestHandler() ) + */ + public Amoss_CalloutMockNextExpectationDefiner handledBy( HttpCalloutMock returnHandler ) { + return new Amoss_CalloutMockNextExpectationDefiner( + parametersDefiner.handledBy( new Amoss_CalloutMockHttpHandlerAdaptor( returnHandler ) ) + ); + } + + /** + * States that when this 'expectation' or 'when' is met, the call show throw the given exception. + * + * For example, to specify: + * .when() + * .method( 'GET' ) + * .throws( new CalloutException( 'Unable to tunnel through proxy. Proxy returns "HTTP/1.1 503 Service Unavailable"' ) ); + * + * Has 'throwing' and 'willThrow' as synonyms, and these methods are entirely interchangeable based on preference. + * + * @param Exception - The Exception to throw when this expectation is met. + */ + public Amoss_CalloutMockNextExpectationDefiner throws( Exception exceptionToThrow ) { + return new Amoss_CalloutMockNextExpectationDefiner( + parametersDefiner.throws( exceptionToThrow ) + ); + } + + /** + * States that when this 'expectation' or 'when' is met, the call show throw the given exception. + * + * For example, to specify: + * .when() + * .method( 'GET' ) + * .willThrow( new CalloutException( 'Unable to tunnel through proxy. Proxy returns "HTTP/1.1 503 Service Unavailable"' ) ); + * + * Has 'throws' and 'throwing' as synonyms, and these methods are entirely interchangeable based on preference. + * + * @param Exception - The Exception to throw when this expectation is met. + */ + public Amoss_CalloutMockNextExpectationDefiner willThrow( Exception exceptionToThrow ) { + return throws( exceptionToThrow ); + } + + /** + * States that when this 'expectation' or 'when' is met, the call show throw the given exception. + * + * For example, to specify: + * .expects() + * .method( 'GET' ) + * .throwing( new CalloutException( 'Unable to tunnel through proxy. Proxy returns "HTTP/1.1 503 Service Unavailable"' ) ); + * + * Has 'throws' and 'willThrow' as synonyms, and these methods are entirely interchangeable based on preference. + * + * @param Exception - The Exception to throw when this expectation is met. + */ + public Amoss_CalloutMockNextExpectationDefiner throwing( Exception exceptionToThrow ) { + return throws( exceptionToThrow ); + } + } + + /** + * The part of the http mock callout grammar that allows the specification of the expected shape of a string property. + * For example, a header. + * + * Is sub-classed for particular properties, so that the verifier used to specify the + * property being checked can be specified. + */ + public abstract class Amoss_CalloutMockRequestStringPropertyDefiner { + + Amoss_NamedParametersMethodDefiner parametersDefiner; + Amoss_CalloutMockHttpHeaderVerifier verifier; + + public Amoss_CalloutMockRequestStringPropertyDefiner( Amoss_NamedParametersMethodDefiner parametersDefiner ) { + this.parametersDefiner = parametersDefiner; + } + + private Amoss_CalloutMockRequestDefiner define( Amoss_ValueVerifier propertyVerifier ) { + return new Amoss_CalloutMockRequestDefiner( + parametersDefiner + .withParameterNamed( 'request' ) + .verifiedBy( buildBaseVerifier().setVerifier( propertyVerifier ) ) + ); + } + + protected abstract Amoss_CalloutMockRequestVerifier buildBaseVerifier(); + + /** + * States that the specified property should be 'set' (meaning not empty) in order to match the specified expectation. + * + * For example, to specify: + * .expects() + * .endpoint().set() + */ + public Amoss_CalloutMockRequestDefiner set() { + return define( valueVerifierBuilder.buildNotEmptyStringVerifier() ); + } + + /** + * States the value that the specified property should be 'setTo' in order to match the specified expectation. + * + * For example, to specify: + * .expects() + * .method( 'GET' ) + * .endpoint().setTo( 'https://api.example.com/account/12345' ) + * + * @param String - The value that the property is expected to be + */ + public Amoss_CalloutMockRequestDefiner setTo( String expectedValue ) { + return define( valueVerifierBuilder.buildSameValueAsVerifier( expectedValue ) ); + } + + /** + * States a value that the specified property should 'contain' in order to match the specified expectation. + * + * For example, to specify: + * .expects() + * .method( 'GET' ) + * .endpoint().containing( '/account/12345' ) + * + * @param String - The value that the property is expected to contain + */ + public Amoss_CalloutMockRequestDefiner containing( String expectedValue ) { + return define( valueVerifierBuilder.buildContainingStringVerifier( expectedValue ) ); + } + + /** + * States a regular expression that the specified property should 'match' in order to match the specified expectation. + * + * Note, in line with Apex and Java's regular expression 'match' method, the *whole* of the property is required to match. + * + * For example, to specify: + * .expects() + * .method( 'GET' ) + * .endpoint().matching( 'http.*account/[0-9]*' ) + * + * @param String - The regular expression that the property is expected to match + */ + public Amoss_CalloutMockRequestDefiner matching( String expectedValue ) { + return define( valueVerifierBuilder.buildMatchingExpressionVerifier( expectedValue ) ); + } + } + + /** + * The part of the http mock callout grammar that allows the specification of the expected shape of a given request's header + * + * Is sub-classed for particular properties, so that the verifier used to specify the + * property being checked can be specified. + */ + public class Amoss_CalloutMockRequestHeaderValueDefiner extends Amoss_CalloutMockRequestStringPropertyDefiner { + + String expectedHeaderKey; + + public Amoss_CalloutMockRequestHeaderValueDefiner( Amoss_NamedParametersMethodDefiner parametersDefiner, String expectedHeaderKey ) { + super( parametersDefiner ); + this.expectedHeaderKey = expectedHeaderKey; + } + + /** + * Builds the verifier that is used to verify the Header on an HttpRequest object + */ + protected override Amoss_CalloutMockRequestVerifier buildBaseVerifier() { + return new Amoss_CalloutMockHttpHeaderVerifier( expectedHeaderKey ); + } + } + + /** + * The part of the http mock callout grammar that allows the specification of the expected shape of a given request's endpoint + * + * Is sub-classed for particular properties, so that the verifier used to specify the + * property being checked can be specified. + */ + public class Amoss_CalloutMockRequestEndpointValueDefiner extends Amoss_CalloutMockRequestStringPropertyDefiner { + + public Amoss_CalloutMockRequestEndpointValueDefiner( Amoss_NamedParametersMethodDefiner parametersDefiner ) { + super( parametersDefiner ); + } + + /** + * Builds the verifier that is used to verify the Endpoint on an HttpRequest object + */ + protected override Amoss_CalloutMockRequestVerifier buildBaseVerifier() { + return new Amoss_CalloutMockHttpEndpointVerifier(); + } + } + + /** + * The part of the http mock callout grammar that allows the specification of the expected shape of a given request's body + * + * Is sub-classed for particular properties, so that the verifier used to specify the + * property being checked can be specified. + */ + public class Amoss_CalloutMockRequestBodyValueDefiner extends Amoss_CalloutMockRequestStringPropertyDefiner { + + public Amoss_CalloutMockRequestBodyValueDefiner( Amoss_NamedParametersMethodDefiner parametersDefiner ) { + super( parametersDefiner ); + } + + /** + * Builds the verifier that is used to verify the Body on an HttpRequest object + */ + protected override Amoss_CalloutMockRequestVerifier buildBaseVerifier() { + return new Amoss_CalloutMockHttpBodyVerifier(); + } + } + + /** + * The part of the http mock callout grammar that allows the specification that another expectation is about to be defined. + * + * Is sub-classed so it can be used at different points of the overall grammar + */ + public virtual class Amoss_CalloutMockNextExpectationDefiner { + + protected Amoss_NextExpectationDefiner nextExpectationDefiner; + + public Amoss_CalloutMockNextExpectationDefiner( Amoss_NextExpectationDefiner nextExpectationDefiner ) { + this.nextExpectationDefiner = nextExpectationDefiner; + } + + /** + * Allows another 'expectation' or 'when' to be defined against the Http Callout Mock + * + * Is actually synonymous with 'also'. + * + * For example: + * .then().expects() + * .method( 'GET' ) + */ + public Amoss_CalloutMockDefiner then() { + return new Amoss_CalloutMockDefiner( nextExpectationDefiner.then() ); + } + + /** + * Allows another 'expectation' or 'when' to be defined against the Http Callout Mock + * + * Is actually synonymous with 'then'. + * + * For example: + * .also().expects() + * .method( 'GET' ) + */ + public Amoss_CalloutMockDefiner also() { + return then(); + } + } + + /** + * The part of the http mock callout grammar that allows the specification of the response that is to be returned. + */ + public class Amoss_CalloutMockResponseDefiner { + + HttpResponse response; + Object returnsDefiner; + + public Amoss_CalloutMockResponseDefiner( Object returnsDefiner, HttpResponse response ) { + this.returnsDefiner = returnsDefiner; + this.response = response; + } + + /** + * Allows another 'expectation' or 'when' to be defined against the Http Callout Mock + * + * Is actually synonymous with 'also'. + * + * For example: + * .then().expects() + * .method( 'GET' ) + */ + public Amoss_CalloutMockDefiner then() { + Amoss_Instance nextDefiner; + // TODO: should be cleaned up - probably a class structure + if ( returnsDefiner instanceOf Amoss_Instance ) { + nextDefiner = (Amoss_Instance)returnsDefiner; + } else { + nextDefiner = ((Amoss_Thenable)returnsDefiner).then(); + } + return new Amoss_CalloutMockDefiner( nextDefiner ); + } + + /** + * Allows another 'expectation' or 'when' to be defined against the Http Callout Mock + * + * Is actually synonymous with 'then'. + * + * For example: + * .also().expects() + * .method( 'GET' ) + */ + public Amoss_CalloutMockDefiner also() { + return then(); + } + + /** + * Specifies the status code that will be returned on the HttpResponse when the current expectation is matched. + * + * For example: + * .when() + * .method( 'GET' ) + * .respondsWith() + * .statusCode( 404 ) + */ + public Amoss_CalloutMockResponseDefiner statusCode( Integer statusCode ) { + response.setStatusCode( statusCode ); + return this; + } + + /** + * Specifies the status that will be returned on the HttpResponse when the current expectation is matched. + * + * For example: + * .when() + * .method( 'GET' ) + * .respondsWith() + * .status( 'Complete' ) + */ + public Amoss_CalloutMockResponseDefiner status( String status ) { + response.setStatus( status ); + return this; + } + + /** + * Specifies the body that will be returned on the HttpResponse when the current expectation is matched. + * + * For example: + * .when() + * .method( 'GET' ) + * .respondsWith() + * .body( 'the body' ) + */ + public Amoss_CalloutMockResponseDefiner body( String body ) { + response.setBody( body ); + return this; + } + + /** + * Specifies the blob value of the body that will be returned on the HttpResponse when the current expectation is matched. + * + * For example: + * .when() + * .method( 'GET' ) + * .respondsWith() + * .body( blobValue ) + */ + public Amoss_CalloutMockResponseDefiner body( Blob body ) { + response.setBodyAsBlob( body ); + return this; + } + + /** + * Specifies the body that will be returned on the HttpResponse when the current expectation is matched. + * + * The body will be JSON serialized at the point the method is called. + * + * For example: + * .when() + * .method( 'GET' ) + * .respondsWith() + * .body( objectValue ) + */ + public Amoss_CalloutMockResponseDefiner body( Object body ) { + response.setBody( JSON.serialize( body ) ); + return this; + } + + /** + * Specifies a header that will be set on the HttpResponse when the current expectation is matched. + * + * For example: + * .when() + * .method( 'GET' ) + * .respondsWith() + * .header( 'Authorised' ).setTo( 'true' ) + */ + public Amoss_CalloutMockResponseHeaderDefiner header( String key ) { + return new Amoss_CalloutMockResponseHeaderDefiner( this, response, key ); + } + } + + /** + * The part of the http mock callout grammar that allows the specification of a header on the response that is to be returned. + */ + public class Amoss_CalloutMockResponseHeaderDefiner { + + String headerKey; + HttpResponse response; + Amoss_CalloutMockResponseDefiner responseDefiner; + + public Amoss_CalloutMockResponseHeaderDefiner( Amoss_CalloutMockResponseDefiner responseDefiner, HttpResponse response, String headerKey ) { + this.headerKey = headerKey; + this.response = response; + this.responseDefiner = responseDefiner; + } + + /** + * Specifies a value that the specified header will be set on the HttpResponse when the current expectation is matched. + * + * For example: + * .when() + * .method( 'GET' ) + * .respondsWith() + * .header( 'Authorised' ).setTo( 'true' ) + */ + public Amoss_CalloutMockResponseDefiner setTo( String headerValue ) { + response.setHeader( headerKey, headerValue ); + return responseDefiner; + } + } + + /** + * Verifier that can be used to check the properties of an HttpRequest object. + * + * Verification of the property is actually performed by a verifer that is set as a member variable, allowing for + * any type of verification to be performed (e.g. set / setTo / contains ) + * + * Is subclassed, with the method 'verifyValue' overridden in order to retrieve the correct property from the request. + */ + private abstract class Amoss_CalloutMockRequestVerifier implements Amoss_ValueVerifier { + + protected Amoss_ValueVerifier verifier; + + public Amoss_CalloutMockRequestVerifier setVerifier( Amoss_ValueVerifier verifier ) { + this.verifier = verifier; + return this; + } + + protected abstract void verifyValue( HttpRequest actualRequest ); + + public void verify( Object actualValue ) { + + if ( ! (actualValue instanceOf HttpRequest) ) { + throw new Amoss_EqualsAssertionFailureException() + .setExpected( 'HttpRequest' ) + .setActual( Amoss_Asserts.getType( actualValue ) ) + .setAssertionMessage( 'The value is not what was expected - The types do not match' ); + } + + verifyValue( (HttpRequest)actualValue ); + } + } + + /** + * An adaptor for Amoss_HttpRequestVerifier that allows it to operate as an Amoss_ValueVerifier + */ + private class Amoss_CalloutMockHttpRequestVerifierAdaptor extends Amoss_CalloutMockRequestVerifier { + + protected Amoss_HttpRequestVerifier requestVerifier; + + public Amoss_CalloutMockHttpRequestVerifierAdaptor setRequestVerifier( Amoss_HttpRequestVerifier requestVerifier ) { + this.requestVerifier = requestVerifier; + return this; + } + + public override String toString() { + return requestVerifier.toString(); + } + + protected override void verifyValue( HttpRequest actualRequest ) { + requestVerifier.verify( actualRequest ); + } + } + + /** + * Verifier that can be used to check the properties of the method on an HttpRequest object. + * + * Verification of the property is actually performed by a verifer that is set as a member variable, allowing for + * any type of verification to be performed (e.g. set / setTo / contains ) + * + */ + private class Amoss_CalloutMockHttpMethodVerifier extends Amoss_CalloutMockRequestVerifier { + + public override String toString() { + return 'HttpRequest with the method ' + verifier.toString(); + } + + protected override void verifyValue( HttpRequest actualRequest ) { + try { + verifier.verify( actualRequest.getMethod() ); + } catch ( Amoss_AssertionFailureException e ) { + throw e.addContextToMessage( 'Method is not what was expected: {0}' ); + } + } + } + + /** + * Verifier that can be used to check the properties of the endpoint on an HttpRequest object. + * + * Verification of the property is actually performed by a verifer that is set as a member variable, allowing for + * any type of verification to be performed (e.g. set / setTo / contains ) + * + */ + private class Amoss_CalloutMockHttpEndpointVerifier extends Amoss_CalloutMockRequestVerifier { + + public override String toString() { + return 'HttpRequest with the endpoint ' + verifier.toString(); + } + + protected override void verifyValue( HttpRequest actualRequest ) { + try { + verifier.verify( actualRequest.getEndpoint() ); + } catch ( Amoss_AssertionFailureException e ) { + throw e.addContextToMessage( 'Endpoint is not what was expected: {0}' ); + } + } + } + + /** + * Verifier that can be used to check the properties of the body on an HttpRequest object. + * + * Verification of the property is actually performed by a verifer that is set as a member variable, allowing for + * any type of verification to be performed (e.g. set / setTo / contains ) + * + */ + private class Amoss_CalloutMockHttpBodyVerifier extends Amoss_CalloutMockRequestVerifier { + + public override String toString() { + return 'HttpRequest with the body ' + verifier.toString(); + } + + protected override void verifyValue( HttpRequest actualRequest ) { + try { + verifier.verify( actualRequest.getBody() ); + } catch ( Amoss_AssertionFailureException e ) { + throw e.addContextToMessage( 'Body is not what was expected: {0}' ); + } + } + } + + /** + * Verifier that can be used to check the properties of a header on an HttpRequest object. + * + * Verification of the property is actually performed by a verifer that is set as a member variable, allowing for + * any type of verification to be performed (e.g. set / setTo / contains ) + * + */ + private class Amoss_CalloutMockHttpHeaderVerifier extends Amoss_CalloutMockRequestVerifier { + + String headerKey; + + public Amoss_CalloutMockHttpHeaderVerifier( String headerKey ) { + this.headerKey = headerKey; + } + + public override String toString() { + return 'HttpRequest with the header "' + headerKey + '" ' + verifier.toString(); + } + + protected override void verifyValue( HttpRequest actualRequest ) { + try { + verifier.verify( actualRequest.getHeader( headerKey ) ); + } catch ( Amoss_AssertionFailureException e ) { + throw e.addContextToMessage( 'Header "' + headerKey + '" is not what was expected: {0}' ); + } + } + } + + /** + * Verifier that can be used to check the properties of the 'compressed' property on an HttpRequest object. + * + * Verification of the property is actually performed by a verifer that is set as a member variable, allowing for + * any type of verification to be performed (e.g. set / setTo / contains ) + * + */ + private class Amoss_CalloutMockHttpCompressedVerifier extends Amoss_CalloutMockRequestVerifier { + + public override String toString() { + return 'HttpRequest with the compressed ' + verifier.toString(); + } + + protected override void verifyValue( HttpRequest actualRequest ) { + try { + verifier.verify( actualRequest.getCompressed() ); + } catch ( Amoss_AssertionFailureException e ) { + throw e.addContextToMessage( 'Compressed is not what was expected: {0}' ); + } + } + } + + /** + * An adaptor for making the HttpCalloutMock compatible with Amoss as an Amoss_MethodHandler + * + */ + private class Amoss_CalloutMockHttpHandlerAdaptor implements Amoss_MethodHandler { + + HttpCalloutMock standardHandler; + + public Amoss_CalloutMockHttpHandlerAdaptor( HttpCalloutMock standardHandler ) { + this.standardHandler = standardHandler; + } + + public Object handleMethodCall( List parameters ) { + + // I'm really not sure how any of these error conditions could ever occur. + // I certainly can't think of a way of unit testing them without making the class public + // or testVisible, neither of which do I think is appropriate. + // That said, it seems that this is a sensible chunk of checking, just in case I've missed + // something. + if ( parameters.size() != 1 ) { + throw new Amoss_EqualsAssertionFailureException() + .setExpected( '1 parameter, being an HttpRequest' ) + .setActual( parameters ) + .setAssertionMessage( 'HttpCalloutMock handler was called with the wrong number of parameters' ); + } + + if ( parameters[0] == null ) { + throw new Amoss_EqualsAssertionFailureException() + .setExpected( 'HttpRequest' ) + .setActual( 'null' ) + .setAssertionMessage( 'HttpCalloutMock handler was called with a null first parameter' ); + } + + if ( ! ( parameters[0] instanceOf HttpRequest ) ) { + throw new Amoss_EqualsAssertionFailureException() + .setExpected( 'HttpRequest' ) + .setActual( parameters[0] ) + .setAssertionMessage( 'HttpCalloutMock handler was called with an object that was not an HttpCalloutMock' ); + } + + return standardHandler.respond( (HttpRequest)parameters[0] ); + } + } +} \ No newline at end of file diff --git a/framework/default/amoss_main/default/classes/Amoss_Instance.cls-meta.xml b/framework/default/amoss_main/default/classes/Amoss_Instance.cls-meta.xml new file mode 100644 index 00000000000..dd61d1f917e --- /dev/null +++ b/framework/default/amoss_main/default/classes/Amoss_Instance.cls-meta.xml @@ -0,0 +1,5 @@ + + + 52.0 + Active + diff --git a/framework/default/amoss_main/default/classes/Amoss_MethodHandler.cls b/framework/default/amoss_main/default/classes/Amoss_MethodHandler.cls new file mode 100644 index 00000000000..9817610b750 --- /dev/null +++ b/framework/default/amoss_main/default/classes/Amoss_MethodHandler.cls @@ -0,0 +1,34 @@ +/* +MIT License + +Copyright (c) 2020 Robert Baillie + +https://github.com/bobalicious/amoss + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +/** +* Public interface for the specification of simple method handlers, for use by handledBy. +* +* The standard Salesforce StubProvider interface can be used if something more substantial is required. +*/ +public interface Amoss_MethodHandler { + Object handleMethodCall( List parameters ); +} \ No newline at end of file diff --git a/framework/default/amoss_main/default/classes/Amoss_MethodHandler.cls-meta.xml b/framework/default/amoss_main/default/classes/Amoss_MethodHandler.cls-meta.xml new file mode 100644 index 00000000000..dd61d1f917e --- /dev/null +++ b/framework/default/amoss_main/default/classes/Amoss_MethodHandler.cls-meta.xml @@ -0,0 +1,5 @@ + + + 52.0 + Active + diff --git a/framework/default/amoss_main/default/classes/Amoss_ValueVerifier.cls b/framework/default/amoss_main/default/classes/Amoss_ValueVerifier.cls new file mode 100644 index 00000000000..d5900c1585a --- /dev/null +++ b/framework/default/amoss_main/default/classes/Amoss_ValueVerifier.cls @@ -0,0 +1,72 @@ +/* +MIT License + +Copyright (c) 2020 Robert Baillie + +https://github.com/bobalicious/amoss + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +/** +* Public interface for the specification of a mechanism for checking the value of a parameter. +* +* Can be used to extend the functionality of the core Amoss framework and allow for complex parameter checking +* to take place that is specific to a particular system's implementation. +* +* Of particular use when trying to verify the values of parameters that contains Maps and Sets, since the casting rules makes +* the implementation of generic verification methods difficult. +* +* However, it is generally not required to create custom verifiers. +*/ +public interface Amoss_ValueVerifier { + + /** + * Describes the verification that this object to configured to make in a way that will be easy to + * interpret when reported in a failed assertion message. + * + * @return String - A clear description of the value's verification + */ + String toString(); + + /** + * Should check that the given value (the parameter, or fragment of the parameter) passes verification, reporting any + * failures by throwing an exception of the following types: + * * Amoss_Instance.Amoss_AssertionFailureException + * * Amoss_Instance.Amoss_EqualsAssertionFailureException + * + * In both cases, setAssertionMessage should be called to clearly define the failure. + * + * When using Amoss_EqualsAssertionFailureException, setExpected and setActual should also be set, with the values + * being relevant within the context of the stated assertionMessage. + * + * If other verifiers are used within a custom verifier, any Amoss_AssertionFailureExceptions can be caught and + * have context added to the failure by calling addContextToMessage against the exception before re-throwing. + * + * Care should be taken to ensure that no exceptions other than Amoss_AssertionFailureExceptions and its subclasses are + * thrown. This ensures that failures are clearly reported to the user. + * + * In addition, no calls to System.assert or its variations should be made directly in this method as it is also called + * without the intention of reporting assertion failures to the user, and unexpected behaviours may result, particularly when using + * the 'when' and 'allows' syntax. + * + * @param Object - The value to verify + */ + void verify( Object value ); +} diff --git a/framework/default/amoss_main/default/classes/Amoss_ValueVerifier.cls-meta.xml b/framework/default/amoss_main/default/classes/Amoss_ValueVerifier.cls-meta.xml new file mode 100644 index 00000000000..dd61d1f917e --- /dev/null +++ b/framework/default/amoss_main/default/classes/Amoss_ValueVerifier.cls-meta.xml @@ -0,0 +1,5 @@ + + + 52.0 + Active + diff --git a/framework/default/amoss_tests/default/classes/AmossTest_Asserts.cls b/framework/default/amoss_tests/default/classes/AmossTest_Asserts.cls new file mode 100644 index 00000000000..cd903ba44b1 --- /dev/null +++ b/framework/default/amoss_tests/default/classes/AmossTest_Asserts.cls @@ -0,0 +1,265 @@ +/* +MIT License + +Copyright (c) 2020 Robert Baillie + +https://github.com/bobalicious/amoss + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +@isTest +public with sharing class AmossTest_Asserts { + + /** + * @method getType + * @case when given a parameter + * @result will return a String representing the type of that parameter + */ + @isTest + private static void getType_whenGivenAParameter_willReturnTheStringTypeOfIt() { + + List> testCases = new List>{ + new Map{ + 'parameter' => true, + 'expected' => 'Boolean', + 'case' => 'a Boolean (true)' + }, + new Map{ + 'parameter' => true, + 'expected' => 'Boolean', + 'case' => 'a Boolean (false)' + }, + new Map{ + 'parameter' => (Boolean)null, + 'expected' => 'Unknown (null)', + 'case' => 'a null Boolean' + }, + new Map{ + 'parameter' => (Id)'003000000000000000', + 'expected' => 'Id', + 'case' => 'an Id' + }, + new Map{ + 'parameter' => (Id)null, + 'expected' => 'Unknown (null)', + 'case' => 'a null Id' + }, + new Map{ + 'parameter' => 'A string', + 'expected' => 'String', + 'case' => 'a String' + }, + new Map{ + 'parameter' => '', + 'expected' => 'String', + 'case' => 'an empty String' + }, + new Map{ + 'parameter' => (String)null, + 'expected' => 'Unknown (null)', + 'case' => 'a null String' + }, + new Map{ + 'parameter' => Integer.valueOf( 1 ), + 'expected' => 'Integer', + 'case' => 'an Integer' + }, + new Map{ + 'parameter' => (Integer)null, + 'expected' => 'Unknown (null)', + 'case' => 'a null Integer' + }, + new Map{ + 'parameter' => Decimal.valueOf( 1 ), + 'expected' => 'Decimal / Double', + 'case' => 'a Decimal' + }, + new Map{ + 'parameter' => (Decimal)null, + 'expected' => 'Unknown (null)', + 'case' => 'a null Decimal' + }, + new Map{ + 'parameter' => Double.valueOf( 1 ), + 'expected' => 'Decimal / Double', + 'case' => 'a Double' + }, + new Map{ + 'parameter' => (Double)null, + 'expected' => 'Unknown (null)', + 'case' => 'a null Double' + }, + new Map{ + 'parameter' => Long.valueOf( '1' ), + 'expected' => 'Long', + 'case' => 'a Long' + }, + new Map{ + 'parameter' => (Long)null, + 'expected' => 'Unknown (null)', + 'case' => 'a null Long' + }, + new Map{ + 'parameter' => Blob.valueOf( '1' ), + 'expected' => 'Blob', + 'case' => 'a Blob' + }, + new Map{ + 'parameter' => Blob.valueOf( '' ), + 'expected' => 'Blob', + 'case' => 'an empty Blob' + }, + new Map{ + 'parameter' => (Blob)null, + 'expected' => 'Unknown (null)', + 'case' => 'a null Blob' + }, + new Map{ + 'parameter' => Date.newInstance( 2020, 12, 31 ), + 'expected' => 'Date', + 'case' => 'an Date' + }, + new Map{ + 'parameter' => (Date)null, + 'expected' => 'Unknown (null)', + 'case' => 'a null Date' + }, + new Map{ + 'parameter' => DateTime.newInstance( 2020, 12, 31, 12, 00, 00 ), + 'expected' => 'DateTime', + 'case' => 'a DateTime' + }, + new Map{ + 'parameter' => DateTime.newInstance( 2020, 12, 31 ), + 'expected' => 'DateTime', + 'case' => 'a DateTime with no time component' + }, + new Map{ + 'parameter' => (DateTime)null, + 'expected' => 'Unknown (null)', + 'case' => 'a null DateTime' + }, + new Map{ + 'parameter' => Time.newInstance( 12, 00, 00, 0 ), + 'expected' => 'Time', + 'case' => 'a Time' + }, + new Map{ + 'parameter' => (Time)null, + 'expected' => 'Unknown (null)', + 'case' => 'a null Time' + }, + new Map{ + 'parameter' => new Contact(), + 'expected' => 'Contact', + 'case' => 'an sObject (Contact)' + }, + new Map{ + 'parameter' => (Contact)null, + 'expected' => 'Unknown (null)', + 'case' => 'a null sObject (Contact)' + }, + new Map{ + 'parameter' => new List(), + 'expected' => 'List', + 'case' => 'a List of sObjects (Contact)' + }, + new Map{ + 'parameter' => new List(), + 'expected' => 'List', + 'case' => 'a List of Strings' + }, + new Map{ + 'parameter' => new List(), + 'expected' => 'List', + 'case' => 'a List of Decimals' + }, + new Map{ + 'parameter' => (List)null, + 'expected' => 'Unknown (null)', + 'case' => 'a null List of sObjects (Contact)' + }, + new Map{ + 'parameter' => new Map(), + 'expected' => 'Map', + 'case' => 'a Map' + }, + new Map{ + 'parameter' => new Map(), + 'expected' => 'Map', + 'case' => 'a Map' + }, + new Map{ + 'parameter' => new Map(), + 'expected' => 'Map', + 'case' => 'a Map' + }, + new Map{ + 'parameter' => new Map(), + 'expected' => 'Map', + 'case' => 'a Map' + }, + new Map{ + 'parameter' => new Map(), + 'expected' => 'Map', + 'case' => 'a Map' + }, + new Map{ + 'parameter' => new Set(), + 'expected' => 'Set', + 'case' => 'a Set' + }, + new Map{ + 'parameter' => new Set(), + 'expected' => 'Set', + 'case' => 'a Set' + }, + new Map{ + 'parameter' => new Set(), + 'expected' => 'Set', + 'case' => 'a Set' + }, + new Map{ + 'parameter' => new List>>(), + 'expected' => 'List>>', + 'case' => 'a nested collection' + }, + new Map{ + 'parameter' => new AmossTest_ClassToDouble( 'param' ), + 'expected' => AmossTest_ClassToDouble.class.getName(), + 'case' => 'a non-system class' + }, + new Map{ + 'parameter' => new Amoss_Instance( AmossTest_ClassToDouble.class ).getDouble(), + 'expected' => AmossTest_ClassToDouble.class.getName(), + 'case' => 'a test double of a class' + } + }; + + for ( Map thisTestCase : testCases ) { + Object parameter = thisTestCase.get( 'parameter' ); + String expected = (String)thisTestCase.get( 'expected' ); + String assertion = 'getType, when passed ' + (String)thisTestCase.get( 'case' ) + ' will return its type'; + + String actual = Amoss_Asserts.getType( parameter ); + System.assertEquals( expected, actual, assertion ); + } + } +} \ No newline at end of file diff --git a/framework/default/amoss_tests/default/classes/AmossTest_Asserts.cls-meta.xml b/framework/default/amoss_tests/default/classes/AmossTest_Asserts.cls-meta.xml new file mode 100644 index 00000000000..dd61d1f917e --- /dev/null +++ b/framework/default/amoss_tests/default/classes/AmossTest_Asserts.cls-meta.xml @@ -0,0 +1,5 @@ + + + 52.0 + Active + diff --git a/framework/default/amoss_tests/default/classes/AmossTest_ClassToDouble.cls b/framework/default/amoss_tests/default/classes/AmossTest_ClassToDouble.cls new file mode 100644 index 00000000000..4dbfd688ea6 --- /dev/null +++ b/framework/default/amoss_tests/default/classes/AmossTest_ClassToDouble.cls @@ -0,0 +1,104 @@ +/* +MIT License + +Copyright (c) 2020 Robert Baillie + +https://github.com/bobalicious/amoss + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +/** + * + * Amoss test class that exists purely so that AmossTest_InstanceTest has a guaranteed class + * that it can create a Test Double for. + * + * If StubProvider supported the stubbing of internal classes, then this would be part of + * Amos_InstanceTest. Until it does, it will need to stay here. + * + */ +@isTest +public with sharing class AmossTest_ClassToDouble { + + // Non-empty parameter constructor defined to prove that + // such classes can have a Test Double generated OK + public AmossTest_ClassToDouble( String parameter1 ) { + } + + public String methodUnderDouble( String parameter1, Integer parameter2 ) { + return 'TheReturn'; + } + public String overloadedMethodUnderDouble( String parameter1, String parameter2 ) { + return 'TheReturn'; + } + public String overloadedMethodUnderDouble( String parameter1, Integer parameter2 ) { + return 'TheReturn'; + } + public String overloadedMethodUnderDouble( String parameter1, String parameter2, String parameter3 ) { + return 'TheReturn'; + } + public void methodUnderDoubleWithNoReturn( String parameter1, Integer parameter2 ) { + } + public String methodWithNoParametersUnderDouble() { + return 'TheReturn'; + } + public String otherMethodUnderDouble( String parameter1, Integer parameter2 ) { + return 'TheReturn'; + } + public String sobjectMethodUnderDouble( Contact contactParam ) { + return 'TheReturn'; + } + public String sobjectMethodUnderDouble( Contact contactParam, Account accountParam ) { + return 'TheReturn'; + } + public String sobjectMethodUnderDouble( String notAnSobject ) { + return 'TheReturn'; + } + public String objectMethodUnderDouble( Object objectParam ) { + return 'TheReturn'; + } + public String objectMethodUnderDouble( String notAnObject ) { + return 'TheReturn'; + } + public List methodWithListOfObjects( List parameter1 ) { + return new List(); + } + public Set methodWithSetOfObjects( Set parameter1 ) { + return new Set(); + } + public Map methodWithMapStringToObject( Map parameter1 ) { + return new Map(); + } + public Map methodWithMapIdToObject( Map parameter1 ) { + return new Map(); + } + public Map methodWithMapDateToObject( Map parameter1 ) { + return new Map(); + } + public AmossTest_ClassToDouble fluentMethod() { + return this; + } + + public class PassableObject { + String property; + public PassableObject( String property ) { + this.property = property; + } + } +} \ No newline at end of file diff --git a/framework/default/amoss_tests/default/classes/AmossTest_ClassToDouble.cls-meta.xml b/framework/default/amoss_tests/default/classes/AmossTest_ClassToDouble.cls-meta.xml new file mode 100644 index 00000000000..dd61d1f917e --- /dev/null +++ b/framework/default/amoss_tests/default/classes/AmossTest_ClassToDouble.cls-meta.xml @@ -0,0 +1,5 @@ + + + 52.0 + Active + diff --git a/framework/default/amoss_tests/default/classes/AmossTest_HttpCalloutMock.cls b/framework/default/amoss_tests/default/classes/AmossTest_HttpCalloutMock.cls new file mode 100644 index 00000000000..3add60abd4a --- /dev/null +++ b/framework/default/amoss_tests/default/classes/AmossTest_HttpCalloutMock.cls @@ -0,0 +1,16 @@ +@isTest +public with sharing class AmossTest_HttpCalloutMock { + /** + * @method respond + * @result will do nothing + */ + @isTest + private static void respond_willDoNothing() { + + Test.startTest(); + new Amoss_HttpCalloutMock().respond( null ); + Test.stopTest(); + + System.assert( true, 'respond, when always, will do nothing' ); + } +} diff --git a/framework/default/amoss_tests/default/classes/AmossTest_HttpCalloutMock.cls-meta.xml b/framework/default/amoss_tests/default/classes/AmossTest_HttpCalloutMock.cls-meta.xml new file mode 100644 index 00000000000..dd61d1f917e --- /dev/null +++ b/framework/default/amoss_tests/default/classes/AmossTest_HttpCalloutMock.cls-meta.xml @@ -0,0 +1,5 @@ + + + 52.0 + Active + diff --git a/framework/default/amoss_tests/default/classes/AmossTest_InstanceTest.cls b/framework/default/amoss_tests/default/classes/AmossTest_InstanceTest.cls new file mode 100644 index 00000000000..0fee395806b --- /dev/null +++ b/framework/default/amoss_tests/default/classes/AmossTest_InstanceTest.cls @@ -0,0 +1,11972 @@ +/* +MIT License + +Copyright (c) 2020 Robert Baillie + +https://github.com/bobalicious/amoss + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ +@SuppressWarnings( 'MethodNamingConventions, EmptyCatchBlock' ) +@isTest +public with sharing class AmossTest_InstanceTest { + + class TestException extends Exception {} + + private static final String CLASS_TO_DOUBLE = AmossTest_ClassToDouble.class.getName(); + private static final String HTTP_CALLOUT_MOCK_UNEXPECTED_EXCEPTION_TEXT = 'More HTTP Callouts were made than expected, and no matching "when" or "allows" exists.'; + + // + // Method definition tests + // + /** + * @case when no methods are defined, and a method is called + * @result will return null + */ + @isTest + private static void whenNoMethodsAreDefinedAndAMethodIsCalled_returnNull() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Test.startTest(); + String returnFromDouble = classToDouble.methodUnderDouble( 'OtherActualParam1', 1 ); + Test.stopTest(); + + System.assertEquals( null, returnFromDouble, 'when no methods are defined, and a method called, will return null' ); + } + + /** + * @case for an interface, when no methods are defined, and a method is called + * @result will return null + */ + @isTest + private static void forAnInterface_whenNoMethodsAreDefinedAndAMethodIsCalled_returnNull() { + + Amoss_Instance interfaceUnderDoubleController = new Amoss_Instance( AmossTest_InterfaceToDouble.class ); + AmossTest_InterfaceToDouble interfaceUnderDouble = (AmossTest_InterfaceToDouble)interfaceUnderDoubleController.getDouble(); + + Test.startTest(); + String returnFromDouble = interfaceUnderDouble.methodUnderDouble( 'OtherActualParam1', 1 ); + Test.stopTest(); + + System.assertEquals( null, returnFromDouble, 'when no methods are defined, and a method called, will return null' ); + } + + /** + * @method when.method.willReturn + * @case when that method is called + * @result will return the stated value + */ + @isTest + private static void whenMethodWillReturn_whenCalled_returnSetUpValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .when() + .method( 'methodUnderDouble' ) + .willReturn( 'expectedReturn' ); + + Test.startTest(); + String returnFromDouble = classToDouble.methodUnderDouble( 'OtherActualParam1', 1 ); + Test.stopTest(); + + System.assertEquals( 'expectedReturn', returnFromDouble, 'when.method.willReturn, and the method called, will return the stated value, regardless of the parameters passed' ); + } + + /** + * @method when.method.willReturn (shortcut version) + * @case when that method is called + * @result will return the stated value + */ + @isTest + private static void whenMethodWillReturnShortcut_whenCalled_returnSetUpValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .when( 'methodUnderDouble' ) + .willReturn( 'expectedReturn' ); + + Test.startTest(); + String returnFromDouble = classToDouble.methodUnderDouble( 'OtherActualParam1', 1 ); + Test.stopTest(); + + System.assertEquals( 'expectedReturn', returnFromDouble, 'when.method.willReturn (shortcut), and the method called, will return the stated value, regardless of the parameters passed' ); + } + + /** + * @method when.method.returns + * @case when that method is called + * @result will return the stated value + */ + @isTest + private static void whenMethodReturns_whenCalled_returnSetUpValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .when() + .method( 'methodUnderDouble' ) + .returns( 'expectedReturn' ); + + Test.startTest(); + String returnFromDouble = classToDouble.methodUnderDouble( 'OtherActualParam1', 1 ); + Test.stopTest(); + + System.assertEquals( 'expectedReturn', returnFromDouble, 'when.method.returns, and the method called, will return the stated value' ); + } + + /** + * @method when.method.returning + * @case when that method is called + * @result will return the stated value + */ + @isTest + private static void whenMethodReturning_whenCalled_returnSetUpValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .when() + .method( 'methodUnderDouble' ) + .returning( 'expectedReturn' ); + + Test.startTest(); + String returnFromDouble = classToDouble.methodUnderDouble( 'OtherActualParam1', 1 ); + Test.stopTest(); + + System.assertEquals( 'expectedReturn', returnFromDouble, 'when.method.returning, and the method called, will return the stated value' ); + } + + /** + * @method isFluent + * @case when an unspecified, fluent method is called + * @result will return the generated double + */ + @isTest + private static void isFluent_whenAnUnspecifiedFluentMethodIsCalled_returnTheGeneratedDouble() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .isFluent(); + + Test.startTest(); + AmossTest_ClassToDouble returnFromDouble = classToDouble.fluentMethod(); + Test.stopTest(); + + System.assertEquals( classToDouble, returnFromDouble, 'isFluent, when an unspecified, fluent method is called, will return the generated double' ); + } + + /** + * @method isFluent + * @case when an unspecified, non fluent method is called + * @result will throw an exception + */ + @isTest + private static void isFluent_whenAnUnspecifiedNonFluentMethodIsCalled_throwAnException() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .isFluent(); + + Boolean exceptionThrown = false; + String exceptionMessage; + + Test.startTest(); + try { + classToDouble.methodUnderDouble( '1', 2 ); + } catch( Exception e ) { + exceptionThrown = true; + exceptionMessage = e.getMessage(); + } + Test.stopTest(); + + System.assertEquals( true, exceptionThrown, 'isFluent, when an unspecified, non fluent method is called, will throw an exception' ); + System.assertEquals( 'Invalid conversion from runtime type ' + CLASS_TO_DOUBLE + ' to String', exceptionMessage, 'isFluent, when an unspecified, non fluent method is called, will throw an exception' ); + } + + /** + * @method isFluent + * @case when an unspecified, method with no return is called + * @result will not throw an exception + */ + @isTest + private static void isFluent_whenAnUnspecifiedMethodWithNoReturnIsCalled_notThrowAnException() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .isFluent(); + + Boolean exceptionThrown = false; + String exceptionMessage; + + Test.startTest(); + try { + classToDouble.methodUnderDoubleWithNoReturn( '1', 2 ); + } catch( Exception e ) { + exceptionThrown = true; + exceptionMessage = e.getMessage(); + } + Test.stopTest(); + + System.assertEquals( false, exceptionThrown, 'isFluent, when an unspecified, method with no return is called, will not throw an exception' ); + } + + /** + * @method isFluent + * @case when a specified method with no defined return is called + * @result will return null + */ + @isTest + private static void isFluent_whenAnSpecifiedMethodWithNoDefinedReturnIsCalled_returnNull() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .isFluent() + .when( 'fluentMethod' ); + + Test.startTest(); + AmossTest_ClassToDouble returnFromMethod = classToDouble.fluentMethod(); + Test.stopTest(); + + System.assertEquals( null, returnFromMethod, 'isFluent, when a specified method with no defined return is called, will return null' ); + } + + /** + * @method isFluent + * @case when a specified method with a defined return is called + * @result will return the defined return + */ + @isTest + private static void isFluent_whenAnSpecifiedMethodWithADefinedReturnIsCalled_returnTheDefinedReturn() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + AmossTest_ClassToDouble specifiedReturn = new AmossTest_ClassToDouble( 'unimportant' ); + classToDoubleController + .isFluent() + .when( 'fluentMethod' ) + .returns( specifiedReturn ); + + Test.startTest(); + AmossTest_ClassToDouble returnFromMethod = classToDouble.fluentMethod(); + Test.stopTest(); + + System.assertEquals( specifiedReturn, returnFromMethod, 'isFluent, when a specified method with a defined return is called, will return the specified return' ); + } + + /** + * @method byDefaultMethodsReturn + * @case when an unspecified, method is called + * @result will return the specified value + */ + @isTest + private static void isFluent_whenAnUnspecifiedMethodIsCalled_returnTheSpecifiedValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .byDefaultMethodsReturn( 'DefaultReturn' ); + + Test.startTest(); + String returnFromDouble = classToDouble.methodUnderDouble( '1', 2 ); + Test.stopTest(); + + System.assertEquals( 'DefaultReturn', returnFromDouble, 'byDefaultMethodsReturn, when an unspecified, method is called, will return the specified value' ); + } + + /** + * @method byDefaultMethodsReturn + * @case when an unspecified, method with a different return type is called + * @result will throw an exception + */ + @isTest + private static void byDefaultMethodsReturn_whenAnUnspecifiedMethodWithADiffReturnTypeIsCalled_throwAnException() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .byDefaultMethodsReturn( 'DefaultReturn' ); + + Boolean exceptionThrown = false; + String exceptionMessage; + + Test.startTest(); + try { + classToDouble.methodWithListOfObjects( null ); + } catch( Exception e ) { + exceptionThrown = true; + exceptionMessage = e.getMessage(); + } + Test.stopTest(); + + System.assertEquals( true, exceptionThrown, 'byDefaultMethodsReturn, when an unspecified method with a different return type is called, will throw an exception' ); + System.assertEquals( 'Invalid conversion from runtime type String to List', exceptionMessage, 'byDefaultMethodsReturn, when an unspecified method with a different return type, will throw an exception' ); + } + + /** + * @method byDefaultMethodsReturn + * @case when an unspecified, method with no return is called + * @result will not throw an exception + */ + @isTest + private static void byDefaultMethodsReturn_whenAnUnspecifiedMethodWithNoReturnIsCalled_notThrowAnException() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .byDefaultMethodsReturn( 'DefaultReturn' ); + + Boolean exceptionThrown = false; + String exceptionMessage; + + Test.startTest(); + try { + classToDouble.methodUnderDoubleWithNoReturn( '1', 2 ); + } catch( DmlException e ) { + exceptionThrown = true; + exceptionMessage = e.getMessage(); + } + Test.stopTest(); + + System.assertEquals( false, exceptionThrown, 'byDefaultMethodsReturn, when an unspecified, method with no return is called, will not throw an exception' ); + } + + /** + * @method when.method.handledBy, StubProvider + * @case when that method is called + * @result will call handleMethodCall on the specified object and return the result + */ + @isTest + private static void whenMethodHandledByStubProvider_whenCalled_CallTheHandlerAndReturnTheResult() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + StubProvider methodHandler = new MethodHandlerUsingStubProvider(); + + classToDoubleController + .when() + .method( 'methodUnderDouble' ) + .handledBy( methodHandler ); + + String expectedReturn = 'methodUnderDouble:String|(String, Integer)|(parameter1, parameter2)|(OtherActualParam1, 1)'; + + Test.startTest(); + String returnFromDouble = classToDouble.methodUnderDouble( 'OtherActualParam1', 1 ); + Test.stopTest(); + + System.assertEquals( expectedReturn, returnFromDouble, 'when.method.handledBy, when given a StubProvider, and the method called, will call handleMethodCall on the given object and return the result' ); + } + + // This cannot be mocked - ironically because of the limitations of StubProvider + class MethodHandlerUsingStubProvider implements StubProvider { + public Object handleMethodCall( Object mockedObject, + String mockedMethod, + Type returnType, + List parameterTypes, + List parameterNames, + List parameters ) { + return mockedMethod + ':' + returnType + '|'+ String.valueOf( parameterTypes ) +'|'+ String.valueOf( parameterNames ) +'|'+ String.valueOf( parameters ); + } + } + + /** + * @method when.method.handledBy, Amoss_MethodHandler + * @case when that method is called + * @result will call handleMethodCall on the specified object and return the result + */ + @isTest + private static void whenMethodHandledByAmossMethodHandler_whenCalled_CallTheHandlerAndReturnTheResult() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + // Generate a mock object version of an Amoss_MethodHandler to test handledBy with + Amoss_Instance methodHandlerController = new Amoss_Instance( Amoss_MethodHandler.class ); + methodHandlerController + .expects() + .method( 'handleMethodCall' ) + .withParameter().setToTheSameValueAs( new List{ 'OtherActualParam1', 1 } ) + .returns( 'expectedReturn' ); + + Amoss_MethodHandler methodHandler = (Amoss_MethodHandler)methodHandlerController.getDouble(); + + classToDoubleController + .when() + .method( 'methodUnderDouble' ) + .handledBy( methodHandler ); + + String expectedReturn = '(OtherActualParam1, 1)'; + + Test.startTest(); + String returnFromDouble = classToDouble.methodUnderDouble( 'OtherActualParam1', 1 ); + Test.stopTest(); + + methodHandlerController.verify(); + + System.assertEquals( 'expectedReturn', returnFromDouble, 'when.method.handledBy, when given an Amoss_Instance.Amoss_MethodHandler, and the method called, will call handleMethodCall on the given object and return the result' ); + } + + /** + * @method when.method.throws + * @case when that method is called + * @result will throw the stated exception + */ + @isTest + private static void whenMethodThrows_whenCalled_throwTheStatedException() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .when() + .method( 'methodUnderDouble' ) + .throws( new DmlException( 'The defined exception' ) ); + + Boolean exceptionThrown = false; + String exceptionMessage; + + Test.startTest(); + try { + String returnFromDouble = classToDouble.methodUnderDouble( 'OtherActualParam1', 1 ); + } catch( DmlException e ) { + exceptionThrown = true; + exceptionMessage = e.getMessage(); + } + Test.stopTest(); + + System.assertEquals( true, exceptionThrown, 'when.method.throw, and the method called, will throw the stated exception' ); + System.assertEquals( 'The defined exception', exceptionMessage, 'when.method.throw, and the method called, will throw the stated exception' ); + } + + /** + * @method when.method.throwing + * @case when that method is called + * @result will throw the stated exception + */ + @isTest + private static void whenMethodThrowing_whenCalled_throwTheStatedException() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .when() + .method( 'methodUnderDouble' ) + .throwing( new DmlException( 'The defined exception' ) ); + + Boolean exceptionThrown = false; + String exceptionMessage; + + Test.startTest(); + try { + String returnFromDouble = classToDouble.methodUnderDouble( 'OtherActualParam1', 1 ); + } catch( DmlException e ) { + exceptionThrown = true; + exceptionMessage = e.getMessage(); + } + Test.stopTest(); + + System.assertEquals( true, exceptionThrown, 'when.method.throwing, and the method called, will throw the stated exception' ); + System.assertEquals( 'The defined exception', exceptionMessage, 'when.method.throwing, and the method called, will throw the stated exception' ); + } + + /** + * @method when.method.willThrow + * @case when that method is called + * @result will throw the stated exception + */ + @isTest + private static void whenMethodWillThrow_whenCalled_throwTheStatedException() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .when() + .method( 'methodUnderDouble' ) + .willThrow( new DmlException( 'The defined exception' ) ); + + Boolean exceptionThrown = false; + String exceptionMessage; + + Test.startTest(); + try { + String returnFromDouble = classToDouble.methodUnderDouble( 'OtherActualParam1', 1 ); + } catch( DmlException e ) { + exceptionThrown = true; + exceptionMessage = e.getMessage(); + } + Test.stopTest(); + + System.assertEquals( true, exceptionThrown, 'when.method.willThrow, and the method called, will throw the stated exception' ); + System.assertEquals( 'The defined exception', exceptionMessage, 'when.method.willThrow, and the method called, will throw the stated exception' ); + } + + /** + * @method when.method.willReturn + * @case for an interface, when that method is called + * @result will return the stated value + */ + @isTest + private static void whenMethodWillReturn_forAnInterface_whenCalled_returnSetUpValue() { + + Amoss_Instance interfaceUnderDoubleController = new Amoss_Instance( AmossTest_InterfaceToDouble.class ); + AmossTest_InterfaceToDouble interfaceUnderDouble = (AmossTest_InterfaceToDouble)interfaceUnderDoubleController.getDouble(); + + interfaceUnderDoubleController + .when() + .method( 'methodUnderDouble' ) + .willReturn( 'expectedReturn' ); + + Test.startTest(); + String returnFromDouble = interfaceUnderDouble.methodUnderDouble( 'OtherActualParam1', 1 ); + Test.stopTest(); + + System.assertEquals( 'expectedReturn', returnFromDouble, 'when.method.willReturn, for an interface, and the method called, will return the stated value, regardless of the parameters passed' ); + } + + /** + * @method when.method.willReturn + * @case when a different method is called + * @result will return null + */ + @isTest + private static void whenMethodWillReturn_whenADiffMethodIsCalled_returnNull() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .when() + .method( 'methodUnderDouble' ) + .willReturn( 'willNotBeReturned' ); + + Test.startTest(); + String returnFromDouble = classToDouble.otherMethodUnderDouble( 'OtherActualParam1', 1 ); + Test.stopTest(); + + System.assertEquals( null, returnFromDouble, 'when.method.willReturn, and a different method called, will return null' ); + } + + /** + * @method when.method.withParameter.willReturn + * @case when that method is called with the stated parameters + * @result will return the stated value + */ + @isTest + private static void whenWithParameterWillReturn_whenCallWithParams_returnSetUpValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .when() + .method( 'methodUnderDouble' ) + .withParameter( 'ActualParam1' ) + .thenParameter( 1 ) + .willReturn( 'expectedReturn' ); + + Test.startTest(); + String returnFromDouble = classToDouble.methodUnderDouble( 'ActualParam1', 1 ); + Test.stopTest(); + + System.assertEquals( 'expectedReturn', returnFromDouble, 'when.method.withParameter.willReturn, and the method called with the stated parameters, will return the stated value' ); + } + + /** + * @method when.method.withParameter.setTo.willReturn + * @case when that method is called with the stated parameters + * @result will return the stated value + */ + @isTest + private static void whenWithParameterSetToWillReturn_whenCallWithParams_returnSetUpValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .when() + .method( 'methodUnderDouble' ) + .withParameter().setTo( 'ActualParam1' ) + .thenParameter().setTo( 1 ) + .willReturn( 'expectedReturn' ); + + Test.startTest(); + String returnFromDouble = classToDouble.methodUnderDouble( 'ActualParam1', 1 ); + Test.stopTest(); + + System.assertEquals( 'expectedReturn', returnFromDouble, 'when.method.withParameter.setTo.willReturn, and the method called with the stated parameters, will return the stated value' ); + } + + /** + * @method when.method.withParameter.set.willReturn + * @case when that method is called with the parameter set (not null) + * @result will return the stated value + */ + @isTest + private static void whenWithParameterSet_whenCallWithParamSet_returnSetUpValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .when() + .method( 'methodUnderDouble' ) + .withParameter().set() + .thenParameter().set() + .willReturn( 'expectedReturn' ); + + Test.startTest(); + String returnFromDouble = classToDouble.methodUnderDouble( 'SetValue', 1 ); + Test.stopTest(); + + System.assertEquals( 'expectedReturn', returnFromDouble, 'when.method.withParameter.set.willReturn, and the method called with parameter set to not null, will return the stated value' ); + } + + /** + * @method when.method.withParameter.set.willReturn + * @case when that method is called with the parameter set to null + * @result will not return the stated value + */ + @isTest + private static void whenWithParameterSet_whenCallWithParamSetToNull_notReturnSetUpValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .when() + .method( 'methodUnderDouble' ) + .withParameter().set() + .thenParameter().set() + .willReturn( 'expectedReturn' ); + + Test.startTest(); + String returnFromDouble = classToDouble.methodUnderDouble( null, 1 ); + Test.stopTest(); + + System.assertEquals( null, returnFromDouble, 'when.method.withParameter.set.willReturn, and the method called with the parameter set to null, will not return the stated value' ); + } + + /** + * @method when.method.withParameter.containing.willReturn + * @case when that method is called with the parameter set to a String that contains the given one + * @result will return the stated value + */ + @isTest + private static void whenWithParameterContaining_whenCallWithParamSetToAStringThatContainsTheGiven_returnSetUpValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .when() + .method( 'methodUnderDouble' ) + .withParameter().containing( 'match' ) + .thenAnyParameter() + .willReturn( 'expectedReturn' ); + + Test.startTest(); + String returnFromDouble = classToDouble.methodUnderDouble( 'this matches', 1 ); + Test.stopTest(); + + System.assertEquals( 'expectedReturn', returnFromDouble, 'when.method.withParameter.containing.willReturn, and the method called with parameter set to a string containing the given value, will return the stated value' ); + } + + /** + * @method when.method.withParameter.containing.willReturn + * @case when that method is called with the parameter set to a String that contains the given one, but in the wrong case + * @result will not return the stated value + */ + @isTest + private static void whenWithParameterContaining_whenCallWithParamSetToAStringThatContainsTheGivenInWrongCase_notReturnSetUpValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .when() + .method( 'methodUnderDouble' ) + .withParameter().containing( 'NoMatch' ) + .thenAnyParameter() + .willReturn( 'notExpectedReturn' ); + + Test.startTest(); + String returnFromDouble = classToDouble.methodUnderDouble( 'this nomatches', 1 ); + Test.stopTest(); + + System.assertEquals( null, returnFromDouble, 'when.method.withParameter.containing.willReturn, and the method called with parameter set to a string containing the given value in the wrong case, will not return the stated value' ); + } + + /** + * @method when.method.withParameter.containing.willReturn + * @case when that method is called with the parameter set to an empty string + * @result will not return the stated value + */ + @isTest + private static void whenWithParameterContaining_whenCallWithParamSetToAnEmptyString_notReturnSetUpValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .when() + .method( 'methodUnderDouble' ) + .withParameter().containing( 'NoMatch' ) + .thenAnyParameter() + .willReturn( 'notExpectedReturn' ); + + Test.startTest(); + String returnFromDouble = classToDouble.methodUnderDouble( '', 1 ); + Test.stopTest(); + + System.assertEquals( null, returnFromDouble, 'when.method.withParameter.containing.willReturn, and the method called with parameter set to an empty string, will not return the stated value' ); + } + + /** + * @method when.method.withParameter.containing.willReturn + * @case when that method is called with the parameter set to null + * @result will not return the stated value + */ + @isTest + private static void whenWithParameterContaining_whenCallWithParamSetToNull_notReturnSetUpValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .when() + .method( 'methodUnderDouble' ) + .withParameter().containing( 'NoMatch' ) + .thenAnyParameter() + .willReturn( 'notExpectedReturn' ); + + Test.startTest(); + String returnFromDouble = classToDouble.methodUnderDouble( null, 1 ); + Test.stopTest(); + + System.assertEquals( null, returnFromDouble, 'when.method.withParameter.containing.willReturn, and the method called with parameter set to null, will not return the stated value' ); + } + + /** + * @method when.method.withParameter.matching.willReturn + * @case when that method is called with the parameter set to a String that matches the given one + * @result will return the stated value + */ + @isTest + private static void whenWithParameterMatching_whenCallWithParamSetToAStringThatMatchesTheGiven_returnSetUpValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .when() + .method( 'methodUnderDouble' ) + .withParameter().matching( 't.*m.*s' ) + .thenAnyParameter() + .willReturn( 'expectedReturn' ); + + Test.startTest(); + String returnFromDouble = classToDouble.methodUnderDouble( 'this matches', 1 ); + Test.stopTest(); + + System.assertEquals( 'expectedReturn', returnFromDouble, 'when.method.withParameter.matching.willReturn, and the method called with parameter set to a string matching the given value, will return the stated value' ); + } + + /** + * @method when.method.withParameter.matching.willReturn + * @case when that method is called with the parameter set to a String that does not all match the expression + * @result will not return the stated value + */ + @isTest + private static void whenWithParameterMatching_whenCallWithParamSetToAStringDoesNotWhileMatch_notReturnSetUpValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .when() + .method( 'methodUnderDouble' ) + .withParameter().matching( 'm.*s' ) + .thenAnyParameter() + .willReturn( 'notExpectedReturn' ); + + Test.startTest(); + String returnFromDouble = classToDouble.methodUnderDouble( 'this nomatches', 1 ); + Test.stopTest(); + + System.assertEquals( null, returnFromDouble, 'when.method.withParameter.matching.willReturn, and the method called with parameter set to a string that does not all match the given expression will not return the stated value' ); + } + + /** + * @method when.method.withParameter.matching.willReturn + * @case when that method is called with the parameter set to a String that does not match the given one + * @result will not return the stated value + */ + @isTest + private static void whenWithParameterMatchingwhenTheMethodIsCalledWithParamSetToAStringDoesNotMatch_notReturnSetUpValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .when() + .method( 'methodUnderDouble' ) + .withParameter().matching( 'Ma.*s' ) + .thenAnyParameter() + .willReturn( 'notExpectedReturn' ); + + Test.startTest(); + String returnFromDouble = classToDouble.methodUnderDouble( 'this nomatches', 1 ); + Test.stopTest(); + + System.assertEquals( null, returnFromDouble, 'when.method.withParameter.matching.willReturn, and the method called with parameter set to a string not matching the given value will not return the stated value' ); + } + + /** + * @method when.method.withParameter.matching.willReturn + * @case when that method is called with the parameter set to null + * @result will not return the stated value + */ + @isTest + private static void whenWithParameterMatching_whenCallWithParamSetToNull_notReturnSetUpValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .when() + .method( 'methodUnderDouble' ) + .withParameter().matching( 'a*b' ) + .thenAnyParameter() + .willReturn( 'notExpectedReturn' ); + + Test.startTest(); + String returnFromDouble = classToDouble.methodUnderDouble( null, 1 ); + Test.stopTest(); + + System.assertEquals( null, returnFromDouble, 'when.method.withParameter.matching.willReturn, and the method called with parameter set to null, will not return the stated value' ); + } + + /** + * @method when.method.withParameter.matching.willReturn + * @case when the stated regular expression is not valid + * @result will fail the test + */ + @isTest + private static void whenWithParameterMatching_whenTheRegExpIsNotValid_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Test.startTest(); + try { + classToDoubleController + .when() + .method( 'methodUnderDouble' ) + .withParameter().matching( '*' ); + } catch( TestException e ) {} + Test.stopTest(); + + System.assertEquals( false, assertsDoubleController.get().latestCallOf( 'assert' ).parameter( 0 ), 'when.method.withParameter.matching.willReturn, and the RegExp is not valid will fail, by calling assert with false' ); + + String expectedAssertion = 'The regular expression "*" does not appear to be valid: Invalid regex: Dangling meta character \'*\' near index 0'; + String actualAssertion = (String)assertsDoubleController.get().latestCallOf( 'assert' ).parameter( 1 ); + + Amoss_Asserts.assertContains( expectedAssertion, actualAssertion, 'when.method.withParameter.matching.willReturn, and the RegExp is not valid will fail, will fail, by calling assert with an assertion message that clearly describes the issue' ); + + String expectedExpected = 'Expected: A valid regular expression as per https://docs.oracle.com/javase/1.5.0/docs/api/index.html?java/util/regex/Pattern.html'; + + Amoss_Asserts.assertContains( expectedExpected, actualAssertion, 'when.method.withParameter.matching.willReturn, and the RegExp is not valid will fail, will fail, by calling assert with an expected that clearly describes what was expected' ); + + String expectedActual = 'Actual: *'; + + Amoss_Asserts.assertContains( expectedActual, actualAssertion, 'when.method.withParameter.matching.willReturn, and the RegExp is not valid will fail, will fail, by calling assert with an actual that contains the regular expression that was set' ); + } + + /** + * @method when.method.withParameter.aListOfLength.willReturn + * @case when that method is called with the parameter set to a List that is the stated length + * @result will return the stated value + */ + @isTest + private static void whenWithParameterAListOfLength_whenCallWithParamListOfGivenLength_returnSetUpValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .when() + .method( 'methodWithListOfObjects' ) + .withParameter().aListOfLength( 3 ) + .willReturn( new List{ 'return' } ); + + Test.startTest(); + List returnFromDouble = classToDouble.methodWithListOfObjects( new List{ 1, 2, 3 } ); + Test.stopTest(); + + System.assertEquals( new List{ 'return' }, returnFromDouble, 'when.method.withParameter.aListOfLength.willReturn, when that method is called with the parameter set to a List that is the stated length, will return the stated value' ); + } + + /** + * @method when.method.withParameter.aListOfLength.willReturn + * @case when that method is called with the parameter set to a List that is a different length + * @result will not return the stated value + */ + @isTest + private static void whenWithParameterAListOfLength_whenCallWithParamListOfDiffLength_notReturnSetUpValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .when() + .method( 'methodWithListOfObjects' ) + .withParameter().aListOfLength( 10 ) + .willReturn( new List{ 'return' } ); + + Test.startTest(); + List returnFromDouble = classToDouble.methodWithListOfObjects( new List{ 1, 2, 3 } ); + Test.stopTest(); + + System.assertEquals( null, returnFromDouble, 'when.method.withParameter.aListOfLength.willReturn, when that method is called with the parameter set to a List that is not the stated length, will not return the stated value' ); + } + + /** + * @method when.method.withParameter.aListOfLength.willReturn + * @case when that method is called with the parameter set to something that is not a list + * @result will not return the stated value + */ + @isTest + private static void whenWithParameterAListOfLength_whenCallWithParamNotAList_notReturnSetUpValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .when() + .method( 'methodUnderDouble' ) + .withParameter().aListOfLength( 10 ) + .thenAnyParameter() + .willReturn( 'return' ); + + Test.startTest(); + String returnFromDouble = classToDouble.methodUnderDouble( 'Thing', 2 ); + Test.stopTest(); + + System.assertEquals( null, returnFromDouble, 'when.method.withParameter.aListOfLength.willReturn, when that method is called with the parameter that is not a list, will not return the stated value' ); + } + + /** + * @method when.method.withParameter.aListOfLength.withElementAt.willReturn + * @case when that method is called with the parameter set to a List that is the stated length, and other criteria met + * @result will return the stated value + */ + @isTest + private static void whenWithParameterAListOfLengthWithElementAt_whenAllMet_returnSetUpValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .when() + .method( 'methodWithListOfObjects' ) + .withParameter().aListOfLength( 3 ) + .withElementAt( 0 ).setTo( 1 ) + .withElementAt( 1 ).setTo( 2 ) + .withElementAt( 2 ).setTo( 3 ) + .willReturn( new List{ 'return' } ); + + Test.startTest(); + List returnFromDouble = classToDouble.methodWithListOfObjects( new List{ 1, 2, 3 } ); + Test.stopTest(); + + System.assertEquals( new List{ 'return' }, returnFromDouble, 'when.method.withParameter.aListOfLength.withElementAt.willReturn, when that method is called with all criteria met, will return the stated value' ); + } + + /** + * @method when.method.withParameter.aListOfLength.withElementAt.willReturn + * @case when not all criteria met + * @result will return the stated value + */ + @isTest + private static void whenWithParameterAListOfLengthWithElementAt_whenNotAllMet_notReturnSetUpValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .when() + .method( 'methodWithListOfObjects' ) + .withParameter().aListOfLength( 3 ) + .withElementAt( 0 ).setTo( 1 ) + .withElementAt( 1 ).setTo( 99 ) + .withElementAt( 2 ).setTo( 3 ) + .willReturn( new List{ 'return' } ); + + Test.startTest(); + List returnFromDouble = classToDouble.methodWithListOfObjects( new List{ 1, 2, 3 } ); + Test.stopTest(); + + System.assertEquals( null, returnFromDouble, 'when.method.withParameter.aListOfLength.withElementAt.willReturn, when that method is called with not all criteria met, will not return the stated value' ); + } + + /** + * @method when.method.withParameter.noVerificationSet + * @case when that method is called + * @result will fail + */ + @isTest + private static void whenWithParameterNoVerificationSet_whenCalled_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'objectMethodUnderDouble' ) + .withParameter(); + + Test.startTest(); + try { + String returnFromDouble = classToDouble.objectMethodUnderDouble( 'ActualParam1' ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterInPositionAssertion( assertsDoubleController, + 'The specification of the expected parameter value is incompletely set up', + CLASS_TO_DOUBLE + '.objectMethodUnderDouble', + 0, + 'when.method.withParameter.noVerificationSet, when the method is called, will fail, stating that the parameter is not fully set up' ); + } + + /** + * @method when.method.withParameters.willReturn + * @case when that method is called with the stated parameters + * @result will return the stated value + */ + @isTest + private static void whenWithParametersWillReturn_whenCallWithParams_returnSetUpValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .when() + .method( 'methodUnderDouble' ) + .withParameters( + new List{ 'ActualParam1', 1 } + ) + .willReturn( 'expectedReturn' ); + + Test.startTest(); + String returnFromDouble = classToDouble.methodUnderDouble( 'ActualParam1', 1 ); + Test.stopTest(); + + System.assertEquals( 'expectedReturn', returnFromDouble, 'when.method.withParameters.willReturn, and the method called with the stated parameters, will return the stated value' ); + } + + /** + * @method when.method.withParameterNamed.setTo.willReturn + * @case when that method is called with the stated parameters + * @result will return the stated value + */ + @isTest + private static void whenWithParameterNamedSetToWillReturn_whenCallWithParams_returnSetUpValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .when() + .method( 'methodUnderDouble' ) + .withParameterNamed( 'parameter2' ).setTo( 1 ) + .andParameterNamed( 'parameter1' ).setTo( 'ActualParam1' ) + .willReturn( 'expectedReturn' ); + + Test.startTest(); + String returnFromDouble = classToDouble.methodUnderDouble( 'ActualParam1', 1 ); + Test.stopTest(); + + System.assertEquals( 'expectedReturn', returnFromDouble, 'when.method.withParameterNamedSetTo.willReturn, and the method called with the stated parameters, will return the stated value' ); + } + + /** + * @method when.method.withParameterNamed.setTo.willReturn + * @case when not all parameters set, but the method is called with the specified parameters + * @result will return the stated value + */ + @isTest + private static void whenWithParameterNamedSetToWillReturn_whenNotAllSetAndTheMethodIsCalledWithParams_returnSetUpValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .when() + .method( 'methodUnderDouble' ) + .withParameterNamed( 'parameter1' ).setTo( 'ActualParam1' ) + .willReturn( 'expectedReturn' ); + + Test.startTest(); + String returnFromDouble = classToDouble.methodUnderDouble( 'ActualParam1', 1 ); + Test.stopTest(); + + System.assertEquals( 'expectedReturn', returnFromDouble, 'when.method.withParameterNamedSetTo.willReturn, not all parameters set, and the method called with the stated parameters, will return the stated value' ); + } + + /** + * @method when.method.withParameters.willReturn (named version) + * @case when that method is called with the stated parameters + * @result will return the stated value + */ + @isTest + private static void whenWithParametersNamedWillReturn_whenCallWithParams_returnSetUpValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .when() + .method( 'methodUnderDouble' ) + .withParameters( + new Map{ 'parameter1' => 'ActualParam1', 'parameter2' => 1 } + ) + .willReturn( 'expectedReturn' ); + + Test.startTest(); + String returnFromDouble = classToDouble.methodUnderDouble( 'ActualParam1', 1 ); + Test.stopTest(); + + System.assertEquals( 'expectedReturn', returnFromDouble, 'when.method.withParameters.willReturn, named version, and the method called with the stated parameters, will return the stated value' ); + } + + /** + * @method when.method.withParameters.willReturn (named version) + * @case when not all parameters set, but the method is called with the specified parameters + * @result will return the stated value + */ + @isTest + private static void whenWithParametersNamedWillReturn_whenNotAllSpecifiedAndTheMethodIsCalledWithParams_returnSetUpValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .when() + .method( 'methodUnderDouble' ) + .withParameters( + new Map{ 'parameter2' => 1 } + ) + .willReturn( 'expectedReturn' ); + + Test.startTest(); + String returnFromDouble = classToDouble.methodUnderDouble( 'ActualParam1', 1 ); + Test.stopTest(); + + System.assertEquals( 'expectedReturn', returnFromDouble, 'when.method.withParameters.willReturn, named version, when not all parameters are specified and the method called with the stated parameters, will return the stated value' ); + } + + /** + * @method when.method.withParameter.willReturn + * @case for an interface, when that method is called with the stated parameters + * @result will return the stated value + */ + @isTest + private static void whenWithParameterWillReturn_forAnInterface_whenCallWithParams_returnSetUpValue() { + + Amoss_Instance interfaceUnderDoubleController = new Amoss_Instance( AmossTest_InterfaceToDouble.class ); + AmossTest_InterfaceToDouble interfaceUnderDouble = (AmossTest_InterfaceToDouble)interfaceUnderDoubleController.getDouble(); + + interfaceUnderDoubleController + .when() + .method( 'methodUnderDouble' ) + .withParameter( 'ActualParam1' ) + .thenParameter( 1 ) + .willReturn( 'expectedReturn' ); + + Test.startTest(); + String returnFromDouble = interfaceUnderDouble.methodUnderDouble( 'ActualParam1', 1 ); + Test.stopTest(); + + System.assertEquals( 'expectedReturn', returnFromDouble, 'when.method.withParameter.willReturn, for an interface, and the method called with the stated parameters, will return the stated value' ); + } + + /** + * @method when.method.withParameter.willReturn + * @case when that method is called with different parameters + * @result will return null + */ + @isTest + private static void whenWithParameterWillReturn_whenCallWithDiffParams_returnNull() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .when() + .method( 'methodUnderDouble' ) + .withParameter( 'ActualParam1' ) + .thenParameter( 1 ) + .willReturn( 'willNotSeeThis' ); + + Test.startTest(); + String returnFromDouble = classToDouble.methodUnderDouble( 'DifferentParam', 1 ); + Test.stopTest(); + + System.assertEquals( null, returnFromDouble, 'when.method.withParameter.willReturn, and the method called with the different parameters, will return null' ); + } + + /** + * @method when.method.withParameters.willReturn + * @case when that method is called with different parameters + * @result will return null + */ + @isTest + private static void whenWithParametersWillReturn_whenCallWithDiffParams_returnNull() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .when() + .method( 'methodUnderDouble' ) + .withParameters( + new List{'ActualParam1', 1 } + ) + .willReturn( 'willNotSeeThis' ); + + Test.startTest(); + String returnFromDouble = classToDouble.methodUnderDouble( 'DifferentParam', 1 ); + Test.stopTest(); + + System.assertEquals( null, returnFromDouble, 'when.method.withParameters.willReturn, and the method called with the different parameters, will return null' ); + } + + /** + * @method when.method.withParameter.setTo.willReturn + * @case when that method is called with different parameters + * @result will return null + */ + @isTest + private static void whenWithParameterSetToWillReturn_whenCallWithDiffParams_returnNull() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .when() + .method( 'methodUnderDouble' ) + .withParameter().setTo( 'ActualParam1' ) + .thenParameter().setTo( 1 ) + .willReturn( 'willNotSeeThis' ); + + Test.startTest(); + String returnFromDouble = classToDouble.methodUnderDouble( 'DifferentParam', 1 ); + Test.stopTest(); + + System.assertEquals( null, returnFromDouble, 'when.method.withParameter.setTo.willReturn, and the method called with the different parameters, will return null' ); + } + + /** + * @method when.method.withParameterNamed.setTo.willReturn + * @case when that method is called with different parameters + * @result will return null + */ + @isTest + private static void whenWithParameterNamedSetToWillReturn_whenCallWithDiffParams_returnNull() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .when() + .method( 'methodUnderDouble' ) + .withParameterNamed( 'parameter1' ).setTo( 'ActualParam1' ) + .willReturn( 'willNotSeeThis' ); + + Test.startTest(); + String returnFromDouble = classToDouble.methodUnderDouble( 'DifferentParam', 1 ); + Test.stopTest(); + + System.assertEquals( null, returnFromDouble, 'when.method.withParameterNamed.setTo.willReturn, and the method called with the different parameters, will return null' ); + } + + /** + * @method when.method.withParameterNamed.set.willReturn + * @case when that method is called with the parameter set (not null) + * @result will return the stated value + */ + @isTest + private static void whenWithParameterNamedSet_whenCallWithParamSet_returnSetUpValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .when() + .method( 'methodUnderDouble' ) + .withParameterNamed( 'parameter1' ).set() + .willReturn( 'expectedReturn' ); + + Test.startTest(); + String returnFromDouble = classToDouble.methodUnderDouble( 'SetValue', 1 ); + Test.stopTest(); + + System.assertEquals( 'expectedReturn', returnFromDouble, 'when.method.withParameterNamed.set.willReturn, and the method called with parameter set to not null, will return the stated value' ); + } + + /** + * @method when.method.withParameterNamed.set.willReturn + * @case when that method is called with the parameter set to null + * @result will not return the stated value + */ + @isTest + private static void whenWithParameterNamedSet_whenCallWithParamSetToNull_notReturnSetUpValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .when() + .method( 'methodUnderDouble' ) + .withParameterNamed( 'parameter1' ).set() + .willReturn( 'expectedReturn' ); + + Test.startTest(); + String returnFromDouble = classToDouble.methodUnderDouble( null, 1 ); + Test.stopTest(); + + System.assertEquals( null, returnFromDouble, 'when.method.withParameterNamed.set.willReturn, and the method called with the parameter set to null, will not return the stated value' ); + } + + /** + * @method when.method.withParameterNamed.containing.willReturn + * @case when that method is called with the parameter containing + * @result will return the stated value + */ + @isTest + private static void whenWithParameterNamedContaining_whenCallWithParamContaining_returnSetUpValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .when() + .method( 'methodUnderDouble' ) + .withParameterNamed( 'parameter1' ).containing( 'will match' ) + .willReturn( 'expectedReturn' ); + + Test.startTest(); + String returnFromDouble = classToDouble.methodUnderDouble( 'this will match the config', 1 ); + Test.stopTest(); + + System.assertEquals( 'expectedReturn', returnFromDouble, 'when.method.withParameterNamed.containing.willReturn, and the method called with parameter set to a String containing, will return the stated value' ); + } + + /** + * @method when.method.withParameterNamed.containing.willReturn + * @case when that method is called with the parameter not containing + * @result will not return the stated value + */ + @isTest + private static void whenWithParameterNamedContaining_whenCallWithParamNotContaining_notReturnSetUpValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .when() + .method( 'methodUnderDouble' ) + .withParameterNamed( 'parameter1' ).containing( 'will not match' ) + .willReturn( 'expectedReturn' ); + + Test.startTest(); + String returnFromDouble = classToDouble.methodUnderDouble( 'this will no match the config', 1 ); + Test.stopTest(); + + System.assertEquals( null, returnFromDouble, 'when.method.withParameterNamed.containing.willReturn, and the method called with parameter set to a String not containing, will not return the stated value' ); + } + + /** + * @method when.method.withParameterNamed.matching.willReturn + * @case when that method is called with the parameter set to a String that matches the given one + * @result will return the stated value + */ + @isTest + private static void whenWithParameterNamedMatching_whenCallWithParamSetToAStringThatMatchesTheGiven_returnSetUpValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .when() + .method( 'methodUnderDouble' ) + .withParameterNamed( 'parameter1' ).matching( 't.*m.*s' ) + .willReturn( 'expectedReturn' ); + + Test.startTest(); + String returnFromDouble = classToDouble.methodUnderDouble( 'this matches', 1 ); + Test.stopTest(); + + System.assertEquals( 'expectedReturn', returnFromDouble, 'when.method.withParameterNamed.matching.willReturn, and the method called with parameter set to a string matching the given value, will return the stated value' ); + } + + /** + * @method when.method.withParameterNamed.matching.willReturn + * @case when that method is called with the parameter set to a String that does not match the given one + * @result will return the stated value + */ + @isTest + private static void whenWithParameterNamedMatching_whenCallWithParamSetToAStringThatNotMatchesTheGiven_notReturnSetUpValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .when() + .method( 'methodUnderDouble' ) + .withParameterNamed( 'parameter1' ).matching( 't.*m.*s' ) + .willReturn( 'expectedReturn' ); + + Test.startTest(); + String returnFromDouble = classToDouble.methodUnderDouble( 'does not match', 1 ); + Test.stopTest(); + + System.assertEquals( null, returnFromDouble, 'when.method.withParameterNamed.matching.willReturn, and the method called with parameter set to a string that does not match the given value, will not return the stated value' ); + } + + /** + * @method when.method.withParameterNamed.aListOfLength.willReturn + * @case when that method is called with the parameter set to a List that is the stated length + * @result will return the stated value + */ + @isTest + private static void whenWithParameterNamedAListOfLength_whenCallWithParamListOfGivenLength_returnSetUpValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .when() + .method( 'methodWithListOfObjects' ) + .withParameterNamed( 'parameter1' ).aListOfLength( 3 ) + .willReturn( new List{ 'return' } ); + + Test.startTest(); + List returnFromDouble = classToDouble.methodWithListOfObjects( new List{ 1, 2, 3 } ); + Test.stopTest(); + + System.assertEquals( new List{ 'return' }, returnFromDouble, 'when.method.withParameterNamed.aListOfLength.willReturn, when that method is called with the parameter set to a List that is the stated length, will return the stated value' ); + } + + /** + * @method when.method.withParameterNamed.aListOfLength.willReturn + * @case when that method is called with the parameter set to a List that is a different length + * @result will not return the stated value + */ + @isTest + private static void whenWithParameterNamedAListOfLength_whenCallWithParamListOfDiffLength_notReturnSetUpValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .when() + .method( 'methodWithListOfObjects' ) + .withParameterNamed( 'parameter1' ).aListOfLength( 10 ) + .willReturn( new List{ 'return' } ); + + Test.startTest(); + List returnFromDouble = classToDouble.methodWithListOfObjects( new List{ 1, 2, 3 } ); + Test.stopTest(); + + System.assertEquals( null, returnFromDouble, 'when.method.withParameterNamed.aListOfLength.willReturn, when that method is called with the parameter set to a List that is not the stated length, will not return the stated value' ); + } + + /** + * @method when.method.withParameterNamed.aListOfLength.willReturn + * @case when that method is called with the parameter set to something that is not a list + * @result will not return the stated value + */ + @isTest + private static void whenWithParameterNamedAListOfLength_whenCallWithParamNotAList_notReturnSetUpValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .when() + .method( 'methodUnderDouble' ) + .withParameterNamed( 'parameter1' ).aListOfLength( 10 ) + .willReturn( 'return' ); + + Test.startTest(); + String returnFromDouble = classToDouble.methodUnderDouble( 'Thing', 2 ); + Test.stopTest(); + + System.assertEquals( null, returnFromDouble, 'when.method.withParameterNamed.aListOfLength.willReturn, when that method is called with the parameter that is not a list, will not return the stated value' ); + } + + /** + * @method when.method.withParameterNamed.aListOfLength.withElementAt.willReturn + * @case when that method is called with the parameter set to a List that is the stated length, and other criteria met + * @result will return the stated value + */ + @isTest + private static void whenWithParameterNamedAListOfLengthWithElementAt_whenAllMet_returnSetUpValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .when() + .method( 'methodWithListOfObjects' ) + .withParameterNamed( 'parameter1' ).aListOfLength( 3 ) + .withElementAt( 0 ).setTo( 1 ) + .withElementAt( 1 ).setTo( 2 ) + .withElementAt( 2 ).setTo( 3 ) + .willReturn( new List{ 'return' } ); + + Test.startTest(); + List returnFromDouble = classToDouble.methodWithListOfObjects( new List{ 1, 2, 3 } ); + Test.stopTest(); + + System.assertEquals( new List{ 'return' }, returnFromDouble, 'when.method.withParameterNamed.aListOfLength.withElementAt.willReturn, when that method is called with all criteria met, will return the stated value' ); + } + + /** + * @method when.method.withParameterNamed.aListOfLength.withElementAt.willReturn + * @case when not all criteria met + * @result will return the stated value + */ + @isTest + private static void whenWithParameterNamedAListOfLengthWithElementAt_whenNotAllMet_notReturnSetUpValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .when() + .method( 'methodWithListOfObjects' ) + .withParameterNamed( 'parameter1' ).aListOfLength( 3 ) + .withElementAt( 0 ).setTo( 1 ) + .withElementAt( 1 ).setTo( 99 ) + .withElementAt( 2 ).setTo( 3 ) + .willReturn( new List{ 'return' } ); + + Test.startTest(); + List returnFromDouble = classToDouble.methodWithListOfObjects( new List{ 1, 2, 3 } ); + Test.stopTest(); + + System.assertEquals( null, returnFromDouble, 'when.method.withParameterNamed.aListOfLength.withElementAt.willReturn, when that method is called with not all criteria met, will not return the stated value' ); + } + + /** + * @method when.method.withParameters.setTo.willReturn (named) + * @case when that method is called with different parameters + * @result will return null + */ + @isTest + private static void whenWithParametersNamedSetToWillReturn_whenCallWithDiffParams_returnNull() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .when() + .method( 'methodUnderDouble' ) + .withParameters( + new Map{ + 'parameter1' => 'ActualParam1', + 'parameter2' => 1 + } + ) + .willReturn( 'willNotSeeThis' ); + + Test.startTest(); + String returnFromDouble = classToDouble.methodUnderDouble( 'DifferentParam', 1 ); + Test.stopTest(); + + System.assertEquals( null, returnFromDouble, 'when.method.withParameters.setTo.willReturn (named), and the method called with the different parameters, will return null' ); + } + + // + // + // Advanced When Parameter Matching Tests + // + // + + /** + * @method when.method.willReturn + * @case when the expectation is set with an object, and that instance is passed + * @result will return the stated value + */ + @isTest + private static void whenMethodWillReturn_whenSetWithObjAndPassed_returnThatValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + AmossTest_ClassToDouble.PassableObject objectPassed = new AmossTest_ClassToDouble.PassableObject( 'TheObject' ); + + classToDoubleController + .when() + .method( 'objectMethodUnderDouble' ) + .withParameter( objectPassed ) + .willReturn( 'expectedReturn' ); + + Test.startTest(); + String returnFromDouble = classToDouble.objectMethodUnderDouble( objectPassed ); + Test.stopTest(); + + System.assertEquals( 'expectedReturn', returnFromDouble, 'when.method.willReturn, when set up with an object, and that instance is passed, will return the set up value' ) ; + } + + /** + * @method when.method.willReturn + * @case when set with an object, and a different instance is passed with same properties + * @result will return null + */ + @isTest + private static void whenMethodWillReturn_whenSetWithObjAndDiffInstPassed_returnNull() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + AmossTest_ClassToDouble.PassableObject objectExpected = new AmossTest_ClassToDouble.PassableObject( 'TheObject' ); + + classToDoubleController + .when() + .method( 'objectMethodUnderDouble' ) + .withParameter( objectExpected ) + .willReturn( 'notExpectedToReturn' ); + + AmossTest_ClassToDouble.PassableObject objectPassed = new AmossTest_ClassToDouble.PassableObject( 'TheObject' ); + Test.startTest(); + String returnFromDouble = classToDouble.objectMethodUnderDouble( objectPassed ); + Test.stopTest(); + + System.assertEquals( null, returnFromDouble, 'when.method.willReturn, when set up with an object, and a different instance is passed, will return null' ) ; + } + + /** + * @method when.method.setToTheSameValueAs.willReturn + * @case when the expectation is set with an object, and that instance is passed + * @result will return the stated value + */ + @isTest + private static void whenMethodSetToTheSameValueAsWillReturn_whenSetWithObjAndPassed_returnThatValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + AmossTest_ClassToDouble.PassableObject objectPassed = new AmossTest_ClassToDouble.PassableObject( 'TheObject' ); + + classToDoubleController + .when() + .method( 'objectMethodUnderDouble' ) + .withParameter().setToTheSameValueAs( objectPassed ) + .willReturn( 'expectedReturn' ); + + Test.startTest(); + String returnFromDouble = classToDouble.objectMethodUnderDouble( objectPassed ); + Test.stopTest(); + + System.assertEquals( 'expectedReturn', returnFromDouble, 'when.method.setToTheSameValueAs.willReturn, when set up with an object, and that instance is passed, will return the set up value' ) ; + } + + /** + * @method when.method.setToTheSameValueAs.willReturn + * @case when set with an object, and a different instance is passed with same properties + * @result will return the stated value + */ + @isTest + private static void whenMethodSetToTheSameValueAsWillReturn_whenSetWithObjAndDiffInstPassed_returnSetUpValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + AmossTest_ClassToDouble.PassableObject objectExpected = new AmossTest_ClassToDouble.PassableObject( 'TheObject' ); + + classToDoubleController + .when() + .method( 'objectMethodUnderDouble' ) + .withParameter().setToTheSameValueAs( objectExpected ) + .willReturn( 'ExpectedToReturn' ); + + AmossTest_ClassToDouble.PassableObject objectPassed = new AmossTest_ClassToDouble.PassableObject( 'TheObject' ); + Test.startTest(); + String returnFromDouble = classToDouble.objectMethodUnderDouble( objectPassed ); + Test.stopTest(); + + System.assertEquals( 'ExpectedToReturn', returnFromDouble, 'when.method.willReturn, when set up with an object, and a different instance is passed with same properties, will return the stated value' ) ; + } + + /** + * @method when.method.setToTheSameValueAs.willReturn + * @case when set with an object, and a different instance is passed with different properties + * @result will return null + */ + @isTest + private static void whenMethodSetToTheSameValueAsWillReturn_whenSetWithObjAndDiffInstPassedWithDiffProps_returnNull() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + AmossTest_ClassToDouble.PassableObject objectExpected = new AmossTest_ClassToDouble.PassableObject( 'TheObject' ); + + classToDoubleController + .when() + .method( 'objectMethodUnderDouble' ) + .withParameter().setToTheSameValueAs( objectExpected ) + .willReturn( 'notExpectedToReturn' ); + + AmossTest_ClassToDouble.PassableObject objectPassed = new AmossTest_ClassToDouble.PassableObject( 'DifferentObject' ); + Test.startTest(); + String returnFromDouble = classToDouble.objectMethodUnderDouble( objectPassed ); + Test.stopTest(); + + System.assertEquals( null, returnFromDouble, 'when.method.willReturn, when set up with an object, and a different instance is passed with different properties, will return null' ) ; + } + + /** + * @method when.method.withParameterNamed.willReturn + * @case when the expectation is set with an object, and that instance is passed + * @result will return the stated value + */ + @isTest + private static void whenWithParameterNamedWillReturn_whenSetWithObjAndPassed_returnThatValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + AmossTest_ClassToDouble.PassableObject objectPassed = new AmossTest_ClassToDouble.PassableObject( 'TheObject' ); + + classToDoubleController + .when() + .method( 'objectMethodUnderDouble' ) + .withParameterNamed( 'objectParam' ).setTo( objectPassed ) + .willReturn( 'expectedReturn' ); + + Test.startTest(); + String returnFromDouble = classToDouble.objectMethodUnderDouble( objectPassed ); + Test.stopTest(); + + System.assertEquals( 'expectedReturn', returnFromDouble, 'when.method.withParameterNamed.willReturn, when set up with an object, and that instance is passed, will return the set up value' ) ; + } + + /** + * @method when.method.withParameterNamed.willReturn + * @case when set with an object, and a different instance is passed with same properties + * @result will return null + */ + @isTest + private static void whenWithParameterNamedWillReturn_whenSetWithObjAndDiffInstPassed_returnNull() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + AmossTest_ClassToDouble.PassableObject objectExpected = new AmossTest_ClassToDouble.PassableObject( 'TheObject' ); + + classToDoubleController + .when() + .method( 'objectMethodUnderDouble' ) + .withParameterNamed( 'objectParam' ).setTo( objectExpected ) + .willReturn( 'notExpectedToReturn' ); + + AmossTest_ClassToDouble.PassableObject objectPassed = new AmossTest_ClassToDouble.PassableObject( 'TheObject' ); + Test.startTest(); + String returnFromDouble = classToDouble.objectMethodUnderDouble( objectPassed ); + Test.stopTest(); + + System.assertEquals( null, returnFromDouble, 'when.method.withParameterNamed.willReturn, when set up with an object, and a different instance is passed, will return null' ) ; + } + + /** + * @method when.method.withParameterNamed.setToTheSameValueAs.willReturn + * @case when the expectation is set with an object, and that instance is passed + * @result will return the stated value + */ + @isTest + private static void whenWithParameterNamedSetToTheSameValueAsWillReturn_whenSetWithObjAndPassed_returnThatValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + AmossTest_ClassToDouble.PassableObject objectPassed = new AmossTest_ClassToDouble.PassableObject( 'TheObject' ); + + classToDoubleController + .when() + .method( 'objectMethodUnderDouble' ) + .withParameterNamed( 'objectParam' ).setToTheSameValueAs( objectPassed ) + .willReturn( 'expectedReturn' ); + + Test.startTest(); + String returnFromDouble = classToDouble.objectMethodUnderDouble( objectPassed ); + Test.stopTest(); + + System.assertEquals( 'expectedReturn', returnFromDouble, 'when.method.withParameterNamed.setToTheSameValueAs.willReturn, when set up with an object, and that instance is passed, will return the set up value' ) ; + } + + /** + * @method when.method.withParameterNamed.setToTheSameValueAs.willReturn + * @case when set with an object, and a different instance is passed with same properties + * @result will return the stated value + */ + @isTest + private static void whenWithParameterNamedSetToTheSameValueAsWillReturn_whenSetWithObjAndDiffInstPassed_returnSetUpValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + AmossTest_ClassToDouble.PassableObject objectExpected = new AmossTest_ClassToDouble.PassableObject( 'TheObject' ); + + classToDoubleController + .when() + .method( 'objectMethodUnderDouble' ) + .withParameterNamed( 'objectParam' ).setToTheSameValueAs( objectExpected ) + .willReturn( 'ExpectedToReturn' ); + + AmossTest_ClassToDouble.PassableObject objectPassed = new AmossTest_ClassToDouble.PassableObject( 'TheObject' ); + Test.startTest(); + String returnFromDouble = classToDouble.objectMethodUnderDouble( objectPassed ); + Test.stopTest(); + + System.assertEquals( 'ExpectedToReturn', returnFromDouble, 'when.method.withParameterNamed.willReturn, when set up with an object, and a different instance is passed with same properties, will return the stated value' ) ; + } + + /** + * @method when.method.withParameterNamed.setToTheSameValueAs.willReturn + * @case when set with an object, and a different instance is passed with different properties + * @result will return null + */ + @isTest + private static void whenWithParameterNamedSetToTheSameValueAsWillReturn_whenSetWithObjAndDiffInstPassedWithDiffProps_returnNull() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + AmossTest_ClassToDouble.PassableObject objectExpected = new AmossTest_ClassToDouble.PassableObject( 'TheObject' ); + + classToDoubleController + .when() + .method( 'objectMethodUnderDouble' ) + .withParameterNamed( 'objectParam' ).setToTheSameValueAs( objectExpected ) + .willReturn( 'notExpectedToReturn' ); + + AmossTest_ClassToDouble.PassableObject objectPassed = new AmossTest_ClassToDouble.PassableObject( 'DifferentObject' ); + Test.startTest(); + String returnFromDouble = classToDouble.objectMethodUnderDouble( objectPassed ); + Test.stopTest(); + + System.assertEquals( null, returnFromDouble, 'when.method.withParameterNamed.willReturn, when set up with an object, and a different instance is passed with different properties, will return null' ) ; + } + + /** + * @method when.method.willReturn + * @case when the is set with an sobject, and that instance is passed + * @result will return the stated value + */ + @isTest + private static void whenMethodWillReturn_whenIsSetWithSobjectAndPassed_returnSetUpValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Contact objectPassed = new Contact( LastName = 'TheContact' ); + + classToDoubleController + .when() + .method( 'sobjectMethodUnderDouble' ) + .withParameter( objectPassed ) + .willReturn( 'expectedReturn' ); + + Test.startTest(); + String returnFromDouble = classToDouble.sobjectMethodUnderDouble( objectPassed ); + Test.stopTest(); + + System.assertEquals( 'expectedReturn', returnFromDouble, 'when.method.willReturn, when set up with an sobject, and that instance is passed, will return the set up value' ) ; + } + + /** + * @method when.method.willReturn + * @case when the expectation is set with an sobject, and a different instance is passed with same properties + * @result will return null + */ + @isTest + private static void whenMethodWillReturn_whenIsSetWithSobjectAndDiffInstPassed_returnNull() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Contact objectExpected = new Contact( LastName = 'TheObject' ); + + classToDoubleController + .when() + .method( 'sobjectMethodUnderDouble' ) + .withParameter( objectExpected ) + .willReturn( 'NotExpectedReturn' ); + + Contact objectPassed = new Contact( LastName = 'TheObject' ); + Test.startTest(); + String returnFromDouble = classToDouble.sobjectMethodUnderDouble( objectPassed ); + Test.stopTest(); + + System.assertEquals( null, returnFromDouble, 'when.method.willReturn, when set up with an sobject, and a different instance is passed, will return null' ) ; + } + + /** + * @method when.method.setToTheSameValueAs.willReturn + * @case when is set with an sobject, and that instance is passed + * @result will return the stated value + */ + @isTest + private static void whenMethodSetToTheSameValueAsWillReturn_whenIsSetWithSobjectAndPassed_returnSetUpValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Contact objectPassed = new Contact( LastName = 'TheContact' ); + + classToDoubleController + .when() + .method( 'sobjectMethodUnderDouble' ) + .withParameter().setToTheSameValueAs( objectPassed ) + .willReturn( 'expectedReturn' ); + + Test.startTest(); + String returnFromDouble = classToDouble.sobjectMethodUnderDouble( objectPassed ); + Test.stopTest(); + + System.assertEquals( 'expectedReturn', returnFromDouble, 'when.method.setToTheSameValueAs.willReturn, when set up with an sobject, and that instance is passed, will return the set up value' ) ; + } + + /** + * @method when.method.setToTheSameValueAs.willReturn + * @case when the expectation is set with an sobject, and a different instance is passed with same properties + * @result will still return the stated value + */ + @isTest + private static void whenMethodSetToTheSameValueAsWillReturn_whenIsSetWithSobjectAndDiffInstPassed_returnSetUpValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Contact objectExpected = new Contact( LastName = 'TheObject' ); + + classToDoubleController + .when() + .method( 'sobjectMethodUnderDouble' ) + .withParameter().setToTheSameValueAs( objectExpected ) + .willReturn( 'ExpectedReturn' ); + + Contact objectPassed = new Contact( LastName = 'TheObject' ); + Test.startTest(); + String returnFromDouble = classToDouble.sobjectMethodUnderDouble( objectPassed ); + Test.stopTest(); + + System.assertEquals( 'ExpectedReturn', returnFromDouble, 'when.method.setToTheSameValueAs.willReturn, when set up with an sobject, and a different instance is passed with the same properies, will return the stated value' ) ; + } + + /** + * @method when.method.setToTheSameValueAs.willReturn + * @case when the expectation is set with an sobject, and a different instance is passed with different properties + * @result will return null + */ + @isTest + private static void whenMethodSetToTheSameValueAsWillReturn_whenIsSetWithSobjectAndDiffInstPassedWithDiffValues_returnNull() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Contact objectExpected = new Contact( LastName = 'TheObject' ); + + classToDoubleController + .when() + .method( 'sobjectMethodUnderDouble' ) + .withParameter().setToTheSameValueAs( objectExpected ) + .willReturn( 'NotExpectedReturn' ); + + Contact objectPassed = new Contact( LastName = 'TheObject', FirstName = 'Different' ); + Test.startTest(); + String returnFromDouble = classToDouble.sobjectMethodUnderDouble( objectPassed ); + Test.stopTest(); + + System.assertEquals( null, returnFromDouble, 'when.method.setToTheSameValueAs.willReturn, when set up with an sobject, and a different instance is passed with different properies, will return null' ) ; + } + + /** + * @method when.method.withParamterNamed.willReturn + * @case when the is set with an sobject, and that instance is passed + * @result will return the stated value + */ + @isTest + private static void whenWithParameterNamedWillReturn_whenIsSetWithSobjectAndPassed_returnSetUpValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Contact objectPassed = new Contact( LastName = 'TheContact' ); + + classToDoubleController + .when() + .method( 'sobjectMethodUnderDouble' ) + .withParameterNamed( 'contactParam' ).setTo( objectPassed ) + .willReturn( 'expectedReturn' ); + + Test.startTest(); + String returnFromDouble = classToDouble.sobjectMethodUnderDouble( objectPassed ); + Test.stopTest(); + + System.assertEquals( 'expectedReturn', returnFromDouble, 'when.method.withParamterNamed.willReturn, when set up with an sobject, and that instance is passed, will return the set up value' ) ; + } + + /** + * @method when.method.withParamterNamed.willReturn + * @case when the expectation is set with an sobject, and a different instance is passed with same properties + * @result will return null + */ + @isTest + private static void whenWithParameterNamedWillReturn_whenIsSetWithSobjectAndDiffInstPassed_returnNull() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Contact objectExpected = new Contact( LastName = 'TheObject' ); + + classToDoubleController + .when() + .method( 'sobjectMethodUnderDouble' ) + .withParameterNamed( 'contactParam' ).setTo( objectExpected ) + .willReturn( 'NotExpectedReturn' ); + + Contact objectPassed = new Contact( LastName = 'TheObject' ); + Test.startTest(); + String returnFromDouble = classToDouble.sobjectMethodUnderDouble( objectPassed ); + Test.stopTest(); + + System.assertEquals( null, returnFromDouble, 'when.method.withParamterNamed.willReturn, when set up with an sobject, and a different instance is passed, will return null' ) ; + } + + /** + * @method when.method.withParamterNamed.setToTheSameValueAs.willReturn + * @case when is set with an sobject, and that instance is passed + * @result will return the stated value + */ + @isTest + private static void whenWithParameterNamedSetToTheSameValueAsWillReturn_whenIsSetWithSobjectAndPassed_returnSetUpValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Contact objectPassed = new Contact( LastName = 'TheContact' ); + + classToDoubleController + .when() + .method( 'sobjectMethodUnderDouble' ) + .withParameterNamed( 'contactParam' ).setToTheSameValueAs( objectPassed ) + .willReturn( 'expectedReturn' ); + + Test.startTest(); + String returnFromDouble = classToDouble.sobjectMethodUnderDouble( objectPassed ); + Test.stopTest(); + + System.assertEquals( 'expectedReturn', returnFromDouble, 'when.method.withParamterNamed.setToTheSameValueAs.willReturn, when set up with an sobject, and that instance is passed, will return the set up value' ) ; + } + + /** + * @method when.method.withParamterNamed.setToTheSameValueAs.willReturn + * @case when the expectation is set with an sobject, and a different instance is passed with same properties + * @result will still return the stated value + */ + @isTest + private static void whenWithParameterNamedSetToTheSameValueAsWillReturn_whenIsSetWithSobjectAndDiffInstPassed_returnSetUpValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Contact objectExpected = new Contact( LastName = 'TheObject' ); + + classToDoubleController + .when() + .method( 'sobjectMethodUnderDouble' ) + .withParameterNamed( 'contactParam' ).setToTheSameValueAs( objectExpected ) + .willReturn( 'ExpectedReturn' ); + + Contact objectPassed = new Contact( LastName = 'TheObject' ); + Test.startTest(); + String returnFromDouble = classToDouble.sobjectMethodUnderDouble( objectPassed ); + Test.stopTest(); + + System.assertEquals( 'ExpectedReturn', returnFromDouble, 'when.method.withParamterNamed.setToTheSameValueAs.willReturn, when set up with an sobject, and a different instance is passed with the same properies, will return the stated value' ) ; + } + + /** + * @method when.method.withParamterNamed.setToTheSameValueAs.willReturn + * @case when the expectation is set with an sobject, and a different instance is passed with different properties + * @result will return null + */ + @isTest + private static void whenWithParameterNamedSetToTheSameValueAsWillReturn_whenIsSetWithSobjectAndDiffInstPassedWithDiffValues_returnNull() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Contact objectExpected = new Contact( LastName = 'TheObject' ); + + classToDoubleController + .when() + .method( 'sobjectMethodUnderDouble' ) + .withParameterNamed( 'contactParam' ).setToTheSameValueAs( objectExpected ) + .willReturn( 'NotExpectedReturn' ); + + Contact objectPassed = new Contact( LastName = 'TheObject', FirstName = 'Different' ); + Test.startTest(); + String returnFromDouble = classToDouble.sobjectMethodUnderDouble( objectPassed ); + Test.stopTest(); + + System.assertEquals( null, returnFromDouble, 'when.method.withParamterNamed.setToTheSameValueAs.willReturn, when set up with an sobject, and a different instance is passed with different properies, will return null' ) ; + } + + /** + * @method when.method.withParameter.willReturn multiple times + * @case when that method is called with the stated parameters + * @result will return the stated value for each call + */ + @isTest + private static void whenWithParameterWillReturnMultiple_whenCallWithParams_returnSetUpValues() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .when() + .method( 'methodUnderDouble' ) + .withParameter( 'ActualParam1' ) + .thenParameter( 1 ) + .willReturn( 'expectedReturn1' ) + .also().when() + .method( 'methodUnderDouble' ) + .withParameter( 'ActualParam2' ) + .thenParameter( 2 ) + .willReturn( 'expectedReturn2' ); + + Test.startTest(); + String returnFromDouble1 = classToDouble.methodUnderDouble( 'ActualParam1', 1 ); + String returnFromDouble2 = classToDouble.methodUnderDouble( 'ActualParam2', 2 ); + Test.stopTest(); + + System.assertEquals( 'expectedReturn1', returnFromDouble1, 'when.method.withParameter.willReturn multiple times, and the method called with the stated parameters, will return the stated value for the matching parameters' ); + System.assertEquals( 'expectedReturn2', returnFromDouble2, 'when.method.withParameter.willReturn multiple times, and the method called with the stated parameters, will return the stated value for the matching parameters' ); + } + + /** + * @method when.method.withParameter.willReturn multiple times + * @case when the same method is defiend with the same parameters and it is called multiple times + * @result will always return the first value with parameters that match + */ + @isTest + private static void whenWithParameterWillReturnMultiple_whenSameParamsUsed_alwaysReturnTheFirstMatch() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .when() + .method( 'methodUnderDouble' ) + .withParameter( 'ActualParam1' ) + .thenParameter( 1 ) + .willReturn( 'expectedReturn1' ) + .also().when() + .method( 'methodUnderDouble' ) + .withParameter( 'ActualParam1' ) + .thenParameter( 1 ) + .willReturn( 'expectedReturn1' ); + + Test.startTest(); + String returnFromDouble1 = classToDouble.methodUnderDouble( 'ActualParam1', 1 ); + String returnFromDouble2 = classToDouble.methodUnderDouble( 'ActualParam1', 1 ); + Test.stopTest(); + + System.assertEquals( 'expectedReturn1', returnFromDouble1, 'when.method.withParameter.willReturn multiple times, when the method is defined with the same parameters, and the method called with the stated parameters, will always return the first match (1st call)' ); + System.assertEquals( 'expectedReturn1', returnFromDouble2, 'when.method.withParameter.willReturn multiple times, when the method is defined with the same parameters, and the method called with the stated parameters, will always return the first match (2nd call)' ); + } + + /** + * @method when.method.withParameter.willReturn multiple times, position first, then named + * @case when the same method is defiend with the same parameters and it is called multiple times + * @result will always return the first value with parameters that match + */ + @isTest + private static void whenWithParameterWillReturnMultiplePositionFirstThenNamed_whenSameParamsUsed_alwaysReturnTheFirstMatch() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .when() + .method( 'methodUnderDouble' ) + .withParameter( 'ActualParam1' ) + .thenParameter( 1 ) + .willReturn( 'expectedReturn1' ) + .also().when() + .method( 'methodUnderDouble' ) + .withParameterNamed( 'parameter1' ).setTo( 'ActualParam1' ) + .andParameterNamed( 'parameter2' ) .setTo( 1 ) + .willReturn( 'expectedReturn1' ); + + Test.startTest(); + String returnFromDouble1 = classToDouble.methodUnderDouble( 'ActualParam1', 1 ); + String returnFromDouble2 = classToDouble.methodUnderDouble( 'ActualParam1', 1 ); + Test.stopTest(); + + System.assertEquals( 'expectedReturn1', returnFromDouble1, 'when.method.withParameter.willReturn multiple times, position notation used first, then named notation, when the method is defined with the same parameters, and the method called with the stated parameters, will always return the first match (1st call)' ); + System.assertEquals( 'expectedReturn1', returnFromDouble2, 'when.method.withParameter.willReturn multiple times, position notation used first, then named notation, when the method is defined with the same parameters, and the method called with the stated parameters, will always return the first match (2nd call)' ); + } + + /** + * @method when.method.withParameter.willReturn multiple times, named first, then position + * @case when the same method is defiend with the same parameters and it is called multiple times + * @result will always return the first value with parameters that match + */ + @isTest + private static void whenWithParameterWillReturnMultipleNamedFirstThenPosition_whenSameParamsUsed_alwaysReturnTheFirstMatch() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .when() + .method( 'methodUnderDouble' ) + .withParameterNamed( 'parameter1' ).setTo( 'ActualParam1' ) + .andParameterNamed( 'parameter2' ) .setTo( 1 ) + .willReturn( 'expectedReturn1' ) + .also().when() + .method( 'methodUnderDouble' ) + .withParameter( 'ActualParam1' ) + .thenParameter( 1 ) + .willReturn( 'expectedReturn1' ); + + Test.startTest(); + String returnFromDouble1 = classToDouble.methodUnderDouble( 'ActualParam1', 1 ); + String returnFromDouble2 = classToDouble.methodUnderDouble( 'ActualParam1', 1 ); + Test.stopTest(); + + System.assertEquals( 'expectedReturn1', returnFromDouble1, 'when.method.withParameter.willReturn multiple times, named notation used first, then position notation, when the method is defined with the same parameters, and the method called with the stated parameters, will always return the first match (1st call)' ); + System.assertEquals( 'expectedReturn1', returnFromDouble2, 'when.method.withParameter.willReturn multiple times, named notation used first, then position notation, when the method is defined with the same parameters, and the method called with the stated parameters, will always return the first match (2nd call)' ); + } + + /** + * @method when.method.withParameter.willReturn multiple times + * @case when different methods are defined with the same parameters + * @result will return the stated value for each call, based on the method called + */ + @isTest + private static void whenWithParameterWillReturnMultiple_whenDiffMethodsDefined_returnSetUpValues() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .when() + .method( 'methodUnderDouble' ) + .withParameter( 'ActualParam1' ) + .thenParameter( 1 ) + .willReturn( 'expectedReturn1' ) + .also().when() + .method( 'otherMethodUnderDouble' ) + .withParameter( 'ActualParam1' ) + .thenParameter( 1 ) + .willReturn( 'expectedReturn2' ); + + Test.startTest(); + String returnFromDouble1 = classToDouble.methodUnderDouble( 'ActualParam1', 1 ); + String returnFromDouble2 = classToDouble.otherMethodUnderDouble( 'ActualParam1', 1 ); + Test.stopTest(); + + System.assertEquals( 'expectedReturn1', returnFromDouble1, 'when.method.withParameter.willReturn multiple times, with different methods with the same parameters, will return the stated value for the matching method (1)' ); + System.assertEquals( 'expectedReturn2', returnFromDouble2, 'when.method.withParameter.willReturn multiple times, with different methods with the same parameters, will return the stated value for the matching method (2)' ); + } + + /** + * @method when.method.withAnyParameter.willReturn + * @case and multiple method calls are defined + * @result will match in order, returning the first definition that matches + */ + @isTest + private static void whenWithAnyParameterWillReturnMultiple_whenCallWithParams_returnTheFirstMatching() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .when() + .method( 'methodUnderDouble' ) + .withParameter( 'ActualParam1' ) + .thenParameter( 1 ) + .willReturn( 'willNotSeeThis' ) + .also().when() + .method( 'methodUnderDouble' ) + .withAnyParameter() + .thenParameter( 1 ) + .willReturn( 'expectedReturnFor1' ) + .also().when() + .method( 'methodUnderDouble' ) + .withAnyParameters() + .willReturn( 'expectedReturnForAnythingElse' ); + + Test.startTest(); + String returnFromDouble1 = classToDouble.methodUnderDouble( 'DropToWithAnyParam' , 1 ); + String returnFromDouble2 = classToDouble.methodUnderDouble( 'DropToWithAnyParams', 100 ); + Test.stopTest(); + + System.assertEquals( 'expectedReturnFor1' , returnFromDouble1, 'when.method.withParameter.willReturn multiple times, and the method called with parameters that match many options, will return the first matching value (1)' ); + System.assertEquals( 'expectedReturnForAnythingElse', returnFromDouble2, 'when.method.withParameter.willReturn multiple times, and the method called with parameters that match many options, will return the first matching value (2)' ); + } + + /** + * @method allows.method.returning + * @case when that method is called + * @result will return the stated value + */ + @isTest + private static void allowsMethodReturning_whenCalled_returnSetUpValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .allows() + .method( 'methodUnderDouble' ) + .returning( 'expectedReturn' ); + + Test.startTest(); + String returnFromDouble = classToDouble.methodUnderDouble( 'OtherActualParam1', 1 ); + Test.stopTest(); + + System.assertEquals( 'expectedReturn', returnFromDouble, 'allows.method.returning, and the method called, will return the stated value, regardless of the parameters passed' ); + } + + /** + * @method allows.method.returning (shortcut) + * @case when that method is called + * @result will return the stated value + */ + @isTest + private static void allowsMethodReturningShortcut_whenCalled_returnSetUpValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .allows( 'methodUnderDouble' ) + .returning( 'expectedReturn' ); + + Test.startTest(); + String returnFromDouble = classToDouble.methodUnderDouble( 'OtherActualParam1', 1 ); + Test.stopTest(); + + System.assertEquals( 'expectedReturn', returnFromDouble, 'allows.method.returning (shortcut), and the method called, will return the stated value, regardless of the parameters passed' ); + } + /** + * @method allows.method.returning + * @case when a different method is called + * @result will fail the test + */ + @isTest + private static void allowsMethodReturning_whenADiffMethodIsCalled_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .allows() + .method( 'methodUnderDouble' ) + .returning( 'expectedReturn' ); + + Test.startTest(); + try { + String returnFromDouble = classToDouble.otherMethodUnderDouble( 'OtherActualParam1', 1 ); + } catch( TestException e ) {} + Test.stopTest(); + + String expectedAssertion = CLASS_TO_DOUBLE + '.otherMethodUnderDouble was called more times than was expected, and no matching "when" or "allows" exists'; + System.assertEquals( false, assertsDoubleController.get().latestCallOf( 'assert' ).parameter( 0 ), 'allows.method.returning, when a different method is called, will fail by calling assert with false' ); + Amoss_Asserts.assertContains( expectedAssertion, (String)assertsDoubleController.get().latestCallOf( 'assert' ).parameter( 1 ), 'allows.method.returning, when a different method is called, will fail by calling assert with an assertion message that clearly describes the issue' ); + } + + /** + * @method allows.method.returning + * @case when the method is called with different parameters + * @result will fail the test + */ + @isTest + private static void allowsMethodReturning_whenCallWithDiffParams_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .allows() + .method( 'methodUnderDouble' ) + .withParameter( 'ActualParam1' ) + .thenParameter( 1 ) + .returning( 'expectedReturn' ); + + Test.startTest(); + try { + String returnFromDouble = classToDouble.otherMethodUnderDouble( 'OtherActualParam1', 1 ); + } catch( TestException e ) {} + Test.stopTest(); + + String expectedAssertion = CLASS_TO_DOUBLE + '.otherMethodUnderDouble was called more times than was expected, and no matching "when" or "allows" exists'; + System.assertEquals( false, assertsDoubleController.get().latestCallOf( 'assert' ).parameter( 0 ), 'allows.method.returning, when the method is called with different parameters, will fail by calling assert with false' ); + Amoss_Asserts.assertContains( expectedAssertion, (String)assertsDoubleController.get().latestCallOf( 'assert' ).parameter( 1 ), 'allows.method.returning, when the method is called with different parameters, will fail by calling assert with an assertion message that clearly describes the issue' ); + } + + /** + * @method allows.method.returning + * @case when multiple methods are defined and called + * @result will return the stated value for each method + */ + @isTest + private static void allowsMethodReturning_whenMultipleMethodsAreDefinedAndCalled_returnSetUpValues() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .allows() + .method( 'methodUnderDouble' ) + .returning( 'expectedReturn1' ) + .also().allows() + .method( 'otherMethodUnderDouble' ) + .returning( 'expectedReturn2' ); + + Test.startTest(); + String returnFromDouble1 = classToDouble.methodUnderDouble( 'ActualParam1', 1 ); + String returnFromDouble2 = classToDouble.otherMethodUnderDouble( 'ActualParam1', 1 ); + Test.stopTest(); + + System.assertEquals( 'expectedReturn1', returnFromDouble1, 'allows.method.returning, when multiple methods are defined and each are called, will return the stated value for each method (1)' ); + System.assertEquals( 'expectedReturn2', returnFromDouble2, 'allows.method.returning, when multiple methods are defined and each are called, will return the stated value for each method (2)' ); + } + + /** + * @method allows.method.returning + * @case when the same method is defined with different parameters + * @result will return the stated value for the first matching + */ + @isTest + private static void allowsMethodReturning_whenMethodIsDefinedWithMultipleParams_returnTheFirstThatMatches() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .allows() + .method( 'methodUnderDouble' ) + .withParameter( 'ActualParam1' ) + .thenParameter( 1 ) + .returning( 'returnForExactMatch' ) + .also().allows() + .method( 'methodUnderDouble' ) + .withParameter( 'ActualParam1' ) + .thenAnyParameter() + .returning( 'returnForFirstParamMatch' ) + .also().allows() + .method( 'methodUnderDouble' ) + .withAnyParameters() + .returning( 'returnForGenericMatch' ); + + Test.startTest(); + String returnFromExactMatch = classToDouble.methodUnderDouble( 'ActualParam1', 1 ); + String returnFromFirstParamMatch = classToDouble.methodUnderDouble( 'ActualParam1', 100 ); + String returnFromGenericMatch = classToDouble.methodUnderDouble( 'AnyOleParam', 100 ); + Test.stopTest(); + + System.assertEquals( 'returnForExactMatch' , returnFromExactMatch , 'allows.method.returning, when method is defined with multiple overlapping parameters, will return the first stated value that matches the parameters (1)' ); + System.assertEquals( 'returnForFirstParamMatch', returnFromFirstParamMatch, 'allows.method.returning, when method is defined with multiple overlapping parameters, will return the first stated value that matches the parameters (2)' ); + System.assertEquals( 'returnForGenericMatch' , returnFromGenericMatch , 'allows.method.returning, when method is defined with multiple overlapping parameters, will return the first stated value that matches the parameters (3)' ); + } + + /** + * @method expectsNoCalls + * @case when a method is called + * @result will fail the test + */ + @isTest + private static void expectsNoCalls_whenAMethodIsCalled_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expectsNoCalls(); + + Test.startTest(); + try { + String returnFromDouble = classToDouble.methodUnderDouble( 'OtherActualParam1', 1 ); + } catch( TestException e ) {} + Test.stopTest(); + + System.assertEquals( '' , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 0 ), 'expectsNoCalls, when a method is called, will fail by calling assertEquals with an empty string as expected' ); + + String expectedMethodCall = 'methodUnderDouble(OtherActualParam1, 1)'; + System.assertEquals( expectedMethodCall, assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 1 ), 'expectsNoCalls, when a method is called, will fail by calling assertEquals with the method called as actual' ); + + String expectedAssertion = CLASS_TO_DOUBLE + ' did not expect any methods to be called'; + System.assertEquals( expectedAssertion, assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 2 ), 'expectsNoCalls, when a method is called, will fail by calling assertEquals with an assertion message that clearly describes the issue' ); + } + + /** + * @method expectsNoCalls.verify + * @case when no method is called + * @result will pass the test + */ + @isTest + private static void expectsNoCalls_whenAMethodIsNotCalled_passTheTest() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expectsNoCalls(); + + Test.startTest(); + classToDoubleController.verify(); + Test.stopTest(); + } + + /** + * @method expects.method.returning + * @case when that method is called once + * @result will return the stated value + */ + @isTest + private static void expectsMethodReturning_whenCalledOnce_returnSetUpValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodUnderDouble' ) + .returning( 'expectedReturn' ); + + Test.startTest(); + String returnFromDouble = classToDouble.methodUnderDouble( 'OtherActualParam1', 1 ); + Test.stopTest(); + + System.assertEquals( 'expectedReturn', returnFromDouble, 'expects.method.returning, and the method called once, will return the stated value, regardless of the parameters passed' ); + } + + /** + * @method expects.method.returning (shortcut) + * @case when that method is called once + * @result will return the stated value + */ + @isTest + private static void expectsMethodReturningShortcut_whenCalledOnce_returnSetUpValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects( 'methodUnderDouble' ) + .returning( 'expectedReturn' ); + + Test.startTest(); + String returnFromDouble = classToDouble.methodUnderDouble( 'OtherActualParam1', 1 ); + Test.stopTest(); + + System.assertEquals( 'expectedReturn', returnFromDouble, 'expects.method.returning (shortcut), and the method called once, will return the stated value, regardless of the parameters passed' ); + } + + /** + * @method expects.method.returning + * @case when a different method is called + * @result will fail the test + */ + @isTest + private static void expectsMethodReturning_whenADiffMethodIsCalled_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodUnderDouble' ) + .returning( 'expectedReturn' ); + + Test.startTest(); + try { + String returnFromDouble = classToDouble.otherMethodUnderDouble( 'OtherActualParam1', 1 ); + } catch( TestException e ) {} + Test.stopTest(); + + String expectedAssertion = CLASS_TO_DOUBLE + '.methodUnderDouble was expected to be called'; + System.assertEquals( 'methodUnderDouble' , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 0 ), 'expects.method.returning, when a different method is called, will fail by calling assertEquals with the expected method' ); + System.assertEquals( 'otherMethodUnderDouble', assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 1 ), 'expects.method.returning, when a different method is called, will fail by calling assertEquals with the actual method called' ); + System.assertEquals( expectedAssertion , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 2 ), 'expects.method.returning, when a different method is called, will fail by calling assertEquals with an assertion message that clearly describes the issue' ); + } + + /** + * @method expects.method.returning + * @case when the method is called with different parameters + * @result will fail the test + */ + @isTest + private static void expectsMethodReturning_whenCallWithDiffParams_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodUnderDouble' ) + .withParameter( 'ActualParam1' ) + .thenParameter( 1 ) + .returning( 'expectedReturn' ); + + Test.startTest(); + try { + String returnFromDouble = classToDouble.methodUnderDouble( 'OtherActualParam1', 1 ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterInPositionAssertion( assertsDoubleController, CLASS_TO_DOUBLE + '.methodUnderDouble', 0, 'ActualParam1', 'OtherActualParam1', 'expects.method.returning, when the method is called with different parameters' ); + } + + // + // + // Advanced Expectation Parameter Matching Tests + // + // + + /** + * @method expects.method.returning + * @case when the expectation is set with an object, and that instance is passed + * @result will not fail the test + */ + @isTest + private static void expectsMethodReturning_whenExpectsObject_andPassed_pass() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + AmossTest_ClassToDouble.PassableObject objectPassed = new AmossTest_ClassToDouble.PassableObject( 'TheObject' ); + + classToDoubleController + .expects() + .method( 'objectMethodUnderDouble' ) + .withParameter( objectPassed ) + .returning( 'expectedReturn' ); + + Test.startTest(); + String returnFromDouble = classToDouble.objectMethodUnderDouble( objectPassed ); + Test.stopTest(); + + classToDoubleController.verify(); + } + + /** + * @method expects.method.returning + * @case when the expectation is set with an object, and a different instance is passed with same properties + * @result will fail the test + */ + @isTest + private static void expectsMethodReturning_whenExpectsObject_andDiffInstPassed_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + AmossTest_ClassToDouble.PassableObject objectExpected = new AmossTest_ClassToDouble.PassableObject( 'TheObject' ); + + classToDoubleController + .expects() + .method( 'objectMethodUnderDouble' ) + .withParameter( objectExpected ) + .returning( 'expectedReturn' ); + + AmossTest_ClassToDouble.PassableObject objectPassed = new AmossTest_ClassToDouble.PassableObject( 'TheObject' ); + Test.startTest(); + try { + String returnFromDouble = classToDouble.objectMethodUnderDouble( objectPassed ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterInPositionAssertion( assertsDoubleController, CLASS_TO_DOUBLE + '.objectMethodUnderDouble', 0, objectExpected, objectPassed, 'expects.method.returning, when specified with an object and the method is called with a different instance' ); + } + + /** + * @method expects.method.setToTheSameValueAs.returning + * @case when the expectation is set with an object, and a different instance is passed with same properties + * @result will not fail the test + */ + @isTest + private static void expectsMethodSetToTheSameValueAsReturning_whenExpectsObject_andDiffInstPassed_pass() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + AmossTest_ClassToDouble.PassableObject objectExpected = new AmossTest_ClassToDouble.PassableObject( 'TheObject' ); + + classToDoubleController + .expects() + .method( 'objectMethodUnderDouble' ) + .withParameter().setToTheSameValueAs( objectExpected ) + .returning( 'expectedReturn' ); + + AmossTest_ClassToDouble.PassableObject objectPassed = new AmossTest_ClassToDouble.PassableObject( 'TheObject' ); + + Test.startTest(); + String returnFromDouble = classToDouble.objectMethodUnderDouble( objectPassed ); + Test.stopTest(); + + classToDoubleController.verify(); + } + + /** + * @method expects.method.setToTheSameValueAs.returning + * @case when the expectation is set with an object, and a different instance is passed with different properties + * @result will fail the test + */ + @isTest + private static void expectsMethodSetToTheSameValueAsReturning_whenExpectsObject_andDiffInstPassedWithDiffProps_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + AmossTest_ClassToDouble.PassableObject objectExpected = new AmossTest_ClassToDouble.PassableObject( 'TheObject' ); + + classToDoubleController + .expects() + .method( 'objectMethodUnderDouble' ) + .withParameter().setToTheSameValueAs( objectExpected ) + .returning( 'expectedReturn' ); + + AmossTest_ClassToDouble.PassableObject objectPassed = new AmossTest_ClassToDouble.PassableObject( 'TheOtherObject' ); + Test.startTest(); + try { + String returnFromDouble = classToDouble.objectMethodUnderDouble( objectPassed ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterInPositionAssertion( assertsDoubleController, CLASS_TO_DOUBLE + '.objectMethodUnderDouble', 0, objectExpected, objectPassed, 'expects.method.setToTheSameValueAs.returning, when specified with an object and the method is called with a different instance with different values' ); + } + + /** + * @method expects.method.returning + * @case when the expectation is set with an sobject, and that instance is passed + * @result will not fail the test + */ + @isTest + private static void expectsMethodReturning_whenExpectsSobject_andPassed_pass() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Contact objectPassed = new Contact( LastName = 'TheContact' ); + + classToDoubleController + .expects() + .method( 'sobjectMethodUnderDouble' ) + .withParameter( objectPassed ) + .returning( 'expectedReturn' ); + + Test.startTest(); + String returnFromDouble = classToDouble.sobjectMethodUnderDouble( objectPassed ); + Test.stopTest(); + + classToDoubleController.verify(); + } + + /** + * @method expects.method.returning + * @case when the expectation is set with an sobject, and a different instance is passed with same properties + * @result will fail the test + */ + @isTest + private static void expectsMethodReturning_whenExpectsSobject_andDiffInstPassed_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Contact objectExpected = new Contact( LastName = 'TheObject' ); + + classToDoubleController + .expects() + .method( 'sobjectMethodUnderDouble' ) + .withParameter( objectExpected ) + .returning( 'expectedReturn' ); + + Contact objectPassed = new Contact( LastName = 'TheObject' ); + Test.startTest(); + try { + String returnFromDouble = classToDouble.sobjectMethodUnderDouble( objectPassed ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterInPositionInstanceAssertion( assertsDoubleController, CLASS_TO_DOUBLE + '.sobjectMethodUnderDouble', 0, objectExpected, objectPassed, 'expects.method.returning, when specified with an sobject and the method is called with a different instance' ); + } + + /** + * @method expects.method.setToTheSameValueAs.returning + * @case when the expectation is set with an sobject, and a different instance is passed with same properties + * @result will not fail the test + */ + @isTest + private static void expectsMethodSetToTheSameValueAsReturning_whenExpectsSobject_andDiffInstPassed_pass() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Contact objectExpected = new Contact( LastName = 'TheObject' ); + + classToDoubleController + .expects() + .method( 'sobjectMethodUnderDouble' ) + .withParameter().setToTheSameValueAs( objectExpected ) + .returning( 'expectedReturn' ); + + Contact objectPassed = new Contact( LastName = 'TheObject' ); + Test.startTest(); + String returnFromDouble = classToDouble.sobjectMethodUnderDouble( objectPassed ); + Test.stopTest(); + + classToDoubleController.verify(); + } + + /** + * @method expects.method.setToTheSameValueAs.returning + * @case when the expectation is set with an sobject, and a different instance is passed with different properties + * @result will fail the test + */ + @isTest + private static void expectsMethodSetToTheSameValueAsReturning_whenExpectsSobject_andDiffInstPassedWithDiffValues_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Contact objectExpected = new Contact( LastName = 'TheObject' ); + + classToDoubleController + .expects() + .method( 'sobjectMethodUnderDouble' ) + .withParameter().setToTheSameValueAs( objectExpected ) + .returning( 'expectedReturn' ); + + Contact objectPassed = new Contact( LastName = 'TheOtherObject' ); + Test.startTest(); + try { + String returnFromDouble = classToDouble.sobjectMethodUnderDouble( objectPassed ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterInPositionAssertion( assertsDoubleController, CLASS_TO_DOUBLE + '.sobjectMethodUnderDouble', 0, objectExpected, objectPassed, 'expects.method.setToTheSameValueAs.returning, when specified with an sobject and the method is called with a different instance with different values' ); + } + + /** + * @method expects.method.withFieldsSetLike.returning + * @case when the expectation is set with an sobject, and that instance is passed + * @result will not fail the test + */ + @isTest + private static void expectsWithFieldsSetLikeReturning_whenExpectsSobject_andPassed_pass() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Contact objectPassed = new Contact( LastName = 'ExpectedLast', FirstName = 'ExpectedFirst' ); + + classToDoubleController + .expects() + .method( 'sobjectMethodUnderDouble' ) + .withParameter().withFieldsSetLike( objectPassed ) + .returning( 'expectedReturn' ); + + Test.startTest(); + String returnFromDouble = classToDouble.sobjectMethodUnderDouble( objectPassed ); + Test.stopTest(); + + classToDoubleController.verify(); + } + + /** + * @method expects.method.withFieldsSetLike.returning + * @case when the expectation is set with an sobject, and a different instance is passed with same properties + * @result will not fail the test + */ + @isTest + private static void expectsWithFieldsSetLikeReturning_whenDiffInstPassedWithSameProps_pass() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Contact objectExpected = new Contact( LastName = 'ExpectedLast', FirstName = 'ExpectedFirst' ); + + classToDoubleController + .expects() + .method( 'sobjectMethodUnderDouble' ) + .withParameter().withFieldsSetLike( objectExpected ) + .returning( 'expectedReturn' ); + + Contact objectPassed = new Contact( LastName = 'ExpectedLast', FirstName = 'ExpectedFirst' ); + Test.startTest(); + String returnFromDouble = classToDouble.sobjectMethodUnderDouble( objectPassed ); + Test.stopTest(); + + classToDoubleController.verify(); + } + + /** + * @method expects.method.withFieldsSetLike.returning + * @case when the expectation is set with an sobject, and a different instance is passed with extra properties + * @result will not fail the test + */ + @isTest + private static void expectsWithFieldsSetLikeReturning_whenDiffInstPassedWithExtraProps_pass() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Contact objectExpected = new Contact( LastName = 'ExpectedLast', FirstName = 'ExpectedFirst' ); + + classToDoubleController + .expects() + .method( 'sobjectMethodUnderDouble' ) + .withParameter().withFieldsSetLike( objectExpected ) + .returning( 'expectedReturn' ); + + Contact objectPassed = new Contact( LastName = 'ExpectedLast', FirstName = 'ExpectedFirst', MobilePhone = '1234' ); + Test.startTest(); + String returnFromDouble = classToDouble.sobjectMethodUnderDouble( objectPassed ); + Test.stopTest(); + + classToDoubleController.verify(); + } + + /** + * @method expects.method.withFieldsSetLike.returning + * @case when the expectation is set with an sobject, and a different instance is passed with different properties + * @result will fail the test + */ + @isTest + private static void expectsWithFieldsSetLikeReturning_whenDiffInstPassedWithDiffProps_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Contact objectExpected = new Contact( LastName = 'ExpectedLast', FirstName = 'ExpectedFirst' ); + + classToDoubleController + .expects() + .method( 'sobjectMethodUnderDouble' ) + .withParameter().withFieldsSetLike( objectExpected ) + .returning( 'expectedReturn' ); + + Contact objectPassed = new Contact( LastName = 'ExpectedLast', FirstName = 'DifferentFirst' ); + Test.startTest(); + try { + String returnFromDouble = classToDouble.sobjectMethodUnderDouble( objectPassed ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterFieldsInPositionAssertion( assertsDoubleController, CLASS_TO_DOUBLE + '.sobjectMethodUnderDouble', 0, objectExpected, objectPassed, 'expects.method.withFieldsSetLike.returning, when specified with an sobject and the method is called with a different instance with different properties' ); + } + + /** + * @method expects.method.withFieldsSetLike.returning + * @case when the expectation is set with an sobject, and a different instance is passed with fewer properties + * @result will fail the test + */ + @isTest + private static void expectsWithFieldsSetLikeReturning_whenDiffInstPassedWithFewerProps_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Contact objectExpected = new Contact( LastName = 'ExpectedLast', FirstName = 'ExpectedFirst' ); + + classToDoubleController + .expects() + .method( 'sobjectMethodUnderDouble' ) + .withParameter().withFieldsSetLike( objectExpected ) + .returning( 'expectedReturn' ); + + Contact objectPassed = new Contact( LastName = 'ExpectedLast' ); + Test.startTest(); + try { + String returnFromDouble = classToDouble.sobjectMethodUnderDouble( objectPassed ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterFieldsInPositionAssertion( assertsDoubleController, CLASS_TO_DOUBLE + '.sobjectMethodUnderDouble', 0, objectExpected, objectPassed, 'expects.method.withFieldsSetLike.returning, when specified with an sobject and the method is called with a different instance with fewer properties' ); + } + + /** + * @method expects.method.withFieldsSetLike.returning + * @case when the expectation is set with an sobject, and null is passed + * @result will fail the test + */ + @isTest + private static void expectsWithFieldsSetLikeReturning_whenExpectsSobject_andNullIsPassed_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Contact objectExpected = new Contact( LastName = 'ExpectedLast', FirstName = 'ExpectedFirst' ); + + classToDoubleController + .expects() + .method( 'sobjectMethodUnderDouble' ) + .withParameter().withFieldsSetLike( objectExpected ) + .returning( 'expectedReturn' ); + + Contact nullContact; + + Test.startTest(); + try { + String returnFromDouble = classToDouble.sobjectMethodUnderDouble( nullContact ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForNullSObjectInPositionInstanceAssertion( assertsDoubleController, CLASS_TO_DOUBLE + '.sobjectMethodUnderDouble', 0, objectExpected, null, 'expects.method.withFieldsSetLike.returning, when specified with an sobject and the method is called with null' ); + } + + /** + * @method expects.method.withFieldsSetLike.returning + * @case when the parameter passed is not an sObject + * @result will fail the test + */ + @isTest + private static void expectsWithFieldsSetLikeReturning_whenParameterPassedIsNotAnSobject_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Contact objectExpected = new Contact( LastName = 'ExpectedLast', FirstName = 'ExpectedFirst' ); + + classToDoubleController + .expects() + .method( 'sobjectMethodUnderDouble' ) + .withParameter().withFieldsSetLike( objectExpected ) + .returning( 'expectedReturn' ); + + Test.startTest(); + try { + String returnFromDouble = classToDouble.sobjectMethodUnderDouble( 'NotAnSobject' ); // possible because of overloading + } catch( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterInPositionAssertion( assertsDoubleController, + 'Expected an sObject, and got String', + CLASS_TO_DOUBLE + '.sobjectMethodUnderDouble', 0, + objectExpected, 'NotAnSobject', + 'expects.method.withFieldsSetLike.returning, when the parameter passed is not an sObject' ); + } + + /** + * @method expects.method.withFieldsSetLike.returning + * @case when the expectation is set with null + * @result will fail the test + */ + @isTest + private static void expectsWithFieldsSetLikeReturning_whenExpectsIsSetWithNull_fail() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + String exceptionMessage; + + Test.startTest(); + try { + classToDoubleController + .expects() + .method( 'sobjectMethodUnderDouble' ) + .withParameter().withFieldsSetLike( null ) + .returning( 'expectedReturn' ); + } catch ( Amoss_Instance.Amoss_ExpectedObjectCannotBeNullException e ) { + exceptionMessage = e.getMessage(); + } + Test.stopTest(); + + System.assertEquals( 'Cannot specify NULL for a "FieldsSetLike" expectation', exceptionMessage, 'expects.method.withFieldsSetLike.returning, when the expectation is set with null, will throw an exception detailing the problem' ); + } + + /** + * @method expects.method.withFieldsSetTo.returning + * @case when the expectation is set with an map, and an sobject passed with same properties + * @result will not fail the test + */ + @isTest + private static void expectsWithFieldsSetToReturning_whenSobjectPassedWithSameProps_pass() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Map objectExpected = new Map{ 'LastName' => 'ExpectedLast', 'FirstName' => 'ExpectedFirst' }; + + classToDoubleController + .expects() + .method( 'sobjectMethodUnderDouble' ) + .withParameter().withFieldsSetTo( objectExpected ) + .returning( 'expectedReturn' ); + + Contact objectPassed = new Contact( LastName = 'ExpectedLast', FirstName = 'ExpectedFirst' ); + Test.startTest(); + String returnFromDouble = classToDouble.sobjectMethodUnderDouble( objectPassed ); + Test.stopTest(); + + classToDoubleController.verify(); + } + + /** + * @method expects.method.withParameterNamed.withFieldsSetTo.returning + * @case when the expectation is set with an map, and an sobject passed with same properties + * @result will not fail the test + */ + @isTest + private static void expectsWithParameterNamedWithFieldsSetToReturning_whenSobjectPassedWithSameProps_pass() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Map objectExpected = new Map{ 'LastName' => 'ExpectedLast', 'FirstName' => 'ExpectedFirst' }; + + classToDoubleController + .expects() + .method( 'sobjectMethodUnderDouble' ) + .withParameterNamed( 'contactParam' ).withFieldsSetTo( objectExpected ) + .returning( 'expectedReturn' ); + + Contact objectPassed = new Contact( LastName = 'ExpectedLast', FirstName = 'ExpectedFirst' ); + Test.startTest(); + String returnFromDouble = classToDouble.sobjectMethodUnderDouble( objectPassed ); + Test.stopTest(); + + classToDoubleController.verify(); + } + + /** + * @method expects.method.withFieldsSetTo.returning + * @case when the expectation is set, and an sObject is passed with extra properties + * @result will not fail the test + */ + @isTest + private static void expectsWithFieldsSetToReturning_whenSobjectPassedWithExtraProps_pass() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Map objectExpected = new Map{ 'LastName' => 'ExpectedLast', 'FirstName' => 'ExpectedFirst' }; + + classToDoubleController + .expects() + .method( 'sobjectMethodUnderDouble' ) + .withParameter().withFieldsSetTo( objectExpected ) + .returning( 'expectedReturn' ); + + Contact objectPassed = new Contact( LastName = 'ExpectedLast', FirstName = 'ExpectedFirst', MobilePhone = '1234' ); + Test.startTest(); + String returnFromDouble = classToDouble.sobjectMethodUnderDouble( objectPassed ); + Test.stopTest(); + + classToDoubleController.verify(); + } + + /** + * @method expects.method.withFieldsSetTo.returning + * @case when the expectation is set, and an sObject is passed with different properties + * @result will fail the test + */ + @isTest + private static void expectsWithFieldsSetToReturning_whenSobjectWithDiffProps_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Map objectExpected = new Map{ 'LastName' => 'ExpectedLast', 'FirstName' => 'ExpectedFirst' }; + + classToDoubleController + .expects() + .method( 'sobjectMethodUnderDouble' ) + .withParameter().withFieldsSetTo( objectExpected ) + .returning( 'expectedReturn' ); + + Contact objectPassed = new Contact( LastName = 'ExpectedLast', FirstName = 'DifferentFirst' ); + Test.startTest(); + try { + String returnFromDouble = classToDouble.sobjectMethodUnderDouble( objectPassed ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterFieldsInPositionAssertion( assertsDoubleController, CLASS_TO_DOUBLE + '.sobjectMethodUnderDouble', 0, objectExpected, objectPassed, 'expects.method.withFieldsSetTo.returning, when the method is called with an sObject with different properties' ); + } + + /** + * @method expects.method.withFieldsSetTo.returning + * @case when the expectation is set, and an sObject is passed with fewer properties + * @result will fail the test + */ + @isTest + private static void expectsWithFieldsSetToReturning_whenSobjectPassedWithFewerProps_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Map objectExpected = new Map{ 'LastName' => 'ExpectedLast', 'FirstName' => 'ExpectedFirst' }; + + classToDoubleController + .expects() + .method( 'sobjectMethodUnderDouble' ) + .withParameter().withFieldsSetTo( objectExpected ) + .returning( 'expectedReturn' ); + + Contact objectPassed = new Contact( LastName = 'ExpectedLast' ); + Test.startTest(); + try { + String returnFromDouble = classToDouble.sobjectMethodUnderDouble( objectPassed ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterFieldsInPositionAssertion( assertsDoubleController, CLASS_TO_DOUBLE + '.sobjectMethodUnderDouble', 0, objectExpected, objectPassed, 'expects.method.withFieldsSetTo.returning, when the method is called with an Sobject with fewer properties' ); + } + + /** + * @method expects.method.withFieldsSetTo.returning + * @case when the expectation is set, and null is passed + * @result will fail the test + */ + @isTest + private static void expectsWithFieldsSetToReturning_whenExpectsIsSetAndNullIsPassed_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Map objectExpected = new Map{ 'LastName' => 'ExpectedLast', 'FirstName' => 'ExpectedFirst' }; + + classToDoubleController + .expects() + .method( 'sobjectMethodUnderDouble' ) + .withParameter().withFieldsSetTo( objectExpected ) + .returning( 'expectedReturn' ); + + Contact nullContact; + + Test.startTest(); + try { + String returnFromDouble = classToDouble.sobjectMethodUnderDouble( nullContact ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForNullSObjectInPositionInstanceAssertion( assertsDoubleController, CLASS_TO_DOUBLE + '.sobjectMethodUnderDouble', 0, objectExpected, null, 'expects.method.withFieldsSetTo.returning, when specified, and the method is called with null' ); + } + + /** + * @method expects.method.withFieldsSetTo.returning + * @case when the expectation is set with null + * @result will fail the test + */ + @isTest + private static void expectsWithFieldsSetToReturning_whenExpectsIsSetWithNull_fail() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + String exceptionMessage; + + Test.startTest(); + try { + classToDoubleController + .expects() + .method( 'sobjectMethodUnderDouble' ) + .withParameter().withFieldsSetTo( null ) + .returning( 'expectedReturn' ); + } catch ( Amoss_Instance.Amoss_ExpectedObjectCannotBeNullException e ) { + exceptionMessage = e.getMessage(); + } + Test.stopTest(); + + System.assertEquals( 'Cannot specify NULL for a "FieldsSetTo" expectation', exceptionMessage, 'expects.method.withFieldsSetTo.returning, when the expectation is set with null, will throw an exception detailing the problem' ); + } + + /** + * @method expects.method.withFieldsSetTo.returning + * @case when the expectation is set with an invalid field + * @result will fail the test + */ + @isTest + private static void expectsWithFieldsSetToReturning_whenExpectIsSetWithAnInvalidField_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Map objectExpected = new Map{ 'LastName' => 'ExpectedLast', 'FirstName' => 'ExpectedFirst', 'InvalidField' => 'A value' }; + + classToDoubleController + .expects() + .method( 'sobjectMethodUnderDouble' ) + .withParameter().withFieldsSetTo( objectExpected ) + .returning( 'expectedReturn' ); + + Contact objectPassed = new Contact( LastName = 'ExpectedLast', FirstName = 'ExpectedFirst' ); + Test.startTest(); + try { + String returnFromDouble = classToDouble.sobjectMethodUnderDouble( objectPassed ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterInPositionAssertion( assertsDoubleController, + 'Problem comparing field "InvalidField": Invalid field InvalidField for Contact', + CLASS_TO_DOUBLE + '.sobjectMethodUnderDouble', 0, + 'expects.method.withFieldsSetTo.returning, when the expectation is set with an invalid field' ); + } + + /** + * @method expects.method.withFieldsSetTo.returning + * @case when the parameter passed is not an sObject + * @result will fail the test + */ + @isTest + private static void expectsWithFieldsSetToReturning_whenParameterPassedIsNotAnSobject_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Map objectExpected = new Map{ 'LastName' => 'ExpectedLast', 'FirstName' => 'ExpectedFirst' }; + + classToDoubleController + .expects() + .method( 'sobjectMethodUnderDouble' ) + .withParameter().withFieldsSetTo( objectExpected ) + .returning( 'expectedReturn' ); + + Test.startTest(); + try { + String returnFromDouble = classToDouble.sobjectMethodUnderDouble( 'NotAnSobject' ); // possible because of overloading + } catch( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterInPositionAssertion( assertsDoubleController, + 'Expected an sObject, and got String', + CLASS_TO_DOUBLE + '.sobjectMethodUnderDouble', 0, + objectExpected, 'NotAnSobject', + 'expects.method.withFieldsSetTo.returning, when the parameter passed is not an sObject' ); + } + + /** + * @method expects.method.withAllElements.setTo.returning + * @case when the list parameter passed does matches all elements + * @result will pass the test + */ + @isTest + private static void expectsWithAllElementsSetToReturning_whenAllElementsInTheParameterMatch_pass() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + List expectedReturn = new List{ 'expectedReturn' }; + + classToDoubleController + .expects() + .method( 'methodWithListOfObjects' ) + .withParameter().withAllElements().setTo( 'expected' ) + .returning( expectedReturn ); + + Test.startTest(); + List returnFromDouble = classToDouble.methodWithListOfObjects( new List{ 'expected', 'expected' } ); + Test.stopTest(); + + classToDoubleController.verify(); + + System.assertEquals( expectedReturn, returnFromDouble, 'expects.method.withAllElements.setTo.returning, when the list parameter passed does matches all elements, will return the specified value' ); + } + + /** + * @method expects.method.withAllElements.setTo.returning + * @case when the list parameter passed does not match all elements + * @result will fail the test + */ + @isTest + private static void expectsWithAllElementsSetToReturning_whenNotAllElementsInTheParameterMatch_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodWithListOfObjects' ) + .withParameter().withAllElements().setTo( 'expected' ); + + Test.startTest(); + try { + List returnFromDouble = classToDouble.methodWithListOfObjects( new List{ 'expected', 'unexpected' } ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterInPositionAssertion( assertsDoubleController, + 'List element 1 does not match what was expected: The value is not what was expected', + CLASS_TO_DOUBLE + '.methodWithListOfObjects', 0, + 'expected', 'unexpected', + 'expects.method.withAllElements.returning, when the list parameter passed does not match every element' ); + } + + /** + * @method expects.method.withAllElements.setTo.returning + * @case when the list parameter passed is not a list + * @result will fail the test + */ + @isTest + private static void expectsWithAllElementsSetToReturning_whenTheParameterPassedIsNotAList_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodUnderDouble' ) + .withParameter().withAllElements().setTo( 'expected' ) + .thenAnyParameter() + .returning( 'expectedReturn' ); + + Test.startTest(); + try { + classToDouble.methodUnderDouble( 'not a list', 2 ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterInPositionAssertion( assertsDoubleController, + 'The types do not match', + CLASS_TO_DOUBLE + '.methodUnderDouble', 0, + 'List', 'String', + 'expects.method.withAllElements.returning, when the parameter passed is not a list' ); + } + + /** + * @method expects.method.withAnyElement.setTo.returning + * @case when the list parameter passed matches any of the elements + * @result will pass the test + */ + @isTest + private static void expectsWithAnyElementSetToReturning_whenAnyElementsInTheParameterMatch_pass() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + List expectedReturn = new List{ 'expectedReturn' }; + + classToDoubleController + .expects() + .method( 'methodWithListOfObjects' ) + .withParameter().withAnyElement().setTo( 'expected' ) + .returning( expectedReturn ); + + Test.startTest(); + List returnFromDouble = classToDouble.methodWithListOfObjects( new List{ 'unexpected', 'expected' } ); + Test.stopTest(); + + classToDoubleController.verify(); + + System.assertEquals( expectedReturn, returnFromDouble, 'expects.method.withAnyElement.setTo.returning, when the list parameter passed matches an element, will return the specified value' ); + } + + /** + * @method expects.method.withAnyElement.setTo.returning + * @case when the list parameter passed does not match all elements + * @result will fail the test + */ + @isTest + private static void expectsWithAnyElementSetToReturning_whenNoElementsInTheParameterMatch_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodWithListOfObjects' ) + .withParameter().withAnyElement().setTo( 'expected' ); + + Test.startTest(); + try { + List returnFromDouble = classToDouble.methodWithListOfObjects( new List{ 'unexpected', 'unexpected' } ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterInPositionAssertion( assertsDoubleController, + 'At least one element in the list should pass the described verification', + CLASS_TO_DOUBLE + '.methodWithListOfObjects', 0, + 'expected', '(unexpected, unexpected)', + 'expects.method.withAnyElement.returning, when the list parameter passed does not match every element' ); + } + + /** + * @method expects.method.withAnyElement.setTo.returning + * @case when the list parameter passed is not a list + * @result will fail the test + */ + @isTest + private static void expectsWithAnyElementSetToReturning_whenTheParameterPassedIsNotAList_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodUnderDouble' ) + .withParameter().withAnyElement().setTo( 'expected' ) + .thenAnyParameter() + .returning( 'expectedReturn' ); + + Test.startTest(); + try { + classToDouble.methodUnderDouble( 'not a list', 2 ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterInPositionAssertion( assertsDoubleController, + 'The types do not match', + CLASS_TO_DOUBLE + '.methodUnderDouble', 0, + 'List', 'String', + 'expects.method.withAnyElement.returning, when the parameter passed is not a list' ); + } + + /** + * @method expects.method.withElementAt.setTo.returning + * @case when the list parameter passed has the element matching + * @result will pass the test + */ + @isTest + private static void expectsWithElementAtSetToReturning_whenElementInTheParameterMatches_pass() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + List expectedReturn = new List{ 'expectedReturn' }; + + classToDoubleController + .expects() + .method( 'methodWithListOfObjects' ) + .withParameter().withElementAt( 1 ).setTo( 'expected' ) + .returning( expectedReturn ); + + Test.startTest(); + List returnFromDouble = classToDouble.methodWithListOfObjects( new List{ 'unexpected', 'expected' } ); + Test.stopTest(); + + classToDoubleController.verify(); + + System.assertEquals( expectedReturn, returnFromDouble, 'expects.method.withElementAt.setTo.returning, when the list parameter contains the element matching the specified value, will return the specified value' ); + } + + /** + * @method expects.method.withElementAt.setTo.returning + * @case when specified multiple times and the list parameter passed has the elements matching + * @result will pass the test + */ + @isTest + private static void expectsWithElementAtSetToReturningMultipleTimes_whenElementInTheParameterMatches_pass() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + List expectedReturn = new List{ 'expectedReturn' }; + + classToDoubleController + .expects() + .method( 'methodWithListOfObjects' ) + .withParameter() + .withElementAt( 0 ).setTo( 'expected1' ) + .withElementAt( 1 ).setTo( 'expected2' ) + .withElementAt( 2 ).setTo( 'expected3' ) + .returning( expectedReturn ); + + Test.startTest(); + List returnFromDouble = classToDouble.methodWithListOfObjects( new List{ 'expected1', 'expected2', 'expected3' } ); + Test.stopTest(); + + classToDoubleController.verify(); + + System.assertEquals( expectedReturn, returnFromDouble, 'expects.method.withElementAt.setTo.returning, when specified multiple times, and the list parameter contains the elements matching the specified value, will return the specified value' ); + } + + /** + * @method expects.method.withElementAt.setTo.returning + * @case when the list parameter's element does not match + * @result will fail the test + */ + @isTest + private static void expectsWithElementAtSetToReturning_whenSpecifiedElementDoesNotMatch_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodWithListOfObjects' ) + .withParameter().withElementAt( 0 ).setTo( 'expected' ); + + Test.startTest(); + try { + List returnFromDouble = classToDouble.methodWithListOfObjects( new List{ 'unexpected', 'unexpected' } ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterInPositionAssertion( assertsDoubleController, + 'List element 0 does not match what was expected: The value is not what was expected', + CLASS_TO_DOUBLE + '.methodWithListOfObjects', 0, + 'expected', 'unexpected', + 'expects.method.withElementAt.returning, when the list parameter\'s element does not match' ); + } + + /** + * @method expects.method.withElementAt.setTo.returning + * @case when specified multiple times and one of the list parameter's element does not match + * @result will fail the test + */ + @isTest + private static void expectsWithElementAtSetToReturningMultipleTimes_whenSpecifiedElementDoesNotMatch_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodWithListOfObjects' ) + .withParameter() + .withElementAt( 0 ).setTo( 'expected1' ) + .withElementAt( 1 ).setTo( 'expected2' ) + .withElementAt( 2 ).setTo( 'expected3' ); + + Test.startTest(); + try { + List returnFromDouble = classToDouble.methodWithListOfObjects( new List{ 'expected1', 'unexpected', 'expected3' } ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterInPositionAssertion( assertsDoubleController, + 'List element 1 does not match what was expected: The value is not what was expected', + CLASS_TO_DOUBLE + '.methodWithListOfObjects', 0, + 'expected2', 'unexpected', + 'expects.method.withElementAt.returning, when the list parameter\'s element does not match' ); + } + + /** + * @method expects.method.withElementAt.setTo.returning + * @case when the list parameter's element does not exist + * @result will fail the test + */ + @isTest + private static void expectsWithElementAtSetToReturning_whenSpecifiedElementDoesNotExist_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodWithListOfObjects' ) + .withParameter().withElementAt( 4 ).setTo( 'expected' ); + + Test.startTest(); + try { + List returnFromDouble = classToDouble.methodWithListOfObjects( new List{ 'unexpected', 'unexpected' } ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterInPositionAssertion( assertsDoubleController, + 'The value is not what was expected - The list is not as long as required', + CLASS_TO_DOUBLE + '.methodWithListOfObjects', 0, + 5, 2, + 'expects.method.withElementAt.returning, when the list parameter does not contain the specified element' ); + } + + /** + * @method expects.method.withElementAt.setTo.returning + * @case when the parameter is not a list + * @result will fail the test + */ + @isTest + private static void expectsWithElementAtSetToReturning_whenParameterIsNotAList_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodUnderDouble' ) + .withParameter().withElementAt( 1 ).setTo( 'expected' ) + .thenAnyParameter(); + + Test.startTest(); + try { + classToDouble.methodUnderDouble( 'not a list', 2 ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterInPositionAssertion( assertsDoubleController, + 'The value is not what was expected - The types do not match', + CLASS_TO_DOUBLE + '.methodUnderDouble', 0, + 'List', 'String', + 'expects.method.withElementAt.returning, when the parameter is not a list' ); + } + + /** + * @method expects.method.withElementAt.setTo.returning + * @case when the parameter is null + * @result will fail the test + */ + @isTest + private static void expectsWithElementAtSetToReturning_whenParameterIsNull_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodWithListOfObjects' ) + .withParameter().withElementAt( 1 ).setTo( 'expected' ); + + Test.startTest(); + try { + classToDouble.methodWithListOfObjects( null); + } catch( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterInPositionAssertion( assertsDoubleController, + 'The value is not what was expected - The types do not match', + CLASS_TO_DOUBLE + '.methodWithListOfObjects', 0, + 'List', 'Unknown (null)', + 'expects.method.withElementAt.returning, when the parameter is null' ); + } + + /** + * @method expects.method.withElementAt.set.returning + * @case when the list parameter passed has the element set to not null + * @result will pass the test + */ + @isTest + private static void expectsWithElementAtSetReturning_whenElementInTheParameterIsSet_pass() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + List expectedReturn = new List{ 'expectedReturn' }; + + classToDoubleController + .expects() + .method( 'methodWithListOfObjects' ) + .withParameter().withElementAt( 1 ).set() + .returning( expectedReturn ); + + Test.startTest(); + List returnFromDouble = classToDouble.methodWithListOfObjects( new List{ 'set', 'set' } ); + Test.stopTest(); + + classToDoubleController.verify(); + + System.assertEquals( expectedReturn, returnFromDouble, 'expects.method.withElementAt.set.returning, when the list parameter contains the element set to not null, will return the specified value' ); + } + + /** + * @method expects.method.withElementAt.set.returning + * @case when the element is null + * @result will fail the test + */ + @isTest + private static void expectsWithElementAtSetReturning_whenElementIsNull_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodWithListOfObjects' ) + .withParameter().withElementAt( 1 ).set(); + + Test.startTest(); + try { + classToDouble.methodWithListOfObjects( new List{ 'set', null } ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterInPositionAssertion( assertsDoubleController, + 'List element 1 does not match what was expected', + CLASS_TO_DOUBLE + '.methodWithListOfObjects', 0, + 'Not Null', 'null', + 'expects.method.withElementAt.set returning, when the element is null' ); + } + + /** + * @method expects.method.withAllElements.set.returning + * @case when the list parameter passed has the element set to not null + * @result will pass the test + */ + @isTest + private static void expectsWithAllElementsSetReturning_whenElementInTheParameterIsSet_pass() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + List expectedReturn = new List{ 'expectedReturn' }; + + classToDoubleController + .expects() + .method( 'methodWithListOfObjects' ) + .withParameter().withAllElements().set() + .returning( expectedReturn ); + + Test.startTest(); + List returnFromDouble = classToDouble.methodWithListOfObjects( new List{ 'set', 'set' } ); + Test.stopTest(); + + classToDoubleController.verify(); + + System.assertEquals( expectedReturn, returnFromDouble, 'expects.method.withAllElements.set.returning, when the list parameter contains the elements set to not null, will return the specified value' ); + } + + /** + * @method expects.method.withAllElements.set.returning + * @case when an element is null + * @result will fail the test + */ + @isTest + private static void expectsWithAllElementsSetReturning_whenElementIsNull_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodWithListOfObjects' ) + .withParameter().withAllElements().set(); + + Test.startTest(); + try { + classToDouble.methodWithListOfObjects( new List{ 'set', null } ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterInPositionAssertion( assertsDoubleController, + 'List element 1 does not match what was expected', + CLASS_TO_DOUBLE + '.methodWithListOfObjects', 0, + 'Not Null', 'null', + 'expects.method.withAllElements.set returning, when an element is null' ); + } + + /** + * @method expects.method.withAnyElement.set.returning + * @case when the list parameter passed has an element set to not null + * @result will pass the test + */ + @isTest + private static void expectsWithAnyElementSetReturning_whenElementInTheParameterIsSet_pass() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + List expectedReturn = new List{ 'expectedReturn' }; + + classToDoubleController + .expects() + .method( 'methodWithListOfObjects' ) + .withParameter().withAnyElement().set() + .returning( expectedReturn ); + + Test.startTest(); + List returnFromDouble = classToDouble.methodWithListOfObjects( new List{ 'set', null } ); + Test.stopTest(); + + classToDoubleController.verify(); + + System.assertEquals( expectedReturn, returnFromDouble, 'expects.method.withAnyElement.set.returning, when the list parameter contains an elements set to not null, will return the specified value' ); + } + + /** + * @method expects.method.withAnyElement.set.returning + * @case when all elements are null + * @result will fail the test + */ + @isTest + private static void expectsWithAnyElementSetReturning_whenAllElementsAreNull_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodWithListOfObjects' ) + .withParameter().withAnyElement().set(); + + Test.startTest(); + try { + classToDouble.methodWithListOfObjects( new List{ null, null } ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterInPositionAssertion( assertsDoubleController, + 'At least one element in the list should pass the described verification', + CLASS_TO_DOUBLE + '.methodWithListOfObjects', 0, + 'Any value', '(null, null)', + 'expects.method.withAnyElement.set returning, when the element is null' ); + } + + /** + * @method expects.method.withAnyElement.set.returning + * @case when the list is empty + * @result will fail the test + */ + @isTest + private static void expectsWithAnyElementSetReturning_whenNoElementsExist_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodWithListOfObjects' ) + .withParameter().withAnyElement().set(); + + Test.startTest(); + try { + classToDouble.methodWithListOfObjects( new List() ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterInPositionAssertion( assertsDoubleController, + 'At least one element in the list should pass the described verification', + CLASS_TO_DOUBLE + '.methodWithListOfObjects', 0, + 'Any value', '()', + 'expects.method.withAnyElement.set returning, when the element is null' ); + } + + /** + * @method expects.method.withElementAt.containing.returning + * @case when the list parameter passed has the element set contain the value + * @result will pass the test + */ + @isTest + private static void expectsWithElementAtContainingReturning_whenElementInTheParameterContains_pass() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + List expectedReturn = new List{ 'expectedReturn' }; + + classToDoubleController + .expects() + .method( 'methodWithListOfObjects' ) + .withParameter().withElementAt( 1 ).containing( 'match' ) + .returning( expectedReturn ); + + Test.startTest(); + List returnFromDouble = classToDouble.methodWithListOfObjects( new List{ 'unimportant', 'match' } ); + Test.stopTest(); + + classToDoubleController.verify(); + + System.assertEquals( expectedReturn, returnFromDouble, 'expects.method.withElementAt.containing.returning, when the list parameter contains the element set to contain, will return the specified value' ); + } + + /** + * @method expects.method.withAllElements.containing.returning + * @case when an element does not contain + * @result will fail the test + */ + @isTest + private static void expectsWithAllElementsContainingReturning_whenElementDoesNotContain_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodWithListOfObjects' ) + .withParameter().withAllElements().containing( 'match' ); + + Test.startTest(); + try { + classToDouble.methodWithListOfObjects( new List{ 'a match', 'different', 'another match' } ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterInPositionAssertion( assertsDoubleController, + 'List element 1 does not match what was expected', + CLASS_TO_DOUBLE + '.methodWithListOfObjects', 0, + 'match', 'different', + 'expects.method.withAllElements.containing returning, when an element does not contain' ); + } + + /** + * @method expects.method.withAllElements.containing.returning + * @case when an element is not a String + * @result will fail the test + */ + @isTest + private static void expectsWithAllElementsContainingReturning_whenElementIsNotAString_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodWithListOfObjects' ) + .withParameter().withAllElements().containing( 'match' ); + + Test.startTest(); + try { + classToDouble.methodWithListOfObjects( new List{ 'a match', 12, 'another match' } ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterInPositionAssertion( assertsDoubleController, + 'List element 1 does not match what was expected', + CLASS_TO_DOUBLE + '.methodWithListOfObjects', 0, + 'String', 'Integer', + 'expects.method.withAllElements.containing returning, when an element is not a String' ); + } + + /** + * @method expects.method.withAllElements.containing.returning + * @case when an element is null + * @result will fail the test + */ + @isTest + private static void expectsWithAllElementsContainingReturning_whenElementIsNull_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodWithListOfObjects' ) + .withParameter().withAllElements().containing( 'match' ); + + Test.startTest(); + try { + classToDouble.methodWithListOfObjects( new List{ 'a match', null, 'another match' } ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterInPositionAssertion( assertsDoubleController, + 'List element 1 does not match what was expected', + CLASS_TO_DOUBLE + '.methodWithListOfObjects', 0, + 'String', 'Unknown (null)', + 'expects.method.withAllElements.containing returning, when an element is not a String' ); + } + + /** + * @method expects.method.withAnyElement.containing.returning + * @case when no elements match + * @result will fail the test + */ + @isTest + private static void expectsWithAnyElementSetContaining_whenNoElementsMatch_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodWithListOfObjects' ) + .withParameter().withAnyElement().containing( 'match' ); + + Test.startTest(); + try { + classToDouble.methodWithListOfObjects( new List{ 'not the same', null } ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterInPositionAssertion( assertsDoubleController, + 'At least one element in the list should pass the described verification', + CLASS_TO_DOUBLE + '.methodWithListOfObjects', 0, + 'String value containing "match"', '(not the same, null)', + 'expects.method.withAnyElement.containing returning, when no elements match' ); + } + + /** + * @method expects.method.withElementAt.matching.returning + * @case when the list parameter passed has the element set matches the value + * @result will pass the test + */ + @isTest + private static void expectsWithElementAtMatchingReturning_whenElementInTheParameterMatches_pass() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + List expectedReturn = new List{ 'expectedReturn' }; + + classToDoubleController + .expects() + .method( 'methodWithListOfObjects' ) + .withParameter().withElementAt( 1 ).matching( 'm.*h' ) + .returning( expectedReturn ); + + Test.startTest(); + List returnFromDouble = classToDouble.methodWithListOfObjects( new List{ 'unimportant', 'match' } ); + Test.stopTest(); + + classToDoubleController.verify(); + + System.assertEquals( expectedReturn, returnFromDouble, 'expects.method.withElementAt.matching.returning, when the list parameter contains the element set to match, will return the specified value' ); + } + + /** + * @method expects.method.withAllElements.matching.returning + * @case when an element does not match + * @result will fail the test + */ + @isTest + private static void expectsWithAllElementsMatchingReturning_whenAnElementDoesNotMatch_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodWithListOfObjects' ) + .withParameter().withAllElements().matching( 'm.*h' ); + + Test.startTest(); + try { + classToDouble.methodWithListOfObjects( new List{ 'match', 'different', 'match' } ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterInPositionAssertion( assertsDoubleController, + 'List element 1 does not match what was expected', + CLASS_TO_DOUBLE + '.methodWithListOfObjects', 0, + 'm.*h', 'different', + 'expects.method.withAllElements.matching returning, when an element does not match' ); + } + + /** + * @method expects.method.withAllElements.matching.returning + * @case when an element is not a String + * @result will fail the test + */ + @isTest + private static void expectsWithAllElementsMatchingReturning_whenElementIsNotAString_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodWithListOfObjects' ) + .withParameter().withAllElements().matching( 'm.*h' ); + + Test.startTest(); + try { + classToDouble.methodWithListOfObjects( new List{ 'match', 12, 'match' } ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterInPositionAssertion( assertsDoubleController, + 'List element 1 does not match what was expected', + CLASS_TO_DOUBLE + '.methodWithListOfObjects', 0, + 'String', 'Integer', + 'expects.method.withAllElements.matching returning, when an element is not a String' ); + } + + /** + * @method expects.method.withAllElements.matching.returning + * @case when an element is null + * @result will fail the test + */ + @isTest + private static void expectsWithAllElementsMatchingReturning_whenElementIsNull_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodWithListOfObjects' ) + .withParameter().withAllElements().matching( 'm.*h' ); + + Test.startTest(); + try { + classToDouble.methodWithListOfObjects( new List{ 'match', null, 'match' } ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterInPositionAssertion( assertsDoubleController, + 'List element 1 does not match what was expected', + CLASS_TO_DOUBLE + '.methodWithListOfObjects', 0, + 'String', 'Unknown (null)', + 'expects.method.withAllElements.matching returning, when an element is not a String' ); + } + + /** + * @method expects.method.withAnyElement.matching.returning + * @case when no elements match + * @result will fail the test + */ + @isTest + private static void expectsWithAnyElementSetMatching_whenNoElementsMatch_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodWithListOfObjects' ) + .withParameter().withAnyElement().matching( 'm.*h' ); + + Test.startTest(); + try { + classToDouble.methodWithListOfObjects( new List{ 'not the same', null } ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterInPositionAssertion( assertsDoubleController, + 'At least one element in the list should pass the described verification', + CLASS_TO_DOUBLE + '.methodWithListOfObjects', 0, + 'String value matching "m.*h"', '(not the same, null)', + 'expects.method.withAnyElement.matching returning, when no elements match' ); + } + + /** + * @method expects.method.withElementAt.aListOfLength.returning + * @case when the list parameter passed has the element set matches the value + * @result will pass the test + */ + @isTest + private static void expectsWithElementAtAListOfLengthReturning_whenElementInTheParameterMatches_pass() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + List expectedReturn = new List{ 'expectedReturn' }; + + classToDoubleController + .expects() + .method( 'methodWithListOfObjects' ) + .withParameter().withElementAt( 1 ).aListOfLength( 2 ) + .returning( expectedReturn ); + + Test.startTest(); + List returnFromDouble = classToDouble.methodWithListOfObjects( new List{ new List{ 0, 1, 2 }, new List{ 0, 1 } } ); + Test.stopTest(); + + classToDoubleController.verify(); + + System.assertEquals( expectedReturn, returnFromDouble, 'expects.method.withElementAt.aListOfLength.returning, when the list parameter contains the element set to match, will return the specified value' ); + } + + /** + * @method expects.method.withAllElements.aListOfLength.returning + * @case when an element does not match + * @result will fail the test + */ + @isTest + private static void expectsWithAllElementsAListOfLengthReturning_whenAnElementDoesNotMatch_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodWithListOfObjects' ) + .withParameter().withAllElements().aListOfLength( 2 ); + + Test.startTest(); + try { + classToDouble.methodWithListOfObjects( new List{ new List{ 0, 1 }, new List{ 0 }, new List{ 0, 1 } } ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterInPositionAssertion( assertsDoubleController, + 'List element 1 does not match what was expected', + CLASS_TO_DOUBLE + '.methodWithListOfObjects', 0, + '2', '1', + 'expects.method.withAllElements.aListOfLength returning, when an element does not match' ); + } + + /** + * @method expects.method.withAllElements.aListOfLength.returning + * @case when an element is not a List + * @result will fail the test + */ + @isTest + private static void expectsWithAllElementsAListOfLengthReturning_whenElementIsNotAList_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodWithListOfObjects' ) + .withParameter().withAllElements().aListOfLength( 2 ); + + Test.startTest(); + try { + classToDouble.methodWithListOfObjects( new List{ new List{ 0, 1 }, 'no match', new List{ 0, 1 } } ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterInPositionAssertion( assertsDoubleController, + 'List element 1 does not match what was expected', + CLASS_TO_DOUBLE + '.methodWithListOfObjects', 0, + 'List', 'String', + 'expects.method.withAllElements.aListOfLength returning, when an element is not a List' ); + } + + /** + * @method expects.method.withAllElements.aListOfLength.returning + * @case when an element is null + * @result will fail the test + */ + @isTest + private static void expectsWithAllElementsAListOfLengthReturning_whenElementIsNull_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodWithListOfObjects' ) + .withParameter().withAllElements().aListOfLength( 2 ); + + Test.startTest(); + try { + classToDouble.methodWithListOfObjects( new List{ new List{ 0, 1 }, null, new List{ 0, 1 }} ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterInPositionAssertion( assertsDoubleController, + 'List element 1 does not match what was expected', + CLASS_TO_DOUBLE + '.methodWithListOfObjects', 0, + 'List', 'Unknown (null)', + 'expects.method.withAllElements.aListOfLength returning, when an element is not a List' ); + } + + /** + * @method expects.method.withAnyElement.aListOfLength.returning + * @case when no elements match + * @result will fail the test + */ + @isTest + private static void expectsWithAnyElementAListOfLength_whenNoElementsMatch_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodWithListOfObjects' ) + .withParameter().withAnyElement().aListOfLength( 2 ); + + Test.startTest(); + try { + classToDouble.methodWithListOfObjects( new List{ 'not the same', null } ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterInPositionAssertion( assertsDoubleController, + 'At least one element in the list should pass the described verification', + CLASS_TO_DOUBLE + '.methodWithListOfObjects', 0, + 'List of length 2', '(not the same, null)', + 'expects.method.withAnyElement.aListOfLength returning, when no elements match' ); + } + + /** + * @method expects.method.withParameterNamed.withAnyElement.aListOfLength.returning + * @case when no elements match + * @result will fail the test + */ + @isTest + private static void expectsWithNamedAnyElementAListOfLength_whenNoElementsMatch_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodWithListOfObjects' ) + .withParameterNamed( 'parameter1' ).withAnyElement().aListOfLength( 2 ); + + Test.startTest(); + try { + classToDouble.methodWithListOfObjects( new List{ 'not the same', null } ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterNamedAssertion( assertsDoubleController, + 'At least one element in the list should pass the described verification', + CLASS_TO_DOUBLE + '.methodWithListOfObjects', 'parameter1', + 'List of length 2', '(not the same, null)', + 'expects.method.withParmeterNamed.withAnyElement.aListOfLength returning, when no elements match' ); + } + + /** + * @method expects.method.verifiedBy.returning + * @case when the verifier does not throw an exception + * @result will pass the test + */ + @isTest + private static void expectsMethodVerifiedByReturning_whenTheVerifierDoesNotThrowAnException_pass() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Amoss_Instance verifierToDoubleController = new Amoss_Instance( Amoss_ValueVerifier.class ); + verifierToDoubleController + .expects() + .method( 'verify' ) + .withParameter().setTo( 'expectedParameterValue' ) + .then().expects() + .method( 'verify' ) + .withParameter().setTo( 1 ); + Amoss_ValueVerifier customVerifierThatPasses = (Amoss_ValueVerifier)verifierToDoubleController.getDouble(); + + String expectedReturn = 'theReturn'; + classToDoubleController + .expects() + .method( 'methodUnderDouble' ) + .withParameter().verifiedBy( customVerifierThatPasses ) + .thenParameter().verifiedBy( customVerifierThatPasses ) + .returning( expectedReturn ); + + Test.startTest(); + String returnFromDouble = classToDouble.methodUnderDouble( 'expectedParameterValue', 1 ); + Test.stopTest(); + + classToDoubleController.verify(); + + System.assertEquals( expectedReturn, returnFromDouble, 'expects.method.verifiedBy.returning, when the verifier does not throw an exception (I.E. passes) will pass the test' ); + } + + /** + * @method expects.method.withParameterNamed.verifiedBy.returning + * @case when the verifier does not throw an exception + * @result will pass the test + */ + @isTest + private static void expectsWithParameterNamedVerifiedByReturning_whenTheVerifierDoesNotThrowAnException_pass() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Amoss_Instance verifierToDoubleController = new Amoss_Instance( Amoss_ValueVerifier.class ); + verifierToDoubleController + .expects() + .method( 'verify' ) + .withParameter().setTo( 'expectedParameterValue' ) + .then().expects() + .method( 'verify' ) + .withParameter().setTo( 1 ); + Amoss_ValueVerifier customVerifierThatPasses = (Amoss_ValueVerifier)verifierToDoubleController.getDouble(); + + String expectedReturn = 'theReturn'; + classToDoubleController + .expects() + .method( 'methodUnderDouble' ) + .withParameterNamed( 'parameter1' ).verifiedBy( customVerifierThatPasses ) + .andParameterNamed( 'parameter2' ).verifiedBy( customVerifierThatPasses ) + .returning( expectedReturn ); + + Test.startTest(); + String returnFromDouble = classToDouble.methodUnderDouble( 'expectedParameterValue', 1 ); + Test.stopTest(); + + classToDoubleController.verify(); + + System.assertEquals( expectedReturn, returnFromDouble, 'expects.method.verifiedBy.returning, when the verifier does not throw an exception (I.E. passes) will pass the test' ); + } + + /** + * @method expects.method.verifiedBy.returning + * @case when the verifier throws an Amoss_AssertionFailureException + * @result will fail the test + */ + @isTest + private static void expectsMethodVerifiedByReturning_whenTheVerifierThrowsAnAssertException_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Amoss_Instance verifierToDoubleController = new Amoss_Instance( Amoss_ValueVerifier.class ); + verifierToDoubleController + .allowsAnyCall() + .expects() + .method( 'verify' ) + .withParameter().setTo( 'expectedParameterValue' ) + .throwing( new Amoss_Instance.Amoss_EqualsAssertionFailureException() + .setExpected( 'expectedValue' ) + .setActual( 'actualValue' ) + .setAssertionMessage( 'This is the failure' ) ); + + Amoss_ValueVerifier customVerifierThatFails = (Amoss_ValueVerifier)verifierToDoubleController.getDouble(); + + String expectedReturn = 'theReturn'; + classToDoubleController + .expects() + .method( 'methodUnderDouble' ) + .withParameter().verifiedBy( customVerifierThatFails ) + .thenParameter().verifiedBy( customVerifierThatFails ) + .returning( expectedReturn ); + + Test.startTest(); + try { + classToDouble.methodUnderDouble( 'expectedParameterValue', 1 ); + } catch( TestException e ) {} + Test.stopTest(); + + classToDoubleController.verify(); + + checkForWrongParameterInPositionAssertion( assertsDoubleController, + 'This is the failure', + CLASS_TO_DOUBLE + '.methodUnderDouble', 0, + 'expectedValue', 'actualValue', + 'expects.method.verifiedBy.returning, when the verifier throws an assertion exception' ); + } + + /** + * @method when.method.verifiedBy.returning + * @case when the verifier does not throw an exception + * @result will match the call and return the value + */ + @isTest + private static void whenMethodVerifiedByReturning_whenTheVerifierDoesNotThrowAnException_matchAndReturnTheValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Amoss_Instance verifierToDoubleController = new Amoss_Instance( Amoss_ValueVerifier.class ); + verifierToDoubleController + .expects() + .method( 'verify' ) + .withParameter().setTo( 'expectedParameterValue' ) + .then().expects() + .method( 'verify' ) + .withParameter().setTo( 1 ); + Amoss_ValueVerifier customVerifierThatPasses = (Amoss_ValueVerifier)verifierToDoubleController.getDouble(); + + String expectedReturn = 'theReturn'; + classToDoubleController + .when() + .method( 'methodUnderDouble' ) + .withParameter().verifiedBy( customVerifierThatPasses ) + .thenParameter().verifiedBy( customVerifierThatPasses ) + .willReturn( expectedReturn ) + .also().when() + .method( 'methodUnderDouble' ) + .willReturn( 'not this value' ); + + Test.startTest(); + String returnFromDouble = classToDouble.methodUnderDouble( 'expectedParameterValue', 1 ); + Test.stopTest(); + + System.assertEquals( expectedReturn, returnFromDouble, 'when.method.verifiedBy.returning, when the verifier does not throw an exception (I.E. passes) will match the return' ); + } + + /** + * @method when.method.verifiedBy.returning + * @case when the verifier throws an exception + * @result will not match the call + */ + @isTest + private static void whenMethodVerifiedByReturning_whenTheVerifierThrowsAnException_notMatchAndReturnTheValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Amoss_Instance verifierToDoubleController = new Amoss_Instance( Amoss_ValueVerifier.class ); + verifierToDoubleController + .expects() + .method( 'verify' ) + .withParameter().setTo( 'expectedParameterValue' ) + .then().expects() + .method( 'verify' ) + .withParameter().setTo( 1 ) + .throwing( new Amoss_Instance.Amoss_EqualsAssertionFailureException() + .setExpected( 'expectedValue' ) + .setActual( 'actualValue' ) + .setAssertionMessage( 'This parameter does not match' ) ); + + Amoss_ValueVerifier customVerifierThatPasses = (Amoss_ValueVerifier)verifierToDoubleController.getDouble(); + + String expectedReturn = 'theReturn'; + classToDoubleController + .when() + .method( 'methodUnderDouble' ) + .withParameter().verifiedBy( customVerifierThatPasses ) + .thenParameter().verifiedBy( customVerifierThatPasses ) + .willReturn( 'not this value' ) + .also().when() + .method( 'methodUnderDouble' ) + .willReturn( expectedReturn ); + + Test.startTest(); + String returnFromDouble = classToDouble.methodUnderDouble( 'expectedParameterValue', 1 ); + Test.stopTest(); + + System.assertEquals( expectedReturn, returnFromDouble, 'when.method.verifiedBy.returning, when the verifier throws an exception (I.E. fails) will not match and return the specified value' ); + } + + /** + * @method expects.method.returning + * @case when the expectation is set with an sobject via named syntax, and a different instance is passed with same properties + * @result will fail the test + */ + @isTest + private static void expectsMethodReturning_whenExpectsIsSetWithSobjectWithNamedSyntaxAndDiffInstPassed_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Contact objectExpected = new Contact( LastName = 'TheObject' ); + + classToDoubleController + .expects() + .method( 'sobjectMethodUnderDouble' ) + .withParameterNamed( 'contactParam' ).setTo( objectExpected ) + .returning( 'expectedReturn' ); + + Contact objectPassed = new Contact( LastName = 'TheObject' ); + Test.startTest(); + try { + String returnFromDouble = classToDouble.sobjectMethodUnderDouble( objectPassed ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterNamedInstanceAssertion( assertsDoubleController, CLASS_TO_DOUBLE + '.sobjectMethodUnderDouble', 'contactParam', objectExpected, objectPassed, 'expects.method.returning, when specified with an sobject via named syntax and the method is called with a different instance' ); + } + + /** + * @method expects.method.returning + * @case when the expectation is set with an sobject, and a different instance is passed with different properties + * @result will fail the test + */ + @isTest + private static void expectsMethodReturning_whenExpectsSobject_andDiffInstPassedWithDiffProps_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Contact objectExpected = new Contact( LastName = 'TheExpected' ); + + classToDoubleController + .expects() + .method( 'sobjectMethodUnderDouble' ) + .withParameter( objectExpected ) + .returning( 'expectedReturn' ); + + Contact objectPassed = new Contact( LastName = 'ThePassed' ); + Test.startTest(); + try { + String returnFromDouble = classToDouble.sobjectMethodUnderDouble( objectPassed ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterInPositionAssertion( assertsDoubleController, CLASS_TO_DOUBLE + '.sobjectMethodUnderDouble', 0, objectExpected, objectPassed, 'expects.method.returning, when specified with an sobject and the method is called with a different instance that has different properties' ); + } + + /** + * @method expects.method.returning + * @case when the expectation is set with a list, and the same instance is passed + * @result will pass + */ + @isTest + private static void expectsMethodReturning_whenExpectList_andSameInstanceIsPassed_pass() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + List objectExpected = new List{ new Contact( LastName = 'Contact1' ), new Contact( LastName = 'Contact2' ) }; + + classToDoubleController + .expects() + .method( 'methodWithListOfObjects' ) + .withParameter( objectExpected ) + .returning( new List() ); + + Test.startTest(); + classToDouble.methodWithListOfObjects( objectExpected ); + Test.stopTest(); + + classToDoubleController.verify(); + } + + /** + * @method expects.method.returning + * @case when the expectation is set with a list, and something that is not a list is passed + * @result will fail + */ + @isTest + private static void expectsMethodReturning_whenExpectList_andSomethingNotAList_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + List objectExpected = new List{ new Contact( LastName = 'Contact1' ), new Contact( LastName = 'Contact2' ) }; + + classToDoubleController + .expects() + .method( 'objectMethodUnderDouble' ) + .withParameter( objectExpected ) + .returning( new List() ); + + Contact objectPassed = new Contact( FirstName = 'Not a list' ); + + Test.startTest(); + try { + classToDouble.objectMethodUnderDouble( objectPassed ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterInPositionAssertion( assertsDoubleController, + 'The value is not what was expected - The types do not match', + CLASS_TO_DOUBLE + '.objectMethodUnderDouble', + 0, + 'List', 'Contact', + 'expects.method.returning, when the expectation is set with a list, and something that is not a list is passed' ); + } + + + /** + * @method expects.method.returning + * @case when the expectation is set with a list, and a different instance is passed, containing the same objects + * @result will fail + */ + @isTest + private static void expectsMethodReturning_whenExpectList_andADiffInstContainingSameObjects_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + List objectExpected = new List{ new Contact( LastName = 'Contact1' ), new Contact( LastName = 'Contact2' ) }; + + classToDoubleController + .expects() + .method( 'methodWithListOfObjects' ) + .withParameter( objectExpected ) + .returning( new List() ); + + List objectPassed = new List{ objectExpected[0], objectExpected[1] }; + + Test.startTest(); + try { + classToDouble.methodWithListOfObjects( objectPassed ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterInPositionAssertion( assertsDoubleController, + 'The value is the same, but not the same instance as was expected', + CLASS_TO_DOUBLE + '.methodWithListOfObjects', + 0, + objectExpected, objectPassed, + 'expects.method.returning, when the expectation is set with a list, and a different instance is passed, containing the same instances' ); + } + + /** + * @method expects.method.returning + * @case when the expectation is set with a list, and a different instance is passed, containing different instances with the same values + * @result will fail + */ + @isTest + private static void expectsMethodReturning_whenExpectList_andADiffInstContainingDiffObjectsWithTheSameValues() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + List objectExpected = new List{ new Contact( LastName = 'Contact1' ), new Contact( LastName = 'Contact2' ) }; + + classToDoubleController + .expects() + .method( 'methodWithListOfObjects' ) + .withParameter( objectExpected ) + .returning( new List() ); + + List objectPassed = new List{ new Contact( LastName = 'Contact1' ), new Contact( LastName = 'Contact2' ) }; + + Test.startTest(); + try { + classToDouble.methodWithListOfObjects( objectPassed ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterInPositionAssertion( assertsDoubleController, + 'The value is the same, but the list and some of its elements are not the same instances as was expected', + CLASS_TO_DOUBLE + '.methodWithListOfObjects', 0, + objectExpected, objectPassed, + 'expects.method.returning, when the expectation is set with a list, and a different instance is passed, containing different instances with the same values' ); + } + + /** + * @method expects.method.returning + * @case when the expectation is set with a list, and a different instance is passed, containing different instances with different values + * @result will fail + */ + @isTest + private static void expectsMethodReturning_whenExpectList_andADiffInstContainingDiffObjectsWithDiffValues() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + List objectExpected = new List{ new Contact( LastName = 'Contact1' ), new Contact( LastName = 'Contact2' ) }; + + classToDoubleController + .expects() + .method( 'methodWithListOfObjects' ) + .withParameter( objectExpected ) + .returning( new List() ); + + List objectPassed = new List{ new Contact( LastName = 'Diff-Contact1' ), new Contact( LastName = 'Diff-Contact2' ) }; + + Test.startTest(); + try { + classToDouble.methodWithListOfObjects( objectPassed ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterInPositionAssertion( assertsDoubleController, CLASS_TO_DOUBLE + '.methodWithListOfObjects', 0, objectExpected, objectPassed, 'expects.method.returning, when the expectation is set with a list, and a different instance is passed, containing different instances with the same values' ); + } + + /** + * @method expects.method.setToTheSameValueAs.returning + * @case when the expectation is set with a list, and the same instance is passed + * @result will pass + */ + @isTest + private static void expectsMethodSetToTheSameValueAsReturning_whenExpectList_andSameInstanceIsPassed_pass() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + List objectExpected = new List{ new Contact( LastName = 'Contact1' ), new Contact( LastName = 'Contact2' ) }; + + classToDoubleController + .expects() + .method( 'methodWithListOfObjects' ) + .withParameter().setToTheSameValueAs( objectExpected ) + .returning( new List() ); + + Test.startTest(); + classToDouble.methodWithListOfObjects( objectExpected ); + Test.stopTest(); + + classToDoubleController.verify(); + } + + /** + * @method expects.method.setToTheSameValueAs.returning + * @case when the expectation is set with a list, and a different instance is passed, containing the same objects + * @result will pass + */ + @isTest + private static void expectsMethodSetToTheSameValueAsReturning_whenExpectList_andADiffInstContainingSameObjects() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + List objectExpected = new List{ new Contact( LastName = 'Contact1' ), new Contact( LastName = 'Contact2' ) }; + + classToDoubleController + .expects() + .method( 'methodWithListOfObjects' ) + .withParameter().setToTheSameValueAs( objectExpected ) + .returning( new List() ); + + List objectPassed = new List{ objectExpected[0], objectExpected[1] }; + + Test.startTest(); + classToDouble.methodWithListOfObjects( objectPassed ); + Test.stopTest(); + + classToDoubleController.verify(); + } + + /** + * @method expects.method.setToTheSameValueAs.returning + * @case when the expectation is set with a list, and a different instance is passed, containing different instances with the same values + * @result will pass + */ + @isTest + private static void expectsMethodSetToTheSameValueAsReturning_whenExpectList_andADiffInstContainingDiffObjectsWithTheSameValues() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + List objectExpected = new List{ new Contact( LastName = 'Contact1' ), new Contact( LastName = 'Contact2' ) }; + + classToDoubleController + .expects() + .method( 'methodWithListOfObjects' ) + .withParameter().setToTheSameValueAs( objectExpected ) + .returning( new List() ); + + List objectPassed = new List{ new Contact( LastName = 'Contact1' ), new Contact( LastName = 'Contact2' ) }; + + Test.startTest(); + classToDouble.methodWithListOfObjects( objectPassed ); + Test.stopTest(); + + classToDoubleController.verify(); + } + + /** + * @method expects.method.setToTheSameValueAs.returning + * @case when the expectation is set with a list, and a different instance is passed, containing different instances with different values + * @result will fail + */ + @isTest + private static void expectsMethodSetToTheSameValueAsReturning_whenExpectList_andADiffInstContainingDiffObjectsWithDiffValues() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + List objectExpected = new List{ new Contact( LastName = 'Contact1' ), new Contact( LastName = 'Contact2' ) }; + + classToDoubleController + .expects() + .method( 'methodWithListOfObjects' ) + .withParameter().setToTheSameValueAs( objectExpected ) + .returning( new List() ); + + List objectPassed = new List{ new Contact( LastName = 'Diff-Contact1' ), new Contact( LastName = 'Diff-Contact2' ) }; + + Test.startTest(); + try { + classToDouble.methodWithListOfObjects( objectPassed ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterInPositionAssertion( assertsDoubleController, CLASS_TO_DOUBLE + '.methodWithListOfObjects', 0, objectExpected, objectPassed, 'expects.method.setToTheSameValueAs.returning, when the expectation is set with a list, and a different instance is passed, containing different instances with the same values' ); + } + + /** + * @method expects.method.setToTheSameValueAs.returning + * @case when the expectation is set with a list, and a different instance is passed, containing different instances, of different types with the same values + * @result will pass + */ + @isTest + private static void expectsMethodSetToTheSameValueAsReturning_whenExpectList_andADiffInstsOfDiffTypesContainingDiffObjectsWithTheSameValues() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + List objectExpected = new List{ new Contact( LastName = 'Contact1' ), new Contact( LastName = 'Contact2' ) }; + + classToDoubleController + .expects() + .method( 'methodWithListOfObjects' ) + .withParameter().setToTheSameValueAs( objectExpected ) + .returning( new List() ); + + List objectPassed = new List{ new Contact( LastName = 'Contact1' ), new Contact( LastName = 'Contact2' ) }; + + Test.startTest(); + classToDouble.methodWithListOfObjects( objectPassed ); + Test.stopTest(); + + classToDoubleController.verify(); + } + + /** + * @method expects.method.setToTheSameValueAs.returning + * @case when the expectation is set with a list, and a different instance is passed, containing different instances, of different types with the same values + * @result will pass + */ + @isTest + private static void expectsMethodSetToTheSameValueAsReturning_whenExpectList_andADiffInstOfDiffTypesContainingDiffObjectsWithTheSameValues_2() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + List objectExpected = new List{ new Contact( LastName = 'Contact1' ), new Contact( LastName = 'Contact2' ) }; + + classToDoubleController + .expects() + .method( 'methodWithListOfObjects' ) + .withParameter().setToTheSameValueAs( objectExpected ) + .returning( new List() ); + + List objectPassed = new List{ new Contact( LastName = 'Contact1' ), new Contact( LastName = 'Contact2' ) }; + + Test.startTest(); + classToDouble.methodWithListOfObjects( objectPassed ); + Test.stopTest(); + + classToDoubleController.verify(); + } + + /** + * @method expects.method.returning + * @case when the expectation is set with a Map, and the same instance is passed + * @result will pass + */ + @isTest + private static void expectsMethodReturning_whenExpectMap_andSameInstanceIsPassed_pass() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Map objectExpected = new Map{ 'one' => new Contact( LastName = 'Contact1' ), 'two' => new Contact( LastName = 'Contact2' ) }; + + classToDoubleController + .expects() + .method( 'methodWithMapStringToObject' ) + .withParameter( objectExpected ) + .returning( new Map() ); + + Test.startTest(); + classToDouble.methodWithMapStringToObject( objectExpected ); + Test.stopTest(); + + classToDoubleController.verify(); + } + + /** + * @method expects.method.returning + * @case when the expectation is set with a map, and a different instance is passed, containing the same objects + * @result will fail + */ + @isTest + private static void expectsMethodReturning_whenExpectMap_andADiffInstContainingSameObjects_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Map objectExpected = new Map{ 'one' => new Contact( LastName = 'Contact1' ), 'two' => new Contact( LastName = 'Contact2' ) }; + + classToDoubleController + .expects() + .method( 'methodWithMapStringToObject' ) + .withParameter( objectExpected ) + .returning( new Map() ); + + Map objectPassed = new Map{ 'one' => objectExpected.get( 'one' ), 'two' => objectExpected.get( 'two' ) }; + + Test.startTest(); + try { + classToDouble.methodWithMapStringToObject( objectPassed ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterInPositionAssertion( assertsDoubleController, 'The value is the same, but not the same instance as was expected', CLASS_TO_DOUBLE + '.methodWithMapStringToObject', 0, objectExpected, objectPassed, 'expects.method.returning, when the expectation is set with a map, and a different instance is passed, containing different instances with the same values' ); + } + + /** + * @method expects.method.returning + * @case when the expectation is set with a map, and a different instance is passed, containing different instances with the same values + * @result will fail + */ + @isTest + private static void expectsMethodReturning_whenExpectMap_andADiffInstContainingDiffObjectsWithTheSameValues() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Map objectExpected = new Map{ 'one' => new Contact( LastName = 'Contact1' ), 'two' => new Contact( LastName = 'Contact2' ) }; + + classToDoubleController + .expects() + .method( 'methodWithMapStringToObject' ) + .withParameter( objectExpected ) + .returning( new Map() ); + + Map objectPassed = new Map{ 'one' => new Contact( LastName = 'Contact1' ), 'two' => new Contact( LastName = 'Contact2' ) }; + + Test.startTest(); + try { + classToDouble.methodWithMapStringToObject( objectPassed ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterInPositionAssertion( assertsDoubleController, + 'The value is the same, but not the same instance as was expected', + CLASS_TO_DOUBLE + '.methodWithMapStringToObject', 0, + objectExpected, objectPassed, + 'expects.method.returning, when the expectation is set with a map, and a different instance is passed, containing different instances with the same values' ); + } + + /** + * @method expects.method.returning + * @case when the expectation is set with a map, and a different instance is passed, containing different instances with different values + * @result will fail + */ + @isTest + private static void expectsMethodReturning_whenExpectMap_andADiffInstContainingDiffObjectsWithDiffValues() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Map objectExpected = new Map{ 'one' => new Contact( LastName = 'Contact1' ), 'two' => new Contact( LastName = 'Contact2' ) }; + + classToDoubleController + .expects() + .method( 'methodWithMapStringToObject' ) + .withParameter( objectExpected ) + .returning( new Map() ); + + Map objectPassed = new Map{ 'one' => new Contact( LastName = 'Diff-Contact1' ), 'two' => new Contact( LastName = 'Diff-Contact2' ) }; + + Test.startTest(); + try { + classToDouble.methodWithMapStringToObject( objectPassed ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterInPositionAssertion( assertsDoubleController, CLASS_TO_DOUBLE + '.methodWithMapStringToObject', 0, objectExpected, objectPassed, 'expects.method.returning, when the expectation is set with a map, and a different instance is passed, containing different instances with the same values' ); + } + + /** + * @method expects.method.setToTheSameValueAs.returning + * @case when the expectation is set with a map, and a different instance is passed, containing the same objects + * @result will pass + */ + @isTest + private static void expectsMethodSetToTheSameValueAsReturning_whenExpectMap_andADiffInstContainingSameObjects() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Map objectExpected = new Map{ 'one' => new Contact( LastName = 'Contact1' ), 'two' => new Contact( LastName = 'Contact2' ) }; + + classToDoubleController + .expects() + .method( 'methodWithMapStringToObject' ) + .withParameter().setToTheSameValueAs( objectExpected ) + .returning( new Map() ); + + Map objectPassed = new Map{ 'one' => objectExpected.get( 'one' ), 'two' => objectExpected.get( 'two' ) }; + + Test.startTest(); + classToDouble.methodWithMapStringToObject( objectPassed ); + Test.stopTest(); + + classToDoubleController.verify(); + } + + /** + * @method expects.method.setToTheSameValueAs.returning + * @case when the expectation is set with a map, and a different instance is passed, containing different instances with the same values + * @result will pass + */ + @isTest + private static void expectsMethodSetToTheSameValueAsReturning_whenExpectMap_andADiffInstContainingDiffObjectsWithTheSameValues() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Map objectExpected = new Map{ 'one' => new Contact( LastName = 'Contact1' ), 'two' => new Contact( LastName = 'Contact2' ) }; + + classToDoubleController + .expects() + .method( 'methodWithMapStringToObject' ) + .withParameter().setToTheSameValueAs( objectExpected ) + .returning( new Map() ); + + Map objectPassed = new Map{ 'one' => new Contact( LastName = 'Contact1' ), 'two' => new Contact( LastName = 'Contact2' ) }; + + Test.startTest(); + classToDouble.methodWithMapStringToObject( objectPassed ); + Test.stopTest(); + + classToDoubleController.verify(); + } + + /** + * @method expects.method.setToTheSameValueAs.returning + * @case when the expectation is set with a map, and a different instance is passed, containing different instances with different values + * @result will fail + */ + @isTest + private static void expectsMethodSetToTheSameValueAsReturning_whenExpectMap_andADiffInstContainingDiffObjectsWithDiffValues() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Map objectExpected = new Map{ 'one' => new Contact( LastName = 'Contact1' ), 'two' => new Contact( LastName = 'Contact2' ) }; + + classToDoubleController + .expects() + .method( 'methodWithMapStringToObject' ) + .withParameter().setToTheSameValueAs( objectExpected ) + .returning( new Map() ); + + Map objectPassed = new Map{ 'one' => new Contact( LastName = 'Diff-Contact1' ), 'two' => new Contact( LastName = 'Diff-Contact2' ) }; + + Test.startTest(); + try { + classToDouble.methodWithMapStringToObject( objectPassed ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterInPositionAssertion( assertsDoubleController, CLASS_TO_DOUBLE + '.methodWithMapStringToObject', 0, objectExpected, objectPassed, 'expects.method.setToTheSameValueAs.returning, when the expectation is set with a map, and a different instance is passed, containing different instances with the different values' ); + } + + /** + * @method expects.method.setToTheSameValueAs.returning + * @case when the expectation is set with a map, and a different instance is passed, containing different instances with different keys + * @result will fail + */ + @isTest + private static void expectsMethodSetToTheSameValueAsReturning_whenExpectMap_andADiffInstContainingDiffObjectsWithDiffKeys() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Map objectExpected = new Map{ 'one' => new Contact( LastName = 'Contact1' ), 'two' => new Contact( LastName = 'Contact2' ) }; + + classToDoubleController + .expects() + .method( 'methodWithMapStringToObject' ) + .withParameter().setToTheSameValueAs( objectExpected ) + .returning( new Map() ); + + Map objectPassed = new Map{ 'diff-one' => new Contact( LastName = 'Contact1' ), 'two' => new Contact( LastName = 'Contact2' ) }; + + Test.startTest(); + try { + classToDouble.methodWithMapStringToObject( objectPassed ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterInPositionAssertion( assertsDoubleController, CLASS_TO_DOUBLE + '.methodWithMapStringToObject', 0, objectExpected, objectPassed, 'expects.method.setToTheSameValueAs.returning, when the expectation is set with a map, and a different instance is passed, containing different instances with the different keys' ); + } + + /** + * @method expects.method.setToTheSameValueAs.returning + * @case when the expectation is set with a map, and a different instance is passed, containing different instances, of different types with the same values + * @result will pass + */ + @isTest + private static void expectsMethodSetToTheSameValueAsReturning_whenExpectMap_andADiffInstsOfDiffTypesContainingDiffObjectsWithTheSameValues() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Map objectExpected = new Map{ 'one' => new Contact( LastName = 'Contact1' ), 'two' => new Contact( LastName = 'Contact2' ) }; + + classToDoubleController + .expects() + .method( 'methodWithMapStringToObject' ) + .withParameter().setToTheSameValueAs( objectExpected ) + .returning( new Map() ); + + Map objectPassed = new Map{ 'one' => new Contact( LastName = 'Contact1' ), 'two' => new Contact( LastName = 'Contact2' ) }; + + Test.startTest(); + classToDouble.methodWithMapStringToObject( objectPassed ); + Test.stopTest(); + + classToDoubleController.verify(); + } + + /** + * @method expects.method.setToTheSameValueAs.returning + * @case when the expectation is set with a map, and a different instance is passed, containing different instances, of different types with the same values + * @result will pass + */ + @isTest + private static void expectsMethodSetToTheSameValueAsReturning_whenExpectMap_andADiffInstOfDiffTypesContainingDiffObjectsWithTheSameValues_2() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Map objectExpected = new Map{ 'one' => new Contact( LastName = 'Contact1' ), 'two' => new Contact( LastName = 'Contact2' ) }; + + classToDoubleController + .expects() + .method( 'methodWithMapStringToObject' ) + .withParameter().setToTheSameValueAs( objectExpected ) + .returning( new Map() ); + + Map objectPassed = new Map{ 'one' => new Contact( LastName = 'Contact1' ), 'two' => new Contact( LastName = 'Contact2' ) }; + + Test.startTest(); + classToDouble.methodWithMapStringToObject( objectPassed ); + Test.stopTest(); + + classToDoubleController.verify(); + } + + /** + * @method expects.method.returning + * @case when the expectation is set with a Map (indexed by Id), and the same instance is passed + * @result will pass + */ + @isTest + private static void expectsMethodReturning_whenExpectMapById_andSameInstanceIsPassed_pass() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Map objectExpected = new Map{ '003000000000001' => new Contact( LastName = 'Contact1' ), '003000000000002' => new Contact( LastName = 'Contact2' ) }; + + classToDoubleController + .expects() + .method( 'methodWithMapIdToObject' ) + .withParameter( objectExpected ) + .returning( new Map() ); + + Test.startTest(); + classToDouble.methodWithMapIdToObject( objectExpected ); + Test.stopTest(); + + classToDoubleController.verify(); + } + + /** + * @method expects.method.returning + * @case when the expectation is set with a Map (indexed by Date), and the same instance is passed + * @result will pass + */ + @isTest + private static void expectsMethodReturning_whenExpectMapByDate_andSameInstanceIsPassed_pass() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Map objectExpected = new Map{ Date.newInstance( 2019, 01, 05 ) => new Contact( LastName = 'Contact1' ), Date.newInstance( 2019, 01, 06 ) => new Contact( LastName = 'Contact2' ) }; + + classToDoubleController + .expects() + .method( 'methodWithMapDateToObject' ) + .withParameter( objectExpected ) + .returning( new Map() ); + + Test.startTest(); + classToDouble.methodWithMapDateToObject( objectExpected ); + Test.stopTest(); + + classToDoubleController.verify(); + } + + /** + * @method expects.method.returning + * @case when the expectation is set with a Set, and the same instance is passed + * @result will pass + */ + @isTest + private static void expectsMethodReturning_whenExpectSetWithASet_andSameInstanceIsPassed_pass() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Set objectExpected = new Set{ new Contact( LastName = 'Contact1' ), new Contact( LastName = 'Contact2' ) }; + + classToDoubleController + .expects() + .method( 'methodWithSetOfObjects' ) + .withParameter( objectExpected ) + .returning( new Set() ); + + Test.startTest(); + classToDouble.methodWithSetOfObjects( objectExpected ); + Test.stopTest(); + + classToDoubleController.verify(); + } + + /** + * @method expects.method.returning + * @case when the expectation is set with a Set, and the a different instance with the same values + * @result will fail + */ + @isTest + private static void expectsMethodReturning_whenExpectSetWithASet_andDiffInstWithSameValuesIsPassed_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Set objectExpected = new Set{ new Contact( LastName = 'Contact1' ), new Contact( LastName = 'Contact2' ) }; + + classToDoubleController + .expects() + .method( 'methodWithSetOfObjects' ) + .withParameter( objectExpected ) + .returning( new Set() ); + + Set objectPassed = new Set{ new Contact( LastName = 'Contact1' ), new Contact( LastName = 'Contact2' ) }; + + Test.startTest(); + try { + classToDouble.methodWithSetOfObjects( objectPassed ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterInPositionInstanceAssertion( assertsDoubleController, CLASS_TO_DOUBLE + '.methodWithSetOfObjects', 0, objectExpected, objectPassed, 'expects.method.returning, when the expectation is set with a Set, and a different instance is passed, containing same values' ); + } + + /** + * @method expects.method.setToSameValueAs.returning + * @case when the expectation is set with a Set, and the a different instance with the same values + * @result will pass + */ + @isTest + private static void expectsMethodSetToTheSameValueAsReturning_whenExpectSetWithASet_andDiffInstWithSameValuesIsPassed_pass() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Set objectExpected = new Set{ new Contact( LastName = 'Contact1' ), new Contact( LastName = 'Contact2' ) }; + + classToDoubleController + .expects() + .method( 'methodWithSetOfObjects' ) + .withParameter().setToTheSameValueAs( objectExpected ) + .returning( new Set() ); + + Set objectPassed = new Set{ new Contact( LastName = 'Contact1' ), new Contact( LastName = 'Contact2' ) }; + + Test.startTest(); + classToDouble.methodWithSetOfObjects( objectPassed ); + Test.stopTest(); + + classToDoubleController.verify(); + } + + /** + * @method expects.method.setToTheSameValueAsreturning + * @case when the expectation is set with a Set, and the a different instance with different values + * @result will fail + */ + @isTest + private static void expectsMethodSetToTheSameValueAsReturning_whenExpectSetWithASet_andDiffInstWithSameValuesIsPassed_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Set objectExpected = new Set{ new Contact( LastName = 'Contact1' ), new Contact( LastName = 'Contact2' ) }; + + classToDoubleController + .expects() + .method( 'methodWithSetOfObjects' ) + .withParameter().setToTheSameValueAs( objectExpected ) + .returning( new Set() ); + + Set objectPassed = new Set{ new Contact( LastName = 'Diff-Contact1' ), new Contact( LastName = 'Diff-Contact2' ) }; + + Test.startTest(); + try { + classToDouble.methodWithSetOfObjects( objectPassed ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterInPositionAssertion( assertsDoubleController, CLASS_TO_DOUBLE + '.methodWithSetOfObjects', 0, objectExpected, objectPassed, 'expects.method.setToTheSameValueAs.returning, when the expectation is set with a Set, and a different instance is passed, containing different values' ); + } + + /** + * @method expects.method.setToTheSameValueAs.returning + * @case when the expectation is set with an sobject via named syntax, and a different instance is passed with same properties + * @result will not fail the test + */ + @isTest + private static void expectsMethodSetToTheSameValueAsReturning_whenExpectsIsSetWithSobjectWithNamedSyntaxAndDiffInstPassed_pass() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Contact objectExpected = new Contact( LastName = 'TheObject' ); + + classToDoubleController + .expects() + .method( 'sobjectMethodUnderDouble' ) + .withParameterNamed( 'contactParam' ).setToTheSameValueAs( objectExpected ) + .returning( 'expectedReturn' ); + + Contact objectPassed = new Contact( LastName = 'TheObject' ); + Test.startTest(); + String returnFromDouble = classToDouble.sobjectMethodUnderDouble( objectPassed ); + Test.stopTest(); + + classToDoubleController.verify(); + } + + /** + * @method expects.method.setToTheSameValueAs.returning + * @case when the expectation is set with an sobject, and a different instance is passed with different properties + * @result will fail the test + */ + @isTest + private static void expectsMethodSetToTheSameValueAsReturning_whenExpectsSobject_andDiffInstPassedWithDiffProps_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Contact objectExpected = new Contact( LastName = 'TheExpected' ); + + classToDoubleController + .expects() + .method( 'sobjectMethodUnderDouble' ) + .withParameter().setToTheSameValueAs( objectExpected ) + .returning( 'expectedReturn' ); + + Contact objectPassed = new Contact( LastName = 'ThePassed' ); + Test.startTest(); + try { + String returnFromDouble = classToDouble.sobjectMethodUnderDouble( objectPassed ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterInPositionAssertion( assertsDoubleController, CLASS_TO_DOUBLE + '.sobjectMethodUnderDouble', 0, objectExpected, objectPassed, 'expects.method.setToTheSameValueAs.returning, when specified with an sobject and the method is called with a different instance that has different properties' ); + } + + /** + * @method expects.method.returning + * @case when an overloaded method is called with the stated parameters + * @result will not the test + */ + @isTest + private static void expectsMethodReturning_whenAnOverloadedMethodIsCalledWithTheStatedParams_pass() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'overloadedMethodUnderDouble' ) + .withParameter( 'ActualParam1' ) + .thenParameter( 1 ) + .returning( 'expectedReturn' ); + + Test.startTest(); + String returnFromDouble = classToDouble.overloadedMethodUnderDouble( 'ActualParam1', 1 ); + Test.stopTest(); + + classToDoubleController.verify(); + } + + /** + * @method expects.method.returning + * @case when an overloaded method is called with different parameters + * @result will fail the test + */ + @isTest + private static void expectsMethodReturning_whenAnOverloadedMethodIsCalledWithDiffParams_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'overloadedMethodUnderDouble' ) + .withParameter( 'ActualParam1' ) + .thenParameter( 1 ) + .returning( 'expectedReturn' ); + + Test.startTest(); + try { + String returnFromDouble = classToDouble.overloadedMethodUnderDouble( 'ActualParam1', '1' ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterInPositionAssertion( assertsDoubleController, + 'The value is not what was expected - The types do not match', + CLASS_TO_DOUBLE + '.overloadedMethodUnderDouble', + 1, + 'Integer', 'String', + 'expects.method.returning, when an overloaded method is called with different parameters' ); + } + + /** + * @method expects.method.returning + * @case when an overloaded method is called with more parameters + * @result will fail the test + */ + @isTest + private static void expectsMethodReturning_whenAnOverloadedMethodIsCalledWithMoreParams_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'overloadedMethodUnderDouble' ) + .withParameter( 'ActualParam1' ) + .returning( 'expectedReturn' ); + + Test.startTest(); + try { + String returnFromDouble = classToDouble.overloadedMethodUnderDouble( 'ActualParam1', '1' ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForEqualsAssertionFailure( assertsDoubleController, 'was called with an unexpected number of parameters', CLASS_TO_DOUBLE + '.overloadedMethodUnderDouble', 1, 2, 'expects.method.returning, when an overloaded method is called with more parameters' ); + } + + /** + * @method expects.method.returning + * @case when an overloaded method is called with fewer parameters + * @result will fail the test + */ + @isTest + private static void expectsMethodReturning_whenAnOverloadedMethodIsCalledWithFewerParams_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'overloadedMethodUnderDouble' ) + .withParameter( 'ActualParam1' ) + .thenParameter( 'ActualParam2' ) + .thenParameter( 'ActualParam3' ) + .returning( 'expectedReturn' ); + + Test.startTest(); + try { + String returnFromDouble = classToDouble.overloadedMethodUnderDouble( 'ActualParam1', 'ActualParam2' ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForEqualsAssertionFailure( assertsDoubleController, 'was called with an unexpected number of parameters', CLASS_TO_DOUBLE + '.overloadedMethodUnderDouble', 3, 2, 'expects.method.returning, when an overloaded method is called with fewer parameters' ); + } + + /** + * @method expects.method.returning (positional) + * @case when an overloaded method is specified without all parameters + * @result will match any appropriate version + */ + @isTest + private static void expectsMethodReturning_whenAnOverloadedMethodIsSpecifiedWithoutAllParametersInPositional_matchAnyApppriateVersion() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'overloadedMethodUnderDouble' ) + .withParameter( 'ActualParam1' ) + .thenAnyParameter() + .returning( 'expectedReturn' ) + .then().expects() + .method( 'overloadedMethodUnderDouble' ) + .withParameter( 'ActualParam1' ) + .thenAnyParameter() + .returning( 'expectedReturn' ); + + Test.startTest(); + classToDouble.overloadedMethodUnderDouble( 'ActualParam1', '1' ); + classToDouble.overloadedMethodUnderDouble( 'ActualParam1', 1 ); + Test.stopTest(); + + classToDoubleController.verify(); + } + + /** + * @method expects.method.returning (named) + * @case when an overloaded method is specified without all parameters + * @result will match any appropriate version + */ + @isTest + private static void expectsMethodReturning_whenAnOverloadedMethodIsSpecifiedWithoutAllParametersInNamed_matchAnyApppriateVersion() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'overloadedMethodUnderDouble' ) + .withParameterNamed( 'parameter1' ).setTo( 'ActualParam1' ) + .returning( 'expectedReturn' ) + .then().expects() + .method( 'overloadedMethodUnderDouble' ) + .withParameterNamed( 'parameter1' ).setTo( 'ActualParam1' ) + .returning( 'expectedReturn' ); + + Test.startTest(); + classToDouble.overloadedMethodUnderDouble( 'ActualParam1', '1' ); + classToDouble.overloadedMethodUnderDouble( 'ActualParam1', 1 ); + Test.stopTest(); + + classToDoubleController.verify(); + } + + /** + * @method expects.method.returning, named parameters + * @case when the method is called with different parameters + * @result will fail the test + */ + @isTest + private static void expectsMethodReturning_whenUsingNamedParametersAndTheMethodIsCalledWithDiffParams_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodUnderDouble' ) + .withParameterNamed( 'parameter1' ).setTo( 'ActualParam1' ) + .andParameterNamed( 'parameter2' ).setTo( 1 ) + .returning( 'expectedReturn' ); + + Test.startTest(); + try { + String returnFromDouble = classToDouble.methodUnderDouble( 'OtherActualParam1', 1 ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterNamedAssertion( assertsDoubleController, CLASS_TO_DOUBLE + '.methodUnderDouble', 'parameter1', 'ActualParam1', 'OtherActualParam1', 'expects.method.returning, when the method defined using named parameters and is called with different parameters' ); + } + + /** + * @method expects.method.returning, named parameters + * @case when the method is called fewer parameters than those specified + * @result will fail the test + */ + @isTest + private static void expectsMethodReturning_whenUsingNamedParametersAndTheMethodIsCalledWithFewerParams_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'overloadedMethodUnderDouble' ) + .withParameterNamed( 'parameter1' ).setTo( 'ActualParam1' ) + .andParameterNamed( 'parameter2' ).setTo( 'ActualParam2' ) + .andParameterNamed( 'parameter3' ).setTo( 'ActualParam3' ) + .returning( 'expectedReturn' ); + + Test.startTest(); + try { + String returnFromDouble = classToDouble.overloadedMethodUnderDouble( 'ActualParam1', 'ActualParam2' ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForEqualsAssertionFailure( assertsDoubleController, 'was called with different parameters to those specified', CLASS_TO_DOUBLE + '.overloadedMethodUnderDouble', new List{ 'parameter1', 'parameter2', 'parameter3' }, new List{ 'parameter1', 'parameter2' }, 'expects.method.returning, when the method defined using named parameters and is called with fewer parameters than were defined' ); + } + + /** + * @method expects.method.returning, named parameters + * @case when the method is called fewer parameters than those specified + * @result will fail the test + */ + @isTest + private static void expectsMethodReturning_whenUsingNamedParametersAndTheMethodIsCalledWithDifflyNamedParams_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'overloadedMethodUnderDouble' ) + .withParameterNamed( 'parameter1' ).setTo( 'ActualParam1' ) + .andParameterNamed( 'parameter3' ).setTo( 'ActualParam3' ) + .returning( 'expectedReturn' ); + + Test.startTest(); + try { + String returnFromDouble = classToDouble.overloadedMethodUnderDouble( 'ActualParam1', 'ActualParam2' ); + } catch( TestException e ) {} + Test.stopTest(); + + checkForEqualsAssertionFailure( assertsDoubleController, 'was called with different parameters to those specified', CLASS_TO_DOUBLE + '.overloadedMethodUnderDouble', new List{ 'parameter1', 'parameter3' }, new List{ 'parameter1', 'parameter2' }, 'expects.method.returning, when the method defined using named parameters and is called with fewer parameters than were defined' ); + } + + /** + * @method expects.method.returning + * @case when multiple methods are defined and called in the stated order + * @result will return the stated value for each method + */ + @isTest + private static void expectsMethodReturning_whenMultipleMethodsAreDefinedAndCalledInOrder_returnSetUpValues() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodUnderDouble' ) + .returning( 'expectedReturn1' ) + .also().allows() + .method( 'otherMethodUnderDouble' ) + .returning( 'expectedReturn2' ); + + Test.startTest(); + String returnFromDouble1 = classToDouble.methodUnderDouble( 'ActualParam1', 1 ); + String returnFromDouble2 = classToDouble.otherMethodUnderDouble( 'ActualParam1', 1 ); + Test.stopTest(); + + System.assertEquals( 'expectedReturn1', returnFromDouble1, 'expects.method.returning, when multiple methods are defined and each are called in order, will return the stated value for each method (1)' ); + System.assertEquals( 'expectedReturn2', returnFromDouble2, 'expects.method.returning, when multiple methods are defined and each are called in order, will return the stated value for each method (2)' ); + } + + /** + * @method expects.method.returning + * @case when the same method is defined with the same parameters, and called in order + * @result will return the stated value in the call order they are specified + */ + @isTest + private static void expectsMethodReturning_whenMethodIsDefinedAndCalledInOrder_returnTheResultsInOrder() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodUnderDouble' ) + .withParameter( 'ActualParam1' ) + .thenParameter( 1 ) + .returning( 'firstReturn' ) + .then().expects() + .method( 'methodUnderDouble' ) + .withParameter( 'ActualParam1' ) + .thenParameter( 1 ) + .returning( 'secondReturn' ) + .then().expects() + .method( 'methodUnderDouble' ) + .withParameter( 'ActualParam1' ) + .thenParameter( 1 ) + .returning( 'thirdReturn' ); + + Test.startTest(); + String returnFromFirstCall = classToDouble.methodUnderDouble( 'ActualParam1', 1 ); + String returnFromSecondCall = classToDouble.methodUnderDouble( 'ActualParam1', 1 ); + String returnFromThirdCall = classToDouble.methodUnderDouble( 'ActualParam1', 1 ); + Test.stopTest(); + + System.assertEquals( 'firstReturn' , returnFromFirstCall , 'expects.method.returning, when method is defined with multiple overlapping parameters, will return the values stated, in the order they are called (1)' ); + System.assertEquals( 'secondReturn', returnFromSecondCall, 'expects.method.returning, when method is defined with multiple overlapping parameters, will return the values stated, in the order they are called (2)' ); + System.assertEquals( 'thirdReturn' , returnFromThirdCall , 'expects.method.returning, when method is defined with multiple overlapping parameters, will return the values stated, in the order they are called (3)' ); + } + + /** + * @method expects.method.returning using named parameters + * @case when the same method is defined with the same parameters, and called in order + * @result will return the stated value in the call order they are specified + */ + @isTest + private static void expectsMethodReturning_usingNamed_whenMethodIsDefinedAndCalledInOrder_returnTheResultsInOrder() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodUnderDouble' ) + .withParameterNamed( 'parameter1').setTo( 'ActualParam1' ) + .andParameterNamed( 'parameter2').setTo( 1 ) + .returning( 'firstReturn' ) + .then().expects() + .method( 'methodUnderDouble' ) + .withParameterNamed( 'parameter1').setTo( 'ActualParam1' ) + .andParameterNamed( 'parameter2').setTo( 1 ) + .returning( 'secondReturn' ) + .then().expects() + .method( 'methodUnderDouble' ) + .withParameterNamed( 'parameter1').setTo( 'ActualParam1' ) + .andParameterNamed( 'parameter2').setTo( 1 ) + .returning( 'thirdReturn' ); + + Test.startTest(); + String returnFromFirstCall = classToDouble.methodUnderDouble( 'ActualParam1', 1 ); + String returnFromSecondCall = classToDouble.methodUnderDouble( 'ActualParam1', 1 ); + String returnFromThirdCall = classToDouble.methodUnderDouble( 'ActualParam1', 1 ); + Test.stopTest(); + + System.assertEquals( 'firstReturn' , returnFromFirstCall , 'expects.method.returning, using named notation, when method is defined with multiple overlapping parameters, will return the values stated, in the order they are called (1)' ); + System.assertEquals( 'secondReturn', returnFromSecondCall, 'expects.method.returning, using named notation, when method is defined with multiple overlapping parameters, will return the values stated, in the order they are called (2)' ); + System.assertEquals( 'thirdReturn' , returnFromThirdCall , 'expects.method.returning, using named notation, when method is defined with multiple overlapping parameters, will return the values stated, in the order they are called (3)' ); + } + + /** + * @method expects.method.returning using named parameters, with some not specified + * @case when the same method is defined with the same parameters, and called in order + * @result will return the stated value in the call order they are specified + */ + @isTest + private static void expectsMethodReturning_usingNamedSomeNotSpecified_whenMethodIsDefinedAndCalledInOrder_returnTheResultsInOrder() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodUnderDouble' ) + .withParameterNamed( 'parameter1').setTo( 'ActualParam1' ) + .returning( 'firstReturn' ) + .then().expects() + .method( 'methodUnderDouble' ) + .withParameterNamed( 'parameter2').setTo( 1 ) + .returning( 'secondReturn' ) + .then().expects() + .method( 'methodUnderDouble' ) + .withParameterNamed( 'parameter2').setTo( 1 ) + .andParameterNamed( 'parameter1').setTo( 'ActualParam1' ) + .returning( 'thirdReturn' ); + + Test.startTest(); + String returnFromFirstCall = classToDouble.methodUnderDouble( 'ActualParam1', 1 ); + String returnFromSecondCall = classToDouble.methodUnderDouble( 'ActualParam1', 1 ); + String returnFromThirdCall = classToDouble.methodUnderDouble( 'ActualParam1', 1 ); + Test.stopTest(); + + System.assertEquals( 'firstReturn' , returnFromFirstCall , 'expects.method.returning, using named notation, when method is defined with multiple overlapping parameters with some not specified, will return the values stated, in the order they are called (1)' ); + System.assertEquals( 'secondReturn', returnFromSecondCall, 'expects.method.returning, using named notation, when method is defined with multiple overlapping parameters with some not specified, will return the values stated, in the order they are called (2)' ); + System.assertEquals( 'thirdReturn' , returnFromThirdCall , 'expects.method.returning, using named notation, when method is defined with multiple overlapping parameters with some not specified, will return the values stated, in the order they are called (3)' ); + } + + /** + * @method expects.method.returning + * @case when the same method is defined with the any parameters methods, and called in order + * @result will return the stated value in the call order they are specified + */ + @isTest + private static void expectsMethodReturning_whenMethodIsDefinedWithAnyParamsAndCalledInOrder_returnTheResultsInOrder() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodUnderDouble' ) + .withAnyParameters() + .returning( 'firstReturn' ) + .then().expects() + .method( 'methodUnderDouble' ) + .withAnyParameter() + .thenParameter( 1 ) + .returning( 'secondReturn' ) + .then().expects() + .method( 'methodUnderDouble' ) + .withParameter( 'ActualParam1' ) + .thenAnyParameter() + .returning( 'thirdReturn' ); + + Test.startTest(); + String returnFromFirstCall = classToDouble.methodUnderDouble( 'ActualParam1', 1 ); + String returnFromSecondCall = classToDouble.methodUnderDouble( 'ActualParam1', 1 ); + String returnFromThirdCall = classToDouble.methodUnderDouble( 'ActualParam1', 1 ); + Test.stopTest(); + + System.assertEquals( 'firstReturn' , returnFromFirstCall , 'expects.method.returning, when method is defined with multiple overlapping parameters using the anyParameter methods, will return the values stated, in the order they are called (1)' ); + System.assertEquals( 'secondReturn', returnFromSecondCall, 'expects.method.returning, when method is defined with multiple overlapping parameters using the anyParameter methods, will return the values stated, in the order they are called (2)' ); + System.assertEquals( 'thirdReturn' , returnFromThirdCall , 'expects.method.returning, when method is defined with multiple overlapping parameters using the anyParameter methods, will return the values stated, in the order they are called (3)' ); + } + + // + // Test Spy behaviour tests + // + /** + * @method countOf + * @case when given a method call that happened + * @result will return the number of times that method was called + */ + @isTest + private static void callCount_whenGivenAMethodCallThatHappened_returnTheNumberOfCalls() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Test.startTest(); + classToDouble.otherMethodUnderDouble( 'OtherActualParam1', 1 ); + classToDouble.methodUnderDouble( 'ActualParam1', 1 ); + classToDouble.methodUnderDouble( 'ActualParam2', 2 ); + classToDouble.otherMethodUnderDouble( 'OtherActualParam2', 2 ); + classToDouble.methodUnderDouble( 'ActualParam3', 3 ); // this is the one we're going to retrieve + classToDouble.otherMethodUnderDouble( 'OtherActualParam2', 3 ); + classToDouble.methodUnderDouble( 'ActualParam4', 4 ); + + Integer returnedMethodCallCount = classToDoubleController.countOf( 'methodUnderDouble' ); + Test.stopTest(); + + System.assertEquals( 4, returnedMethodCallCount, 'countOf, when given the details of a method call that happened amongst many - will return the number of calls' ); + } + + /** + * @method countOf + * @case when given a method call that did not happen + * @result will return zero + */ + @isTest + private static void callCount_whenGivenAMethodCallThatDidNotHappen_returnZero() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Test.startTest(); + Integer returnedMethodCallCount = classToDoubleController.countOf( 'methodUnderDouble' ); + Test.stopTest(); + + System.assertEquals( 0, returnedMethodCallCount, 'countOf, when given the details of a method call that did not happen - will return zero' ); + } + + /** + * @method call.of.parameters + * @case when given the details of a method call that happened + * @result will return the parameters of that call + */ + @isTest + private static void callOfParams_whenGivenACallThatHappened_returnTheParamsOfThatCall() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Test.startTest(); + classToDouble.methodUnderDouble( 'ActualParam', 2 ); + List passedParameters = classToDoubleController.call( 0 ).of( 'methodUnderDouble' ).parameters(); + Test.stopTest(); + + System.assertEquals( new List{ 'ActualParam', 2 }, passedParameters, 'call.of.parameters, when given the details of a method call that happened, will return the parameters of that call' ); + } + + /** + * @method call.of.parameters + * @case when given the details of a method that does not return anything, and a call that happened + * @result will return the parameters of that call + */ + @isTest + private static void callOfParams_whenGivenACallOfMethodWithNoReturnThatHappened_returnTheParamsOfThatCall() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Test.startTest(); + classToDouble.methodUnderDoubleWithNoReturn( 'ActualParam', 2 ); + List passedParameters = classToDoubleController.call( 0 ).of( 'methodUnderDoubleWithNoReturn' ).parameters(); + Test.stopTest(); + + System.assertEquals( new List{ 'ActualParam', 2 }, passedParameters, 'call.of.parameters, when given the details of a method call that returns nothing, will return the parameters of that call' ); + } + + /** + * @method call.of.parameters + * @case when given the details of a method call that happened amongst many + * @result will return the parameters of that call + */ + @isTest + private static void callOfParams_whenGivenACallThatHappenedAmongstMany_returnTheParamsOfThatCall() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Test.startTest(); + classToDouble.otherMethodUnderDouble( 'OtherActualParam1', 1 ); + classToDouble.methodUnderDouble( 'ActualParam1', 1 ); + classToDouble.methodUnderDouble( 'ActualParam2', 2 ); + classToDouble.otherMethodUnderDouble( 'OtherActualParam2', 2 ); + classToDouble.methodUnderDouble( 'ActualParam3', 3 ); // this is the one we're going to retrieve + classToDouble.otherMethodUnderDouble( 'OtherActualParam2', 3 ); + classToDouble.methodUnderDouble( 'ActualParam4', 4 ); + + List passedParameters = classToDoubleController.call( 2 ).of( 'methodUnderDouble' ).parameters(); + Test.stopTest(); + + System.assertEquals( new List{ 'ActualParam3', 3 }, passedParameters, 'call.of.parameters, when given the details of a method call that happened amongst many - index from 0, will return the parameters of that call' ); + } + + /** + * @method call.of.parameters + * @case when given a call number of -1 + * @result will return the parameters of the last call + */ + @isTest + private static void callOfParams_whenGivenAMinusOneCallNumber_returnTheParamsOfTheLastCall() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Test.startTest(); + classToDouble.otherMethodUnderDouble( 'OtherActualParam1', 1 ); + classToDouble.methodUnderDouble( 'ActualParam1', 1 ); + classToDouble.methodUnderDouble( 'ActualParam2', 2 ); + classToDouble.otherMethodUnderDouble( 'OtherActualParam2', 2 ); + classToDouble.methodUnderDouble( 'ActualParam3', 3 ); + classToDouble.otherMethodUnderDouble( 'OtherActualParam3', 3 ); + classToDouble.methodUnderDouble( 'ActualParam4', 4 ); // this is the one we're going to retrieve + classToDouble.otherMethodUnderDouble( 'OtherActualParam4', 4 ); + + List passedParameters = classToDoubleController.call( -1 ).of( 'methodUnderDouble' ).parameters(); + Test.stopTest(); + + System.assertEquals( new List{ 'ActualParam4', 4 }, passedParameters, 'call.of.parameters, when given the details of a method call with -1 call number, will return the parameters of the last call' ); + } + + /** + * @method latestCallOf.parameters + * @case when the method has been called + * @result will return the parameters of the last call + */ + @isTest + private static void latestCallOfParams_whenTheMethodHasBeenCalled_returnTheParamsOfTheLastCall() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Test.startTest(); + classToDouble.otherMethodUnderDouble( 'OtherActualParam1', 1 ); + classToDouble.methodUnderDouble( 'ActualParam1', 1 ); + classToDouble.methodUnderDouble( 'ActualParam2', 2 ); + classToDouble.otherMethodUnderDouble( 'OtherActualParam2', 2 ); + classToDouble.methodUnderDouble( 'ActualParam3', 3 ); + classToDouble.otherMethodUnderDouble( 'OtherActualParam3', 3 ); + classToDouble.methodUnderDouble( 'ActualParam4', 4 ); // this is the one we're going to retrieve + classToDouble.otherMethodUnderDouble( 'OtherActualParam4', 4 ); + + List passedParameters = classToDoubleController.latestCallOf( 'methodUnderDouble' ).parameters(); + Test.stopTest(); + + System.assertEquals( new List{ 'ActualParam4', 4 }, passedParameters, 'latestCallOf.parameters, when the method has been called, will return the parameters of the last call' ); + } + + /** + * @method call.of.parameters + * @case when given a negative call number + * @result will return the parameters of the call that many from the end + */ + @isTest + private static void callOfParams_whenGivenANegativeCallNumber_returnTheParamsOfTheCallThatManyFromTheEnd() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Test.startTest(); + classToDouble.otherMethodUnderDouble( 'OtherActualParam1', 1 ); + classToDouble.methodUnderDouble( 'ActualParam1', 1 ); + classToDouble.methodUnderDouble( 'ActualParam2', 2 ); // this is the one we're going to retrieve + classToDouble.otherMethodUnderDouble( 'OtherActualParam2', 2 ); + classToDouble.methodUnderDouble( 'ActualParam3', 3 ); + classToDouble.otherMethodUnderDouble( 'OtherActualParam2', 3 ); + classToDouble.methodUnderDouble( 'ActualParam4', 4 ); + + List passedParameters = classToDoubleController.call( -3 ).of( 'methodUnderDouble' ).parameters(); + Test.stopTest(); + + System.assertEquals( new List{ 'ActualParam2', 2 }, passedParameters, 'call.of.parameters, when given the details of a method call with a negative call number, will return the parameters of the call that many from the end' ); + } + + /** + * @method call.of.parameters + * @case when given the details of a method call that happened with an Sobject + * @result will return the parameters of that call + */ + @isTest + private static void callOfParams_whenGivenACallThatHappenedWithAnSobject_returnTheParamsOfThatCall() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Test.startTest(); + Contact passedContact = new Contact( FirstName = 'Person' ); + + classToDouble.sobjectMethodUnderDouble( passedContact ); + List passedParameters = classToDoubleController.call( 0 ).of( 'sobjectMethodUnderDouble' ).parameters(); + Test.stopTest(); + + System.assertEquals( new List{ passedContact }, passedParameters, 'call.of.parameters, when given the details of a method call that happened with an sobject, will return the parameters of that call' ); + } + + /** + * @method call.of.parameters + * @case when given the details of a method call that happened with no parameters + * @result will return an empty list for that call + */ + @isTest + private static void callOfParams_whenGivenACallThatHappenedWithNoParams_returnAnEmptyList() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Test.startTest(); + classToDouble.methodWithNoParametersUnderDouble(); + List passedParameters = classToDoubleController.call( 0 ).of( 'methodWithNoParametersUnderDouble' ).parameters(); + Test.stopTest(); + + System.assertEquals( new List(), passedParameters, 'call.of.parameters, when given the details of a method call that happened without any parameters, will return an empty list' ); + } + + /** + * @method call.of.parameters + * @case when given the details of a call number that did not happen + * @result will throw an exception + */ + @isTest + private static void callOfParams_whenGivenACallNumberThatDidNotHappen_throwAnException() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Test.startTest(); + + classToDouble.methodUnderDouble( 'ActualParam', 2 ); + + Boolean exceptionThrown = false; + String exceptionMessage; + + try { + List passedParameters = classToDoubleController.call( 1 ).of( 'methodUnderDouble' ).parameters(); + } catch ( Exception e ) { + exceptionThrown = true; + exceptionMessage = e.getMessage(); + } + + Test.stopTest(); + + System.assert( exceptionThrown, 'call.of.parameters, when given the details of a call number that did not happen, will throw an exception' ); + Amoss_Asserts.assertContains( 'methodUnderDouble was not called 2 times', exceptionMessage, 'call.of.parameters, when given the details of a call number that did not happen, will throw an exception with a useful message' ); + } + + + /** + * @method call.of.parameters + * @case when given a negative call number that did not happen + * @result will throw an exception + */ + @isTest + private static void callOfParams_whenGivenANegativeCallNumberThatDidNotHappen_throwAnException() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Test.startTest(); + + classToDouble.methodUnderDouble( 'ActualParam', 2 ); + + Boolean exceptionThrown = false; + String exceptionMessage; + + try { + List passedParameters = classToDoubleController.call( -2 ).of( 'methodUnderDouble' ).parameters(); + } catch ( Exception e ) { + exceptionThrown = true; + exceptionMessage = e.getMessage(); + } + + Test.stopTest(); + + System.assert( exceptionThrown, 'call.of.parameters, when given the details of a negative call number that did not happen, will throw an exception' ); + Amoss_Asserts.assertContains( 'methodUnderDouble was not called 2 times', exceptionMessage, 'call.of.parameters, when given the details of a negative mcall number that did not happen, will throw an exception with a useful message' ); + } + + /** + * @method call.of.parameters + * @case when given the details of a method that was never called + * @result will throw an exception + */ + @isTest + private static void callOfParams_whenGivenAMethodThatWasNeverCalled_throwAnException() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Test.startTest(); + + Boolean exceptionThrown = false; + String exceptionMessage; + + try { + List passedParameters = classToDoubleController.call( 0 ).of( 'methodUnderDouble' ).parameters(); + } catch ( Exception e ) { + exceptionThrown = true; + exceptionMessage = e.getMessage(); + } + + Test.stopTest(); + + System.assert( exceptionThrown, 'call.of.parameters, when given the details of a method that was never called, will throw an exception' ); + Amoss_Asserts.assertContains( 'methodUnderDouble was never called', exceptionMessage, 'call.of.parameters, when given the details of method that was never called, will throw an exception with a useful message' ); + } + + /** + * @method call.of.parametersByName + * @case when given the details of a method that was never called + * @result will throw an exception + */ + @isTest + private static void callOfParametersByName_whenGivenAMethodThatWasNeverCalled_throwAnException() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Test.startTest(); + + Boolean exceptionThrown = false; + String exceptionMessage; + + try { + Map passedParameters = classToDoubleController.call( 0 ).of( 'methodUnderDouble' ).parametersByName(); + } catch ( Exception e ) { + exceptionThrown = true; + exceptionMessage = e.getMessage(); + } + + Test.stopTest(); + + System.assert( exceptionThrown, 'call.of.parametersByName, when given the details of a method that was never called, will throw an exception' ); + Amoss_Asserts.assertContains( 'methodUnderDouble was never called', exceptionMessage, 'call.of.parametersByName, when given the details of method that was never called, will throw an exception with a useful message' ); + } + + /** + * @method call.of.parameter (Integer) + * @case when given the details of a method call that happened + * @result will return the stated parameter of that call + */ + @isTest + private static void callOfParameter_whenGivenACallThatHappened_returnTheStatedParameterOfThatCall() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Test.startTest(); + classToDouble.methodUnderDouble( 'ActualParam', 2 ); + Object passedParameter = classToDoubleController.call( 0 ).of( 'methodUnderDouble' ).parameter( 0 ); + Test.stopTest(); + + System.assertEquals( 'ActualParam', passedParameter, 'call.of.parameter, when given the details of a method call that happened, will return the stated parameter of that call' ); + } + + /** + * @method call.of.parameter (String) + * @case when given the details of a method call that happened + * @result will return the stated parameter of that call + */ + @isTest + private static void callOfParameterString_whenGivenACallThatHappened_returnTheStatedParameterOfThatCall() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Test.startTest(); + classToDouble.methodUnderDouble( 'ActualParam', 2 ); + Object passedParameter = classToDoubleController.call( 0 ).of( 'methodUnderDouble' ).parameter( 'parameter1' ); + Test.stopTest(); + + System.assertEquals( 'ActualParam', passedParameter, 'call.of.parameter (String), when given the details of a method call that happened, will return the stated parameter of that call' ); + } + + /** + * @method call.of.parameter + * @case when given the details of a method call that happened amongst many + * @result will return the parameter of that call + */ + @isTest + private static void callOfParameter_whenGivenACallThatHappenedAmongstMany_returnTheParameterOfThatCall() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Test.startTest(); + classToDouble.otherMethodUnderDouble( 'OtherActualParam1', 1 ); + classToDouble.methodUnderDouble( 'ActualParam1', 1 ); + classToDouble.methodUnderDouble( 'ActualParam2', 2 ); + classToDouble.otherMethodUnderDouble( 'OtherActualParam2', 2 ); + classToDouble.methodUnderDouble( 'ActualParam3', 3 ); // this is the one we're going to retrieve + classToDouble.otherMethodUnderDouble( 'OtherActualParam2', 3 ); + classToDouble.methodUnderDouble( 'ActualParam4', 4 ); + + Object passedParameter = classToDoubleController.call( 2 ).of( 'methodUnderDouble' ).parameter( 1 ); + Test.stopTest(); + + System.assertEquals( 3, passedParameter, 'call.of.parameter, when given the details of a method call that happened amongst many - index from 0, will return the stated parameter of that call' ); + } + + /** + * @method call.of.parameter (String) + * @case when given the details of a method call that happened amongst many + * @result will return the parameter of that call + */ + @isTest + private static void callOfParameterString_whenGivenACallThatHappenedAmongstMany_returnTheParameterOfThatCall() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Test.startTest(); + classToDouble.otherMethodUnderDouble( 'OtherActualParam1', 1 ); + classToDouble.methodUnderDouble( 'ActualParam1', 1 ); + classToDouble.methodUnderDouble( 'ActualParam2', 2 ); + classToDouble.otherMethodUnderDouble( 'OtherActualParam2', 2 ); + classToDouble.methodUnderDouble( 'ActualParam3', 3 ); // this is the one we're going to retrieve + classToDouble.otherMethodUnderDouble( 'OtherActualParam2', 3 ); + classToDouble.methodUnderDouble( 'ActualParam4', 4 ); + + Object passedParameter = classToDoubleController.call( 2 ).of( 'methodUnderDouble' ).parameter( 'parameter2' ); + Test.stopTest(); + + System.assertEquals( 3, passedParameter, 'call.of.parameter (String), when given the details of a method call that happened amongst many - index from 0, will return the stated parameter of that call' ); + } + + /** + * @method call.of.parameter + * @case when given the details of a call number that did not happen + * @result will throw an exception + */ + @isTest + private static void callOfParameter_whenGivenACallNumberThatDidNotHappen_throwAnException() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Test.startTest(); + + classToDouble.methodUnderDouble( 'ActualParam', 2 ); + + Boolean exceptionThrown = false; + String exceptionMessage; + + try { + Object passedParameter = classToDoubleController.call( 1 ).of( 'methodUnderDouble' ).parameter( 1 ); + } catch ( Exception e ) { + exceptionThrown = true; + exceptionMessage = e.getMessage(); + } + + Test.stopTest(); + + System.assert( exceptionThrown, 'call.of.parameter, when given the details of a call number that did not happen, will throw an exception' ); + Amoss_Asserts.assertContains( 'methodUnderDouble was not called 2 times', exceptionMessage, 'call.of.parameter, when given the details of a call number that did not happen, will throw an exception with a useful message' ); + } + + /** + * @method call.of.parameter (String) + * @case when given the details of a call number that did not happen + * @result will throw an exception + */ + @isTest + private static void callOfParameterString_whenGivenACallNumberThatDidNotHappen_throwAnException() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Test.startTest(); + + classToDouble.methodUnderDouble( 'ActualParam', 2 ); + + Boolean exceptionThrown = false; + String exceptionMessage; + + try { + Object passedParameter = classToDoubleController.call( 1 ).of( 'methodUnderDouble' ).parameter( 'parameter1' ); + } catch ( Exception e ) { + exceptionThrown = true; + exceptionMessage = e.getMessage(); + } + + Test.stopTest(); + + System.assert( exceptionThrown, 'call.of.parameter (String), when given the details of a call number that did not happen, will throw an exception' ); + Amoss_Asserts.assertContains( 'methodUnderDouble was not called 2 times', exceptionMessage, 'call.of.parameter (String), when given the details of a call number that did not happen, will throw an exception with a useful message' ); + } + + /** + * @method call.of.parameter + * @case when given the details of a method that was never called + * @result will throw an exception + */ + @isTest + private static void callOfParameter_whenGivenAMethodThatWasNeverCalled_throwAnException() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Test.startTest(); + + Boolean exceptionThrown = false; + String exceptionMessage; + + try { + Object passedParameter = classToDoubleController.call( 0 ).of( 'methodUnderDouble' ).parameter( 1 ); + } catch ( Exception e ) { + exceptionThrown = true; + exceptionMessage = e.getMessage(); + } + + Test.stopTest(); + + System.assert( exceptionThrown, 'call.of.parameter, when given the details of a method that was never called, will throw an exception' ); + Amoss_Asserts.assertContains( 'methodUnderDouble was never called', exceptionMessage, 'call.of.parameter, when given the details of method that was never called, will throw an exception with a useful message' ); + } + + /** + * @method call.of.parameter (String) + * @case when given the details of a method that was never called + * @result will throw an exception + */ + @isTest + private static void callOfParameterString_whenGivenAMethodThatWasNeverCalled_throwAnException() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Test.startTest(); + + Boolean exceptionThrown = false; + String exceptionMessage; + + try { + Object passedParameter = classToDoubleController.call( 0 ).of( 'methodUnderDouble' ).parameter( 'parameter1' ); + } catch ( Exception e ) { + exceptionThrown = true; + exceptionMessage = e.getMessage(); + } + + Test.stopTest(); + + System.assert( exceptionThrown, 'call.of.parameter (String), when given the details of a method that was never called, will throw an exception' ); + Amoss_Asserts.assertContains( 'methodUnderDouble was never called', exceptionMessage, 'call.of.parameter (String), when given the details of method that was never called, will throw an exception with a useful message' ); + } + + /** + * @method call.of.parameter + * @case when given the parameter number that is out of range + * @result will throw an exception + */ + @isTest + private static void callOfParameter_whenGivenAParameterNumberThatIsOutOfRange_throwAnException() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Test.startTest(); + + classToDouble.methodUnderDouble( 'ActualParam', 2 ); + + Boolean exceptionThrown = false; + String exceptionMessage; + + try { + Object passedParameter = classToDoubleController.call( 0 ).of( 'methodUnderDouble' ).parameter( 2 ); + } catch ( Exception e ) { + exceptionThrown = true; + exceptionMessage = e.getMessage(); + } + + Test.stopTest(); + + System.assert( exceptionThrown, 'call.of.parameter, when given a parameter number that is out of range, will throw an exception' ); + Amoss_Asserts.assertContains( 'methodUnderDouble was not called with 3 parameters', exceptionMessage, 'call.of.parameter, when given a parameter number that is out of range, will throw an exception with a useful message' ); + } + + /** + * @method call.of.parameter + * @case when given the parameter number that is negative + * @result will throw an exception + */ + @isTest + private static void callOfParameter_whenGivenAParameterNumberThatIsNegative_throwAnException() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Test.startTest(); + + classToDouble.methodUnderDouble( 'ActualParam', 2 ); + + Boolean exceptionThrown = false; + String exceptionMessage; + + try { + Object passedParameter = classToDoubleController.call( 0 ).of( 'methodUnderDouble' ).parameter( -1 ); + } catch ( Exception e ) { + exceptionThrown = true; + exceptionMessage = e.getMessage(); + } + + Test.stopTest(); + + System.assert( exceptionThrown, 'call.of.parameter, when given a parameter number that is negative, will throw an exception' ); + Amoss_Asserts.assertContains( 'Cannot request a negative parameter number (got -1)', exceptionMessage, 'call.of.parameter, when given a parameter number that is negative, will throw an exception with a useful message' ); + } + + /** + * @method call.of.parameter (String) + * @case when given the parameter name that does not exist + * @result will throw an exception + */ + @isTest + private static void callOfParameter_whenGivenAParameterNameThatDoesNotExist_throwAnException() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Test.startTest(); + + classToDouble.methodUnderDouble( 'ActualParam', 2 ); + + Boolean exceptionThrown = false; + String exceptionMessage; + + try { + Object passedParameter = classToDoubleController.call( 0 ).of( 'methodUnderDouble' ).parameter( 'doesNotExist' ); + } catch ( Exception e ) { + exceptionThrown = true; + exceptionMessage = e.getMessage(); + } + + Test.stopTest(); + + System.assert( exceptionThrown, 'call.of.parameter (String), when given a parameter name that does not exist, will throw an exception' ); + Amoss_Asserts.assertContains( 'methodUnderDouble was not called with parameter "doesNotExist"', exceptionMessage, 'call.of.parameter (String), when given a parameter name that does not exist, will throw an exception with a useful message' ); + } + + /** + * @method call.of.parameter + * @case when given the parameter number for a method that has no parameters + * @result will throw an exception + */ + @isTest + private static void callOfParameter_whenGivenAParameterForAMethodThatHasNoParams_throwAnException() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Test.startTest(); + + classToDouble.methodWithNoParametersUnderDouble(); + + Boolean exceptionThrown = false; + String exceptionMessage; + + try { + Object passedParameter = classToDoubleController.call( 0 ).of( 'methodWithNoParametersUnderDouble' ).parameter( 0 ); + } catch ( Exception e ) { + exceptionThrown = true; + exceptionMessage = e.getMessage(); + } + + Test.stopTest(); + + System.assert( exceptionThrown, 'call.of.parameter, when given a parameter number for a method that has no parameters, will throw an exception' ); + Amoss_Asserts.assertContains( 'methodWithNoParametersUnderDouble was not called with any parameters', exceptionMessage, 'call.of.parameter, when given a parameter number for a method that has no parameters, will throw an exception with a useful message' ); + } + + /** + * @method call.of.parameter (String) + * @case when given the parameter name for a method that has no parameters + * @result will throw an exception + */ + @isTest + private static void callOfParameterString_whenGivenAParameterForAMethodThatHasNoParams_throwAnException() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Test.startTest(); + + classToDouble.methodWithNoParametersUnderDouble(); + + Boolean exceptionThrown = false; + String exceptionMessage; + + try { + Object passedParameter = classToDoubleController.call( 0 ).of( 'methodWithNoParametersUnderDouble' ).parameter( 'noParametersExist' ); + } catch ( Exception e ) { + exceptionThrown = true; + exceptionMessage = e.getMessage(); + } + + Test.stopTest(); + + System.assert( exceptionThrown, 'call.of.parameter (String), when given a parameter name for a method that has no parameters, will throw an exception' ); + Amoss_Asserts.assertContains( 'methodWithNoParametersUnderDouble was not called with any parameters', exceptionMessage, 'call.of.parameter (String), when given a parameter name for a method that has no parameters, will throw an exception with a useful message' ); + } + + /** + * @method get.call.of.parameters + * @case when given the details of a method call that happened + * @result will return the parameters of that call + */ + @isTest + private static void getCallOfParams_whenGivenACallThatHappened_returnTheParamsOfThatCall() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Test.startTest(); + classToDouble.methodUnderDouble( 'ActualParam', 2 ); + List passedParameters = classToDoubleController.get().call( 0 ).of( 'methodUnderDouble' ).parameters(); + Test.stopTest(); + + System.assertEquals( new List{ 'ActualParam', 2 }, passedParameters, 'get.call.of.parameters, when given the details of a method call that happened, will return the parameters of that call' ); + } + + /** + * @method get.call.of.parameters + * @case when given the details of a method call with no parameters that happened + * @result will return an empty parameters list + */ + @isTest + private static void getCallOfParams_whenGivenACallThatHappenedWithNoParams_returnAnEmptyParametersList() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Test.startTest(); + classToDouble.methodWithNoParametersUnderDouble(); + List passedParameters = classToDoubleController.get().call( 0 ).of( 'methodWithNoParametersUnderDouble' ).parameters(); + Test.stopTest(); + + System.assertEquals( new List(), passedParameters, 'get.call.of.parameters, when given the details of a method call with no parameters that happened, will return an empty list of parameters' ); + } + + /** + * @method get.call.of.parametersByName + * @case when given the details of a method call that happened + * @result will return the parameters of that call + */ + @isTest + private static void getCallOfParametersByName_whenGivenACallThatHappened_returnTheParamsOfThatCall() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Test.startTest(); + classToDouble.methodUnderDouble( 'ActualParam', 2 ); + Map passedParameters = classToDoubleController.get().call( 0 ).of( 'methodUnderDouble' ).parametersByName(); + Test.stopTest(); + + Map expectedParameters = new Map{ + 'parameter1' => 'ActualParam', + 'parameter2' => 2 + }; + + System.assertEquals( expectedParameters, passedParameters, 'get.call.of.parametersByName, when given the details of a method call that happened, will return the parameters of that call indexed by name' ); + } + + /** + * @method get.call.of.parametersByName + * @case when given the details of a method call with no parameters that happened + * @result will return an empty parameters list + */ + @isTest + private static void getCallOfParametersByName_whenGivenACallThatHappenedWithNoParams_returnAnEmptyParametersList() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Test.startTest(); + classToDouble.methodWithNoParametersUnderDouble(); + Map passedParameters = classToDoubleController.get().call( 0 ).of( 'methodWithNoParametersUnderDouble' ).parametersByName(); + Test.stopTest(); + + System.assertEquals( new Map(), passedParameters, 'get.call.of.parameters, when given the details of a method call with no parameters that happened, will return an empty map of parameters' ); + } + + /** + * @method get.latestCallOf.parameters + * @case when given the details of the last method call that happened + * @result will return the parameters of that call + */ + @isTest + private static void getLatestCallOfParams_whenGivenACallThatHappened_returnTheParamsOfThatCall() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Test.startTest(); + classToDouble.methodUnderDouble( 'ActualParam1', 2 ); + classToDouble.methodUnderDouble( 'ActualParam2', 3 ); + classToDouble.methodUnderDouble( 'ActualParam3', 4 ); + List passedParameters = classToDoubleController.get().latestCallOf( 'methodUnderDouble' ).parameters(); + Test.stopTest(); + + System.assertEquals( new List{ 'ActualParam3', 4 }, passedParameters, 'get.latestCallOf.parameters, when given the details of a method call that happened, will return the parameters of that call' ); + } + + /** + * @method parameter + * @case when a parameter is a list of objects + * @result will return the parameter + */ + @isTest + private static void parameter_whenAParameterIsAListOfObjects_returnTheParameter() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Test.startTest(); + List parameterPassed = new List{ 2, '3', new Account() }; + + classToDouble.methodWithListOfObjects( parameterPassed ); + List parameterReturned = (List)classToDoubleController.call( 0 ).of( 'methodWithListOfObjects' ).parameter( 0 ); + Test.stopTest(); + + System.assertEquals( parameterPassed, parameterReturned, 'parameters, when a parameter is a list of objects, will return the parameter' ); + } + + /** + * @method parameter (String) + * @case when a parameter is a list of objects + * @result will return the parameter + */ + @isTest + private static void parameterString_whenAParameterIsAListOfObjects_returnTheParameter() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Test.startTest(); + List parameterPassed = new List{ 2, '3', new Account() }; + + classToDouble.methodWithListOfObjects( parameterPassed ); + List parameterReturned = (List)classToDoubleController.call( 0 ).of( 'methodWithListOfObjects' ).parameter( 'parameter1' ); + Test.stopTest(); + + System.assertEquals( parameterPassed, parameterReturned, 'parameter (String), when a parameter is a list of objects, will return the parameter' ); + } + + /** + * @method parameter + * @case when a parameter is a set of objects + * @result will return the parameter + */ + @isTest + private static void parameter_whenAParameterIsASetOfObjects_returnTheParameter() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Test.startTest(); + Set parameterPassed = new Set{ 2, '3', new Account() }; + + classToDouble.methodWithSetOfObjects( parameterPassed ); + Set parameterReturned = (Set)classToDoubleController.call( 0 ).of( 'methodWithSetOfObjects' ).parameter( 0 ); + Test.stopTest(); + + System.assertEquals( parameterPassed, parameterReturned, 'parameters, when a parameter is a set of objects, will return the parameter' ); + } + + /** + * @method parameter (String) + * @case when a parameter is a set of objects + * @result will return the parameter + */ + @isTest + private static void parameterString_whenAParameterIsASetOfObjects_returnTheParameter() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Test.startTest(); + Set parameterPassed = new Set{ 2, '3', new Account() }; + + classToDouble.methodWithSetOfObjects( parameterPassed ); + Set parameterReturned = (Set)classToDoubleController.call( 0 ).of( 'methodWithSetOfObjects' ).parameter( 'parameter1' ); + Test.stopTest(); + + System.assertEquals( parameterPassed, parameterReturned, 'parameter (String), when a parameter is a set of objects, will return the parameter' ); + } + + /** + * @method parameter + * @case when a parameter is a map of objects + * @result will return the parameter + */ + @isTest + private static void parameter_whenAParameterIsAMapOfObjects_returnTheParameter() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Test.startTest(); + Map parameterPassed = new Map{ 'one' => 1, 'two' => '2', 'three' => new Contact() }; + + classToDouble.methodWithMapStringToObject( parameterPassed ); + Map parameterReturned = (Map)classToDoubleController.call( 0 ).of( 'methodWithMapStringToObject' ).parameter( 0 ); + Test.stopTest(); + + System.assertEquals( parameterPassed, parameterReturned, 'parameters, when a parameter is a map of objects, will return the parameter' ); + } + + /** + * @method parameter (String) + * @case when a parameter is a map of objects + * @result will return the parameter + */ + @isTest + private static void parameterString_whenAParameterIsAMapOfObjects_returnTheParameter() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Test.startTest(); + Map parameterPassed = new Map{ 'one' => 1, 'two' => '2', 'three' => new Contact() }; + + classToDouble.methodWithMapStringToObject( parameterPassed ); + Map parameterReturned = (Map)classToDoubleController.call( 0 ).of( 'methodWithMapStringToObject' ).parameter( 'parameter1' ); + Test.stopTest(); + + System.assertEquals( parameterPassed, parameterReturned, 'parameter (String), when a parameter is a map of objects, will return the parameter' ); + } + + // + // Mock Object behaviour tests + // + /** + * @method expects.method + * @case when the method is called + * @result will not fail + */ + @isTest + private static void expectsMethod_whenCalled_pass() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodUnderDoubleWithNoReturn' ); + + Test.startTest(); + classToDouble.methodUnderDoubleWithNoReturn( 'this', 1 ); + Test.stopTest(); + + System.assertEquals( 1, classToDoubleController.countOf( 'methodUnderDoubleWithNoReturn' ), 'expects.method, when the method is called once, will not fail - and the call count will be 1' ); + } + + /** + * @method expects.method + * @case when the method is called more than once + * @result will fail the test + */ + @isTest + private static void expectsMethod_whenCalledMoreThanOnce_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodUnderDoubleWithNoReturn' ); + + Test.startTest(); + classToDouble.methodUnderDoubleWithNoReturn( 'this', 1 ); + + try { + classToDouble.methodUnderDoubleWithNoReturn( 'this', 1 ); + } catch ( TestException e ) {} + Test.stopTest(); + + String expectedAssertion = CLASS_TO_DOUBLE + '.methodUnderDoubleWithNoReturn was called more times than was expected'; + System.assertEquals( false, assertsDoubleController.get().latestCallOf( 'assert' ).parameter( 0 ), 'expects.method, when the method is called more times than expected, will fail by calling assert with false' ); + Amoss_Asserts.assertContains( expectedAssertion, assertsDoubleController.get().latestCallOf( 'assert' ).parameter( 1 ), 'expects.method, when the method is called more times than expected, will fail by calling assert with an assertion message that clearly describes the issue' ); + } + + /** + * @method expects.method + * @case when the method is not called, but neither is verify + * @result will not fail + */ + @isTest + private static void expectsMethod_whenTheMethodIsNotCalledButNeitherIsVerify_pass() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodUnderDoubleWithNoReturn' ); + + Test.startTest(); + // we do nothing, because we're testing what happens when the method is not called + Test.stopTest(); + + System.assertEquals( 0, classToDoubleController.countOf( 'methodUnderDoubleWithNoReturn' ), 'expects.method, when the method is not called, and neither is verify, will not fail - and the call count will be zero' ); + } + + /** + * @method expects.method + * @case when the method is not called, and verify is + * @result will fail + */ + @isTest + private static void expectsMethod_whenTheMethodIsNotCalledAndVerifyIs_fail() { + + Amoss_Instance assertsDoubleController = new Amoss_Instance( Amoss_Asserts.class ); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + assertsDoubleController + .allows() + .method( 'assertEquals' ); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodUnderDoubleWithNoReturn' ); + + Test.startTest(); + classToDoubleController.verify(); + Test.stopTest(); + + String expectedAssertion = 'Expected call stack for ' + CLASS_TO_DOUBLE + ' should be empty, and it is not'; + + System.assertEquals( new List() , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 0 ), 'expects.method, when the method is not called, and verify is, will fail by calling assertEquals with an expected empty call stack' ); + System.assertEquals( new List{ 'methodUnderDoubleWithNoReturn( any )' }, assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 1 ), 'expects.method, when the method is not called, and verify is, will fail by calling assertEquals with a call stack description that describes the outstanding method calls' ); + System.assertEquals( expectedAssertion , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 2 ), 'expects.method, when the method is not called, and verify is, will fail by calling assertEquals with an assertion message that clearly describes the issue' ); + } + + /** + * @method expects.method, multiple times + * @case when the methods are not called, and verify is + * @result will fail + */ + @isTest + private static void expectsMethodMultipleTimes_whenTheMethodsAreNotCalledAndVerifyIs_fail() { + + Amoss_Instance assertsDoubleController = new Amoss_Instance( Amoss_Asserts.class ); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + assertsDoubleController + .allows() + .method( 'assertEquals' ); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodUnderDoubleWithNoReturn' ) + .then().expects() + .method( 'methodUnderDouble' ) + .then().expects() + .method( 'methodUnderDouble' ); + + Test.startTest(); + classToDoubleController.verify(); + Test.stopTest(); + + List expectedMethodDescriptions = new List{ 'methodUnderDoubleWithNoReturn( any )', 'methodUnderDouble( any )', 'methodUnderDouble( any )' }; + String expectedAssertion = 'Expected call stack for ' + CLASS_TO_DOUBLE + ' should be empty, and it is not'; + + System.assertEquals( new List() , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 0 ), 'expects.method multiple time, when the methods are not called, and verify is, will fail by calling assertEquals with an expected empty call stack' ); + System.assertEquals( expectedMethodDescriptions, assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 1 ), 'expects.method multiple time, when the methods are not called, and verify is, will fail by calling assertEquals with a call stack description that describes the outstanding method calls' ); + System.assertEquals( expectedAssertion , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 2 ), 'expects.method multiple time, when the methods are not called, and verify is, will fail by calling assertEquals with an assertion message that clearly describes the issue' ); + } + + /** + * @method expects.method.withParameter + * @case when the method is not called, and verify is + * @result will fail and describe the parameters + */ + @isTest + private static void expectsWithParameter_whenTheMethodIsNotCalledAndVerifyIs_failDescribingTheParams() { + + Amoss_Instance assertsDoubleController = new Amoss_Instance( Amoss_Asserts.class ); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + assertsDoubleController + .allows() + .method( 'assertEquals' ); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodUnderDouble' ) + .withParameter( 'ParamValue1' ) + .thenParameter( 2 ); + + Test.startTest(); + classToDoubleController.verify(); + Test.stopTest(); + + String expectedAssertion = 'Expected call stack for ' + CLASS_TO_DOUBLE + ' should be empty, and it is not'; + + System.assertEquals( new List() , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 0 ), 'expects.method.withParameter, when the method is not called, and verify is, will fail by calling assertEquals with an expected empty call stack' ); + System.assertEquals( new List{ 'methodUnderDouble( ParamValue1, 2 )' }, assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 1 ), 'expects.method.withParameter, when the method is not called, and verify is, will fail by calling assertEquals with a call stack description that describes the outstanding method calls' ); + System.assertEquals( expectedAssertion , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 2 ), 'expects.method.withParameter, when the method is not called, and verify is, will fail by calling assertEquals with an assertion message that clearly describes the issue' ); + } + + /** + * @method expects.method.withParameter + * @case when an 'any' parameter is defined, the method is not called, and verify is + * @result will fail and describe the parameters + */ + @isTest + private static void expectsWithParameter_whenTheMethodWithAnyParamDefinedIsNotCalledAndVerifyIs_failDescribingTheParams() { + + Amoss_Instance assertsDoubleController = new Amoss_Instance( Amoss_Asserts.class ); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + assertsDoubleController + .allows() + .method( 'assertEquals' ); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodUnderDouble' ) + .withParameter( 'ParamValue1' ) + .thenAnyParameter(); + + Test.startTest(); + classToDoubleController.verify(); + Test.stopTest(); + + String expectedAssertion = 'Expected call stack for ' + CLASS_TO_DOUBLE + ' should be empty, and it is not'; + + System.assertEquals( new List() , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 0 ), 'expects.method.withParameter, when the method is not called, and an any parameter is defined, and verify is, will fail by calling assertEquals with an expected empty call stack' ); + System.assertEquals( new List{ 'methodUnderDouble( ParamValue1, Any value )' }, assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 1 ), 'expects.method.withParameter, when the method is not called, and an any parameter is defined, and verify is, will fail by calling assertEquals with a call stack description that describes the outstanding method calls' ); + System.assertEquals( expectedAssertion , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 2 ), 'expects.method.withParameter, when the method is not called, and an any parameter is defined, and verify is, will fail by calling assertEquals with an assertion message that clearly describes the issue' ); + } + + /** + * @method expects.method.withParameter.setToTheSameValueAs + * @case when the method is not called, and verify is + * @result will fail and describe the parameters + */ + @isTest + private static void expectsWithParameterSetToTheSameValueAs_whenTheMethodIsNotCalled_failDescribingTheParams() { + + Amoss_Instance assertsDoubleController = new Amoss_Instance( Amoss_Asserts.class ); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + assertsDoubleController + .allows() + .method( 'assertEquals' ); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodUnderDouble' ) + .withParameter().setToTheSameValueAs( 'expectedParameterValue' ); + + Test.startTest(); + classToDoubleController.verify(); + Test.stopTest(); + + String expectedAssertion = 'Expected call stack for ' + CLASS_TO_DOUBLE + ' should be empty, and it is not'; + + System.assertEquals( new List() , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 0 ), 'expects.method.withParameter.setToTheSameValueAs, when the method is not called, and verify is, will fail by calling assertEquals with an expected empty call stack' ); + System.assertEquals( new List{ 'methodUnderDouble( expectedParameterValue )' }, assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 1 ), 'expects.method.withParameter.setToTheSameValueAs, when the method is not called, and verify is, will fail by calling assertEquals with a call stack description that describes the outstanding method calls' ); + System.assertEquals( expectedAssertion , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 2 ), 'expects.method.withParameter.setToTheSameValueAs, when the method is not called, and verify is, will fail by calling assertEquals with an assertion message that clearly describes the issue' ); + } + + /** + * @method expects.method.withParameter.setToTheSameValueAs (object) + * @case when the method is not called, and verify is + * @result will fail and describe the parameters + */ + @isTest + private static void expectsWithParameterSetToTheSameValueAsWithObject_whenTheMethodIsNotCalled_failDescribingTheParams() { + + Amoss_Instance assertsDoubleController = new Amoss_Instance( Amoss_Asserts.class ); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + assertsDoubleController + .allows() + .method( 'assertEquals' ); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodUnderDouble' ) + .withParameter().setToTheSameValueAs( new AmossTest_ClassToDouble( 'expectedObjectParam' ) ) + .thenParameter().setToTheSameValueAs( new AmossTest_ClassToDouble( 'expectedObjectParam' ) ); + + Test.startTest(); + classToDoubleController.verify(); + Test.stopTest(); + + String expectedAssertion = 'Expected call stack for ' + CLASS_TO_DOUBLE + ' should be empty, and it is not'; + String expectedCall = 'methodUnderDouble( AmossTest_ClassToDouble:[], AmossTest_ClassToDouble:[] )'; + + System.assertEquals( new List() , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 0 ), 'expects.method.withParameter.setToTheSameValueAs, with an object, when the method is not called, and verify is, will fail by calling assertEquals with an expected empty call stack' ); + System.assertEquals( new List{ expectedCall }, assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 1 ), 'expects.method.withParameter.setToTheSameValueAs, with an object, when the method is not called, and verify is, will fail by calling assertEquals with a call stack description that describes the outstanding method calls' ); + System.assertEquals( expectedAssertion , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 2 ), 'expects.method.withParameter.setToTheSameValueAs, with an object, when the method is not called, and verify is, will fail by calling assertEquals with an assertion message that clearly describes the issue' ); + } + + /** + * @method expects.method.withParameter.withFieldsSetTo + * @case when the method is not called, and verify is + * @result will fail and describe the parameters + */ + @isTest + private static void expectsWithParameterWithFieldsSetTo_whenTheMethodIsNotCalled_failDescribingTheParams() { + + Amoss_Instance assertsDoubleController = new Amoss_Instance( Amoss_Asserts.class ); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + assertsDoubleController + .allows() + .method( 'assertEquals' ); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodUnderDouble' ) + .withParameter().withFieldsSetTo( new Map{ 'Field' => 'expectedValue' } ); + + Test.startTest(); + classToDoubleController.verify(); + Test.stopTest(); + + String expectedAssertion = 'Expected call stack for ' + CLASS_TO_DOUBLE + ' should be empty, and it is not'; + String expectedCallStackEntry = 'methodUnderDouble( SObject with at least the same fields set as {Field=expectedValue} )'; + + System.assertEquals( new List() , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 0 ), 'expects.method.withParameter.withFieldsSetTo, when the method is not called, and verify is, will fail by calling assertEquals with an expected empty call stack' ); + System.assertEquals( new List{ expectedCallStackEntry }, assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 1 ), 'expects.method.withParameter.withFieldsSetTo, when the method is not called, and verify is, will fail by calling assertEquals with a call stack description that describes the outstanding method calls' ); + System.assertEquals( expectedAssertion , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 2 ), 'expects.method.withParameter.withFieldsSetTo, when the method is not called, and verify is, will fail by calling assertEquals with an assertion message that clearly describes the issue' ); + } + + /** + * @method expects.method.withParameter.withFieldsSetLike + * @case when the method is not called, and verify is + * @result will fail and describe the parameters + */ + @isTest + private static void expectsWithParameterWithFieldsSetLike_whenTheMethodIsNotCalled_failDescribingTheParams() { + + Amoss_Instance assertsDoubleController = new Amoss_Instance( Amoss_Asserts.class ); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + assertsDoubleController + .allows() + .method( 'assertEquals' ); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodUnderDouble' ) + .withParameter().withFieldsSetLike( new Contact( FirstName = 'expectedName' ) ); + + Test.startTest(); + classToDoubleController.verify(); + Test.stopTest(); + + String expectedAssertion = 'Expected call stack for ' + CLASS_TO_DOUBLE + ' should be empty, and it is not'; + String expectedCallStackEntry = 'methodUnderDouble( SObject with fields set like Contact:{FirstName=expectedName} )'; + + System.assertEquals( new List() , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 0 ), 'expects.method.withParameter.withFieldsSetLike, when the method is not called, and verify is, will fail by calling assertEquals with an expected empty call stack' ); + System.assertEquals( new List{ expectedCallStackEntry }, assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 1 ), 'expects.method.withParameter.withFieldsSetLike, when the method is not called, and verify is, will fail by calling assertEquals with a call stack description that describes the outstanding method calls' ); + System.assertEquals( expectedAssertion , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 2 ), 'expects.method.withParameter.withFieldsSetLike, when the method is not called, and verify is, will fail by calling assertEquals with an assertion message that clearly describes the issue' ); + } + + /** + * @method expects.method.withParameter.withAllElements + * @case when the method is not called, and verify is + * @result will fail and describe the parameters + */ + @isTest + private static void expectsWithParameterWithAllElements_whenTheMethodIsNotCalled_failDescribingTheParams() { + + Amoss_Instance assertsDoubleController = new Amoss_Instance( Amoss_Asserts.class ); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + assertsDoubleController + .allows() + .method( 'assertEquals' ); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodUnderDouble' ) + .withParameter().withAllElements().withFieldsSetLike( new Contact( FirstName = 'expectedName' ) ); + + Test.startTest(); + classToDoubleController.verify(); + Test.stopTest(); + + String expectedAssertion = 'Expected call stack for ' + CLASS_TO_DOUBLE + ' should be empty, and it is not'; + String expectedCallStackEntry = 'methodUnderDouble( All => SObject with fields set like Contact:{FirstName=expectedName} )'; + + System.assertEquals( new List() , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 0 ), 'expects.method.withParameter.withAllElements, when the method is not called, and verify is, will fail by calling assertEquals with an expected empty call stack' ); + System.assertEquals( new List{ expectedCallStackEntry }, assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 1 ), 'expects.method.withParameter.withAllElements, when the method is not called, and verify is, will fail by calling assertEquals with a call stack description that describes the outstanding method calls' ); + System.assertEquals( expectedAssertion , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 2 ), 'expects.method.withParameter.withAllElements, when the method is not called, and verify is, will fail by calling assertEquals with an assertion message that clearly describes the issue' ); + } + + /** + * @method expects.method.withParameter.withAnyElement + * @case when the method is not called, and verify is + * @result will fail and describe the parameters + */ + @isTest + private static void expectsWithParameterWithAnyElement_whenTheMethodIsNotCalled_failDescribingTheParams() { + + Amoss_Instance assertsDoubleController = new Amoss_Instance( Amoss_Asserts.class ); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + assertsDoubleController + .allows() + .method( 'assertEquals' ); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodUnderDouble' ) + .withParameter().withAnyElement().withFieldsSetLike( new Contact( FirstName = 'expectedName' ) ); + + Test.startTest(); + classToDoubleController.verify(); + Test.stopTest(); + + String expectedAssertion = 'Expected call stack for ' + CLASS_TO_DOUBLE + ' should be empty, and it is not'; + String expectedCallStackEntry = 'methodUnderDouble( At least one => SObject with fields set like Contact:{FirstName=expectedName} )'; + + System.assertEquals( new List() , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 0 ), 'expects.method.withParameter.withAnyElement, when the method is not called, and verify is, will fail by calling assertEquals with an expected empty call stack' ); + System.assertEquals( new List{ expectedCallStackEntry }, assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 1 ), 'expects.method.withParameter.withAnyElement, when the method is not called, and verify is, will fail by calling assertEquals with a call stack description that describes the outstanding method calls' ); + System.assertEquals( expectedAssertion , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 2 ), 'expects.method.withParameter.withAnyElement, when the method is not called, and verify is, will fail by calling assertEquals with an assertion message that clearly describes the issue' ); + } + + /** + * @method expects.method.withParameter.withElementAt + * @case when the method is not called, and verify is + * @result will fail and describe the parameters + */ + @isTest + private static void expectsWithParameterWithElementAt_whenTheMethodIsNotCalled_failDescribingTheParams() { + + Amoss_Instance assertsDoubleController = new Amoss_Instance( Amoss_Asserts.class ); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + assertsDoubleController + .allows() + .method( 'assertEquals' ); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodUnderDouble' ) + .withParameter().withElementAt( 1 ).withFieldsSetLike( new Contact( FirstName = 'expectedName' ) ); + + Test.startTest(); + classToDoubleController.verify(); + Test.stopTest(); + + String expectedAssertion = 'Expected call stack for ' + CLASS_TO_DOUBLE + ' should be empty, and it is not'; + String expectedCallStackEntry = 'methodUnderDouble( 1 => SObject with fields set like Contact:{FirstName=expectedName} )'; + + System.assertEquals( new List() , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 0 ), 'expects.method.withParameter.withElementAt, when the method is not called, and verify is, will fail by calling assertEquals with an expected empty call stack' ); + System.assertEquals( new List{ expectedCallStackEntry }, assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 1 ), 'expects.method.withParameter.withElementAt, when the method is not called, and verify is, will fail by calling assertEquals with a call stack description that describes the outstanding method calls' ); + System.assertEquals( expectedAssertion , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 2 ), 'expects.method.withParameter.withElementAt, when the method is not called, and verify is, will fail by calling assertEquals with an assertion message that clearly describes the issue' ); + } + + /** + * @method expects.method.withParameterNamed + * @case when the method is not called, and verify is + * @result will fail and describe the parameters + */ + @isTest + private static void expectsWithParameterNamed_whenTheMethodIsNotCalledAndVerifyIs_failDescribingTheParams() { + + Amoss_Instance assertsDoubleController = new Amoss_Instance( Amoss_Asserts.class ); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + assertsDoubleController + .allows() + .method( 'assertEquals' ); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodUnderDouble' ) + .withParameterNamed( 'parameter1' ).setTo( 'ParamValue1' ) + .andParameterNamed( 'parameter2' ).setTo( 2 ); + + Test.startTest(); + classToDoubleController.verify(); + Test.stopTest(); + + List expectedCallDescriptions = new List{ 'methodUnderDouble( parameter1 => ParamValue1, parameter2 => 2 )' }; + String expectedAssertion = 'Expected call stack for ' + CLASS_TO_DOUBLE + ' should be empty, and it is not'; + + System.assertEquals( new List() , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 0 ), 'expects.method.withParameter, when the method is not called, and verify is, will fail by calling assertEquals with an expected empty call stack' ); + System.assertEquals( expectedCallDescriptions, assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 1 ), 'expects.method.withParameter, when the method is not called, and verify is, will fail by calling assertEquals with a call stack description that describes the outstanding method calls' ); + System.assertEquals( expectedAssertion , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 2 ), 'expects.method.withParameter, when the method is not called, and verify is, will fail by calling assertEquals with an assertion message that clearly describes the issue' ); + } + + /** + * @method expects.method.withParameter.setTo (List) + * @case when the method is not called, and verify is + * @result will fail and describe the parameters + */ + @isTest + private static void expectsWithParameterSetToSet_whenTheMethodIsNotCalledAndVerifyIs_failDescribingTheParams() { + + Amoss_Instance assertsDoubleController = new Amoss_Instance( Amoss_Asserts.class ); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + assertsDoubleController + .allows() + .method( 'assertEquals' ); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodWithSetOfObjects' ) + .withParameter().setTo( new Set{ new Contact( LastName = 'expectedName' ) } ); + + Test.startTest(); + classToDoubleController.verify(); + Test.stopTest(); + + List expectedCallDescriptions = new List{ 'methodWithSetOfObjects( Same instance as {Contact:{LastName=expectedName}} )' }; + String expectedAssertion = 'Expected call stack for ' + CLASS_TO_DOUBLE + ' should be empty, and it is not'; + + System.assertEquals( new List() , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 0 ), 'expects.method.withParameter.setTo (Set), when the method is not called, and verify is, will fail by calling assertEquals with an expected empty call stack' ); + System.assertEquals( expectedCallDescriptions, assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 1 ), 'expects.method.withParameter.setTo (Set), when the method is not called, and verify is, will fail by calling assertEquals with a call stack description that describes the outstanding method calls' ); + System.assertEquals( expectedAssertion , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 2 ), 'expects.method.withParameter.setTo (Set), when the method is not called, and verify is, will fail by calling assertEquals with an assertion message that clearly describes the issue' ); + } + + /** + * @method expects.method.withParameter.setTo (list) + * @case when the method is not called, and verify is + * @result will fail and describe the parameters + */ + @isTest + private static void expectsWithParameterSetToList_whenTheMethodIsNotCalledAndVerifyIs_failDescribingTheParams() { + + Amoss_Instance assertsDoubleController = new Amoss_Instance( Amoss_Asserts.class ); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + assertsDoubleController + .allows() + .method( 'assertEquals' ); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodWithListOfObjects' ) + .withParameter().setTo( new List{ new Contact( LastName = 'expectedName' ) } ); + + Test.startTest(); + classToDoubleController.verify(); + Test.stopTest(); + + List expectedCallDescriptions = new List{ 'methodWithListOfObjects( Same instance as (Contact:{LastName=expectedName}) )' }; + String expectedAssertion = 'Expected call stack for ' + CLASS_TO_DOUBLE + ' should be empty, and it is not'; + + System.assertEquals( new List() , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 0 ), 'expects.method.withParameter.setTo (List), when the method is not called, and verify is, will fail by calling assertEquals with an expected empty call stack' ); + System.assertEquals( expectedCallDescriptions, assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 1 ), 'expects.method.withParameter.setTo (List), when the method is not called, and verify is, will fail by calling assertEquals with a call stack description that describes the outstanding method calls' ); + System.assertEquals( expectedAssertion , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 2 ), 'expects.method.withParameter.setTo (List), when the method is not called, and verify is, will fail by calling assertEquals with an assertion message that clearly describes the issue' ); + } + + /** + * @method expects.method.withParameterNamed.setToTheSameValueAs + * @case when the method is not called, and verify is + * @result will fail and describe the parameters + */ + @isTest + private static void expectsWithParameterNamedSetToTheSameValueAs_whenTheMethodIsNotCalled_failDescribingTheParams() { + + Amoss_Instance assertsDoubleController = new Amoss_Instance( Amoss_Asserts.class ); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + assertsDoubleController + .allows() + .method( 'assertEquals' ); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodUnderDouble' ) + .withParameterNamed( 'parameter1' ).setToTheSameValueAs( 'expectedParameterValue' ); + + Test.startTest(); + classToDoubleController.verify(); + Test.stopTest(); + + String expectedAssertion = 'Expected call stack for ' + CLASS_TO_DOUBLE + ' should be empty, and it is not'; + String expectedCallStackEntry = 'methodUnderDouble( parameter1 => expectedParameterValue )'; + + System.assertEquals( new List() , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 0 ), 'expects.method.withParameterNamed.setToTheSameValueAs, when the method is not called, and verify is, will fail by calling assertEquals with an expected empty call stack' ); + System.assertEquals( new List{ expectedCallStackEntry }, assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 1 ), 'expects.method.withParameterNamed.setToTheSameValueAs, when the method is not called, and verify is, will fail by calling assertEquals with a call stack description that describes the outstanding method calls' ); + System.assertEquals( expectedAssertion , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 2 ), 'expects.method.withParameterNamed.setToTheSameValueAs, when the method is not called, and verify is, will fail by calling assertEquals with an assertion message that clearly describes the issue' ); + } + + /** + * @method expects.method.withParameterNamed.setToTheSameValueAs (object) + * @case when the method is not called, and verify is + * @result will fail and describe the parameters + */ + @isTest + private static void expectsWithParameterNamedSetToTheSameValueAsWithObject_whenTheMethodIsNotCalled_failDescribingTheParams() { + + Amoss_Instance assertsDoubleController = new Amoss_Instance( Amoss_Asserts.class ); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + assertsDoubleController + .allows() + .method( 'assertEquals' ); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodUnderDouble' ) + .withParameterNamed( 'parameter1' ).setToTheSameValueAs( new AmossTest_ClassToDouble( 'expectedObjectParam' ) ) + .andParameterNamed( 'parameter2' ).setToTheSameValueAs( new AmossTest_ClassToDouble( 'expectedObjectParam' ) ); + + Test.startTest(); + classToDoubleController.verify(); + Test.stopTest(); + + String expectedAssertion = 'Expected call stack for ' + CLASS_TO_DOUBLE + ' should be empty, and it is not'; + String expectedCallStackEntry = 'methodUnderDouble( parameter1 => AmossTest_ClassToDouble:[], parameter2 => AmossTest_ClassToDouble:[] )'; + + System.assertEquals( new List() , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 0 ), 'expects.method.withParameterNamed.setToTheSameValueAs, with an object, when the method is not called, and verify is, will fail by calling assertEquals with an expected empty call stack' ); + System.assertEquals( new List{ expectedCallStackEntry }, assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 1 ), 'expects.method.withParameterNamed.setToTheSameValueAs, with an object, when the method is not called, and verify is, will fail by calling assertEquals with a call stack description that describes the outstanding method calls' ); + System.assertEquals( expectedAssertion , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 2 ), 'expects.method.withParameterNamed.setToTheSameValueAs, with an object, when the method is not called, and verify is, will fail by calling assertEquals with an assertion message that clearly describes the issue' ); + } + + /** + * @method expects.method.withParameterNamed.withFieldsSetTo + * @case when the method is not called, and verify is + * @result will fail and describe the parameters + */ + @isTest + private static void expectsWithParameterNamedWithFieldsSetTo_whenTheMethodIsNotCalled_failDescribingTheParams() { + + Amoss_Instance assertsDoubleController = new Amoss_Instance( Amoss_Asserts.class ); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + assertsDoubleController + .allows() + .method( 'assertEquals' ); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodUnderDouble' ) + .withParameterNamed( 'parameter1' ).withFieldsSetTo( new Map{ 'Field' => 'expectedValue' } ) + .andParameterNamed( 'parameter2' ).withFieldsSetTo( new Map{ 'Field2' => 'expectedValue2' }); + + Test.startTest(); + classToDoubleController.verify(); + Test.stopTest(); + + String expectedAssertion = 'Expected call stack for ' + CLASS_TO_DOUBLE + ' should be empty, and it is not'; + String expectedCallStackEntry = 'methodUnderDouble( parameter1 => SObject with at least the same fields set as {Field=expectedValue}, parameter2 => SObject with at least the same fields set as {Field2=expectedValue2} )'; + + System.assertEquals( new List() , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 0 ), 'expects.method.withParameterNamed.withFieldsSetTo, when the method is not called, and verify is, will fail by calling assertEquals with an expected empty call stack' ); + System.assertEquals( new List{ expectedCallStackEntry }, assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 1 ), 'expects.method.withParameterNamed.withFieldsSetTo, when the method is not called, and verify is, will fail by calling assertEquals with a call stack description that describes the outstanding method calls' ); + System.assertEquals( expectedAssertion , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 2 ), 'expects.method.withParameterNamed.withFieldsSetTo, when the method is not called, and verify is, will fail by calling assertEquals with an assertion message that clearly describes the issue' ); + } + + /** + * @method expects.method.withParameterNamed.withFieldsSetLike + * @case when the method is not called, and verify is + * @result will fail and describe the parameters + */ + @isTest + private static void expectsWithParameterNamedWithFieldsSetLike_whenTheMethodIsNotCalled_failDescribingTheParams() { + + Amoss_Instance assertsDoubleController = new Amoss_Instance( Amoss_Asserts.class ); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + assertsDoubleController + .allows() + .method( 'assertEquals' ); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodUnderDouble' ) + .withParameterNamed( 'parameter1' ).withFieldsSetLike( new Contact( FirstName = 'expectedName' ) ); + + Test.startTest(); + classToDoubleController.verify(); + Test.stopTest(); + + String expectedAssertion = 'Expected call stack for ' + CLASS_TO_DOUBLE + ' should be empty, and it is not'; + String expectedCallStackEntry = 'methodUnderDouble( parameter1 => SObject with fields set like Contact:{FirstName=expectedName} )'; + + System.assertEquals( new List() , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 0 ), 'expects.method.withParameterNamed.withFieldsSetLike, when the method is not called, and verify is, will fail by calling assertEquals with an expected empty call stack' ); + System.assertEquals( new List{ expectedCallStackEntry }, assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 1 ), 'expects.method.withParameterNamed.withFieldsSetLike, when the method is not called, and verify is, will fail by calling assertEquals with a call stack description that describes the outstanding method calls' ); + System.assertEquals( expectedAssertion , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 2 ), 'expects.method.withParameterNamed.withFieldsSetLike, when the method is not called, and verify is, will fail by calling assertEquals with an assertion message that clearly describes the issue' ); + } + + /** + * @method expects.method.withParameterNamed.withAllElements + * @case when the method is not called, and verify is + * @result will fail and describe the parameters + */ + @isTest + private static void expectsWithParameterNamedWithAllElements_whenTheMethodIsNotCalled_failDescribingTheParams() { + + Amoss_Instance assertsDoubleController = new Amoss_Instance( Amoss_Asserts.class ); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + assertsDoubleController + .allows() + .method( 'assertEquals' ); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodUnderDouble' ) + .withParameterNamed( 'parameter1' ).withAllElements().withFieldsSetLike( new Contact( FirstName = 'expectedName' ) ); + + Test.startTest(); + classToDoubleController.verify(); + Test.stopTest(); + + String expectedAssertion = 'Expected call stack for ' + CLASS_TO_DOUBLE + ' should be empty, and it is not'; + String expectedCallStackEntry = 'methodUnderDouble( parameter1 => All => SObject with fields set like Contact:{FirstName=expectedName} )'; + + System.assertEquals( new List() , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 0 ), 'expects.method.withParameterNamed.withAllElements, when the method is not called, and verify is, will fail by calling assertEquals with an expected empty call stack' ); + System.assertEquals( new List{ expectedCallStackEntry }, assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 1 ), 'expects.method.withParameterNamed.withAllElements, when the method is not called, and verify is, will fail by calling assertEquals with a call stack description that describes the outstanding method calls' ); + System.assertEquals( expectedAssertion , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 2 ), 'expects.method.withParameterNamed.withAllElements, when the method is not called, and verify is, will fail by calling assertEquals with an assertion message that clearly describes the issue' ); + } + + /** + * @method expects.method.withParameter.withAllElements.withAnyElement.withElementAt + * @case when the method is not called, and verify is + * @result will fail and describe the parameters + */ + @isTest + private static void expectsWithParameterWithElementsCombination_whenTheMethodIsNotCalled_failDescribingTheParams() { + + Amoss_Instance assertsDoubleController = new Amoss_Instance( Amoss_Asserts.class ); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + assertsDoubleController + .allows() + .method( 'assertEquals' ); + + Amoss_Instance verifierToDoubleController = new Amoss_Instance( Amoss_ValueVerifier.class ); + verifierToDoubleController + .expects() + .method( 'toString' ) + .returning( 'VerifiedBy' ); + Amoss_ValueVerifier customVerifier = (Amoss_ValueVerifier)verifierToDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodUnderDouble' ) + .withParameter() + .withAnyElement().setTo( 'anyElementSetTo1' ) + .withAnyElement().setTo( 'anyElementSetTo2' ) + .withAllElements().withFieldsSetLike( new Contact( FirstName = 'allSet' ) ) + .withAllElements().withFieldsSetTo( new Map{ 'LastName' => 'allSet' } ) + .withElementAt( 0 ).setToTheSameValueAs( 'setToValue' ) + .withElementAt( 1 ).verifiedBy( customVerifier ); + + Test.startTest(); + classToDoubleController.verify(); + Test.stopTest(); + + String expectedAssertion = 'Expected call stack for ' + CLASS_TO_DOUBLE + ' should be empty, and it is not'; + String expectedCallStackEntry = 'methodUnderDouble( Should match all of [ At least one => anyElementSetTo1, At least one => anyElementSetTo2, All => SObject with fields set like Contact:{FirstName=allSet}, All => SObject with at least the same fields set as {LastName=allSet}, 0 => setToValue, 1 => VerifiedBy ] )'; + + System.assertEquals( new List() , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 0 ), 'expects.method.withParameter.withAllElements.withAnyElement.withElementAt, when the method is not called, and verify is, will fail by calling assertEquals with an expected empty call stack' ); + System.assertEquals( new List{ expectedCallStackEntry }, assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 1 ), 'expects.method.withParameter.withAllElements.withAnyElement.withElementAt, when the method is not called, and verify is, will fail by calling assertEquals with a call stack description that describes the outstanding method calls' ); + System.assertEquals( expectedAssertion , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 2 ), 'expects.method.withParameter.withAllElements.withAnyElement.withElementAt, when the method is not called, and verify is, will fail by calling assertEquals with an assertion message that clearly describes the issue' ); + } + + /** + * @method expects.method.withParameterNamed.withAllElements.withAnyElement.withElementAt + * @case when the method is not called, and verify is + * @result will fail and describe the parameters + */ + @isTest + private static void expectsWithParameterNamedWithElementsCombination_whenTheMethodIsNotCalled_failDescribingTheParams() { + + Amoss_Instance assertsDoubleController = new Amoss_Instance( Amoss_Asserts.class ); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + assertsDoubleController + .allows() + .method( 'assertEquals' ); + + Amoss_Instance verifierToDoubleController = new Amoss_Instance( Amoss_ValueVerifier.class ); + verifierToDoubleController + .expects() + .method( 'toString' ) + .returning( 'VerifiedBy' ); + Amoss_ValueVerifier customVerifier = (Amoss_ValueVerifier)verifierToDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodUnderDouble' ) + .withParameterNamed( 'parameter1' ) + .withAnyElement().setTo( 'anyElementSetTo1' ) + .withAnyElement().setTo( 'anyElementSetTo2' ) + .withAllElements().withFieldsSetLike( new Contact( FirstName = 'allSet' ) ) + .withAllElements().withFieldsSetTo( new Map{ 'LastName' => 'allSet' } ) + .withElementAt( 0 ).setToTheSameValueAs( 'setToValue' ) + .withElementAt( 1 ).verifiedBy( customVerifier ); + + Test.startTest(); + classToDoubleController.verify(); + Test.stopTest(); + + String expectedAssertion = 'Expected call stack for ' + CLASS_TO_DOUBLE + ' should be empty, and it is not'; + String expectedCallStackEntry = 'methodUnderDouble( parameter1 => Should match all of [ At least one => anyElementSetTo1, At least one => anyElementSetTo2, All => SObject with fields set like Contact:{FirstName=allSet}, All => SObject with at least the same fields set as {LastName=allSet}, 0 => setToValue, 1 => VerifiedBy ] )'; + + System.assertEquals( new List() , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 0 ), 'expects.method.withParameter.withAllElements.withAnyElement.withElementAt, when the method is not called, and verify is, will fail by calling assertEquals with an expected empty call stack' ); + System.assertEquals( new List{ expectedCallStackEntry }, assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 1 ), 'expects.method.withParameter.withAllElements.withAnyElement.withElementAt, when the method is not called, and verify is, will fail by calling assertEquals with a call stack description that describes the outstanding method calls' ); + System.assertEquals( expectedAssertion , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 2 ), 'expects.method.withParameter.withAllElements.withAnyElement.withElementAt, when the method is not called, and verify is, will fail by calling assertEquals with an assertion message that clearly describes the issue' ); + } + + /** + * @method expects.method.withParameterNamed.withAnyElement + * @case when the method is not called, and verify is + * @result will fail and describe the parameters + */ + @isTest + private static void expectsWithParameterNamedWithAnyElement_whenTheMethodIsNotCalled_failDescribingTheParams() { + + Amoss_Instance assertsDoubleController = new Amoss_Instance( Amoss_Asserts.class ); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + assertsDoubleController + .allows() + .method( 'assertEquals' ); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodUnderDouble' ) + .withParameterNamed( 'parameter1' ).withAnyElement().withFieldsSetLike( new Contact( FirstName = 'expectedName' ) ); + + Test.startTest(); + classToDoubleController.verify(); + Test.stopTest(); + + String expectedAssertion = 'Expected call stack for ' + CLASS_TO_DOUBLE + ' should be empty, and it is not'; + String expectedCallStackEntry = 'methodUnderDouble( parameter1 => At least one => SObject with fields set like Contact:{FirstName=expectedName} )'; + + System.assertEquals( new List() , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 0 ), 'expects.method.withParameterNamed.withAnyElement, when the method is not called, and verify is, will fail by calling assertEquals with an expected empty call stack' ); + System.assertEquals( new List{ expectedCallStackEntry }, assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 1 ), 'expects.method.withParameterNamed.withAnyElement, when the method is not called, and verify is, will fail by calling assertEquals with a call stack description that describes the outstanding method calls' ); + System.assertEquals( expectedAssertion , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 2 ), 'expects.method.withParameterNamed.withAnyElement, when the method is not called, and verify is, will fail by calling assertEquals with an assertion message that clearly describes the issue' ); + } + + /** + * @method expects.method.withParameterNamed.withElementAt + * @case when the method is not called, and verify is + * @result will fail and describe the parameters + */ + @isTest + private static void expectsWithParameterNamedWithElementAt_whenTheMethodIsNotCalled_failDescribingTheParams() { + + Amoss_Instance assertsDoubleController = new Amoss_Instance( Amoss_Asserts.class ); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + assertsDoubleController + .allows() + .method( 'assertEquals' ); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodUnderDouble' ) + .withParameterNamed( 'parameter1' ).withElementAt( 1 ).withFieldsSetLike( new Contact( FirstName = 'expectedName' ) ); + + Test.startTest(); + classToDoubleController.verify(); + Test.stopTest(); + + String expectedAssertion = 'Expected call stack for ' + CLASS_TO_DOUBLE + ' should be empty, and it is not'; + String expectedCallStackEntry = 'methodUnderDouble( parameter1 => 1 => SObject with fields set like Contact:{FirstName=expectedName} )'; + + System.assertEquals( new List() , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 0 ), 'expects.method.withParameterNamed.withElementAt, when the method is not called, and verify is, will fail by calling assertEquals with an expected empty call stack' ); + System.assertEquals( new List{ expectedCallStackEntry }, assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 1 ), 'expects.method.withParameterNamed.withElementAt, when the method is not called, and verify is, will fail by calling assertEquals with a call stack description that describes the outstanding method calls' ); + System.assertEquals( expectedAssertion , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 2 ), 'expects.method.withParameterNamed.withElementAt, when the method is not called, and verify is, will fail by calling assertEquals with an assertion message that clearly describes the issue' ); + } + + //HERE + + /** + * @method expects.method.returning + * @case when the method is called + * @result will not fail, and will return the stated result + */ + @isTest + private static void expectsMethodReturning_whenCalled_passAndWillReturnTheStatedResult() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodUnderDouble' ) + .returning( 'TheReturn' ); + + Test.startTest(); + String actualReturn = classToDouble.methodUnderDouble( 'this', 1 ); + Test.stopTest(); + + System.assertEquals( 'TheReturn', actualReturn, 'expects.method, when the method is called, will not fail, and will return the stated result' ); + } + + /** + * @method expects.method.returning + * @case when the method is called more than once + * @result will fail the test + */ + @isTest + private static void expectsMethodReturning_whenCalledMoreThanOnce_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodUnderDouble' ) + .returning( 'TheReturn' ); + + Test.startTest(); + classToDouble.methodUnderDouble( 'this', 1 ); + + try { + classToDouble.methodUnderDouble( 'this', 1 ); + } catch ( TestException e ) {} + Test.stopTest(); + + String expectedAssertion = CLASS_TO_DOUBLE + '.methodUnderDouble was called more times than was expected'; + System.assertEquals( false, assertsDoubleController.get().latestCallOf( 'assert' ).parameter( 0 ), 'expects.method.returning, when the method is not called more times than expected, will fail by calling assert with false' ); + Amoss_Asserts.assertContains( expectedAssertion, assertsDoubleController.get().latestCallOf( 'assert' ).parameter( 1 ), 'expects.method.returning, when the method is not called more times than expected, will fail by calling assert with an assertion message that clearly describes the issue' ); + } + + /** + * @method expects.method.returning + * @case when the method is not called, but neither is verify + * @result will not fail + */ + @isTest + private static void expectsMethodReturning_whenTheMethodIsNotCalledButNeitherIsVerify_pass() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodUnderDouble' ) + .returning( 'TheReturn' ); + + Test.startTest(); + // we do nothing, because we're testing what happens when the method is not called + Test.stopTest(); + + System.assertEquals( 0, classToDoubleController.countOf( 'methodUnderDouble' ), 'expects.method.returning, when the method is not called, and neither is verify, will not fail - and the call count will be zero' ); + } + + /** + * @method expects.method.returning + * @case when the method is not called, and verify is + * @result will fail + */ + @isTest + private static void expectsMethodReturning_whenTheMethodIsNotCalledAndVerifyIs_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodUnderDouble' ) + .returning( 'TheReturn' ); + + Test.startTest(); + try { + classToDoubleController.verify(); + } catch ( TestException e ) { + // this is how the mock asserts object halts the test at the failed assertion... + } + Test.stopTest(); + + String expectedAssertion = 'Expected call stack for ' + CLASS_TO_DOUBLE + ' should be empty, and it is not'; + + System.assertEquals( new List() , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 0 ), 'expects.method.returning, when the method is not called, and verify is, will fail by calling assertEquals with an expected empty call stack' ); + System.assertEquals( new List{ 'methodUnderDouble( any )' }, assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 1 ), 'expects.method.returning, when the method is not called, and verify is, will fail by calling assertEquals with a call stack description that describes the outstanding method calls' ); + System.assertEquals( expectedAssertion , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 2 ), 'expects.method.returning, when the method is not called, and verify is, will fail by calling assertEquals with an assertion message that clearly describes the issue' ); + } + + /** + * @method expects.method.withAnyParameters.returning + * @case when the method is called + * @result will not fail, and will return the stated result + */ + @isTest + private static void expectsWithAnyParametersReturning_whenCalled_passAndWillReturnTheStatedResult() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodUnderDouble' ) + .withAnyParameters() + .returning( 'TheReturn' ); + + Test.startTest(); + String actualReturn = classToDouble.methodUnderDouble( 'this', 1 ); + Test.stopTest(); + + System.assertEquals( 'TheReturn', actualReturn, 'expects.method.withAnyParameters.returning, when the method is called, will not fail, and will return the stated result' ); + } + + /** + * @method expects.method.withAnyParameters.returning + * @case when the method is called more than once + * @result will fail the test + */ + @isTest + private static void expectsWithAnyParametersReturning_whenCalledMoreThanOnce_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodUnderDouble' ) + .withAnyParameters() + .returning( 'TheReturn' ); + + Test.startTest(); + classToDouble.methodUnderDouble( 'this', 1 ); + + try { + classToDouble.methodUnderDouble( 'thisNew', 2 ); + } catch ( TestException e ) {} + Test.stopTest(); + + String expectedAssertion = CLASS_TO_DOUBLE + '.methodUnderDouble was called more times than was expected'; + System.assertEquals( false , assertsDoubleController.get().latestCallOf( 'assert' ).parameter( 0 ), 'expects.method.withAnyParameters.returning, when the method is not called more times than expected, will fail by calling assert with false' ); + Amoss_Asserts.assertContains( expectedAssertion, assertsDoubleController.get().latestCallOf( 'assert' ).parameter( 1 ), 'expects.method.withAnyParameters.returning, when the method is not called more times than expected, will fail by calling assert with an assertion message that clearly describes the issue' ); + } + + /** + * @method expects.method.withAnyParameters.returning + * @case when the method is not called, but neither is verify + * @result will not fail + */ + @isTest + private static void expectsWithAnyParametersReturning_whenTheMethodIsNotCalledButNeitherIsVerify_pass() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodUnderDouble' ) + .withAnyParameters() + .returning( 'TheReturn' ); + + Test.startTest(); + // we do nothing, because we're testing what happens when the method is not called + Test.stopTest(); + + System.assertEquals( 0, classToDoubleController.countOf( 'methodUnderDouble' ), 'expects.method.withAnyParameters.returning, when the method is not called, and neither is verify, will not fail - and the call count will be zero' ); + } + + /** + * @method expects.method.withAnyParameters.returning + * @case when the method is not called, and verify is + * @result will fail + */ + @isTest + private static void expectsWithAnyParametersReturning_whenTheMethodIsNotCalledAndVerifyIs_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodUnderDouble' ) + .withAnyParameters() + .returning( 'TheReturn' ); + + Test.startTest(); + try { + classToDoubleController.verify(); + } catch ( TestException e ) { + // this is how the mock asserts object halts the test at the failed assertion... + } + Test.stopTest(); + + String expectedAssertion = 'Expected call stack for ' + CLASS_TO_DOUBLE + ' should be empty, and it is not'; + + System.assertEquals( new List() , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 0 ), 'expects.method.withAnyParameters.returning, when the method is not called, and verify is, will fail by calling assertEquals with an expected empty call stack' ); + System.assertEquals( new List{ 'methodUnderDouble( any )' }, assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 1 ), 'expects.method.withAnyParameters.returning, when the method is not called, and verify is, will fail by calling assertEquals with a call stack description that describes the outstanding method calls' ); + System.assertEquals( expectedAssertion , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 2 ), 'expects.method.withAnyParameters.returning, when the method is not called, and verify is, will fail by calling assertEquals with an assertion message that clearly describes the issue' ); + } + + /** + * @method expects.method.verifiedBy.returning.verify + * @case when the specified method is not called + * @result will fail the test using the toString to form the description + */ + @isTest + private static void expectsMethodVerifiedByReturning_whenTheMethodIsNotCalledAndVerifyIs_failAndUseToString() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Amoss_Instance verifierToDoubleController = new Amoss_Instance( Amoss_ValueVerifier.class ); + verifierToDoubleController + .expects() + .method( 'toString' ) + .returning( 'The custom description for parameter 1' ) + .then().expects() + .method( 'toString' ) + .returning( 'The custom description for parameter 2' ); // since we use the verifier for two parameters, we expect 2 calls + + Amoss_ValueVerifier customVerifierFails = (Amoss_ValueVerifier)verifierToDoubleController.getDouble(); + + String expectedReturn = 'theReturn'; + classToDoubleController + .expects() + .method( 'methodUnderDouble' ) + .withParameter().verifiedBy( customVerifierFails ) + .thenParameter().verifiedBy( customVerifierFails ) + .returning( expectedReturn ); + + Test.startTest(); + try { + classToDoubleController.verify(); + } catch ( TestException e ) {} + Test.stopTest(); + + String expectedAssertion = 'Expected call stack for ' + CLASS_TO_DOUBLE + ' should be empty, and it is not'; + String expectedCall = 'methodUnderDouble( The custom description for parameter 1, The custom description for parameter 2 )'; + + System.assertEquals( new List() , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 0 ), 'expects.method.verifiedBy.returning.verify, when the method is not called, and verify is, will fail by calling assertEquals with an expected empty call stack' ); + System.assertEquals( new List{ expectedCall }, assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 1 ), 'expects.method.verifiedBy.returning.verify, when the method is not called, and verify is, will fail by calling assertEquals with a call stack description that contains the return from the custom verifier toString method' ); + System.assertEquals( expectedAssertion , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 2 ), 'expects.method.verifiedBy.returning.verify, when the method is not called, and verify is, will fail by calling assertEquals with an assertion message that clearly describes the issue' ); + } + + /** + * @method expects.method.withParameterNamed.verifiedBy.returning.verify + * @case when the specified method is not called + * @result will fail the test using the toString to form the description + */ + @isTest + private static void expectsWithParameterNamedVerifiedByReturning_whenTheMethodIsNotCalledAndVerifyIs_failAndUseToString() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Amoss_Instance verifierToDoubleController = new Amoss_Instance( Amoss_ValueVerifier.class ); + verifierToDoubleController + .expects() + .method( 'toString' ) + .returning( 'The custom description for parameter 1' ) + .then().expects() + .method( 'toString' ) + .returning( 'The custom description for parameter 2' ); // since we use the verifier for two parameters, we expect 2 calls + + Amoss_ValueVerifier customVerifierFails = (Amoss_ValueVerifier)verifierToDoubleController.getDouble(); + + String expectedReturn = 'theReturn'; + classToDoubleController + .expects() + .method( 'methodUnderDouble' ) + .withParameterNamed( 'parameter1' ).verifiedBy( customVerifierFails ) + .andParameterNamed( 'parameter2' ).verifiedBy( customVerifierFails ) + .returning( expectedReturn ); + + Test.startTest(); + try { + classToDoubleController.verify(); + } catch ( TestException e ) {} + Test.stopTest(); + + String expectedAssertion = 'Expected call stack for ' + CLASS_TO_DOUBLE + ' should be empty, and it is not'; + String expectedCall = 'methodUnderDouble( parameter1 => The custom description for parameter 1, parameter2 => The custom description for parameter 2 )'; + + System.assertEquals( new List() , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 0 ), 'expects.method.verifiedBy.returning.verify, when the method is not called, and verify is, will fail by calling assertEquals with an expected empty call stack' ); + System.assertEquals( new List{ expectedCall }, assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 1 ), 'expects.method.verifiedBy.returning.verify, when the method is not called, and verify is, will fail by calling assertEquals with a call stack description that contains the return from the custom verifier toString method' ); + System.assertEquals( expectedAssertion , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 2 ), 'expects.method.verifiedBy.returning.verify, when the method is not called, and verify is, will fail by calling assertEquals with an assertion message that clearly describes the issue' ); + } + + + /** + * @method expects.method.withParameter.noVerifier.verify + * @case always + * @result will fail the test + */ + @isTest + private static void expectsWithParameterVerifiedNoVerifierVerify_alwaysFail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + String expectedReturn = 'theReturn'; + classToDoubleController + .expects() + .method( 'methodUnderDouble' ) + .withParameterNamed( 'parameter1' ); + + Test.startTest(); + try { + classToDoubleController.verify(); + } catch ( TestException e ) {} + Test.stopTest(); + + String expectedAssertion = 'Expected call stack for ' + CLASS_TO_DOUBLE + ' should be empty, and it is not'; + String expectedCall = 'methodUnderDouble( parameter1 => ERROR: No specification of the parameter shape was made (e.g. setTo) )'; + + System.assertEquals( new List() , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 0 ), 'expects.method.withParameterNamed.noVerifier.verify, when verify is called, will fail by calling assertEquals with an expected empty call stack' ); + System.assertEquals( new List{ expectedCall }, assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 1 ), 'expects.method.withParameterNamed.noVerifier.verify, when verify is called, will fail by calling assertEquals with a call stack description that contains a message stating the verifier was not set up' ); + System.assertEquals( expectedAssertion , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 2 ), 'expects.method.withParameterNamed.noVerifier.verify, when verify is called, will fail by calling assertEquals with an assertion message that clearly describes the issue' ); + } + + /** + * @method expects.method.withParameter.thenParameter.returning + * @case when the method is called with matching parameters + * @result will not fail, and will return the stated result + */ + @isTest + private static void expectsWithParameterAndThenParameterReturning_whenCalled_passAndWillReturnTheStatedResult() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodUnderDouble' ) + .withParameter( 'this' ) + .thenParameter( 1 ) + .returning( 'TheReturn' ); + + Test.startTest(); + String actualReturn = classToDouble.methodUnderDouble( 'this', 1 ); + Test.stopTest(); + + System.assertEquals( 'TheReturn', actualReturn, 'expects.method.withParameter.thenParameter.returning, when the method is called with matching parameters, will not fail, and will return the stated result' ); + } + + /** + * @method expects.method.withParameter.thenParameter.returning + * @case when the method is called with non-matching parameters + * @result will fail the test + */ + @isTest + private static void expectsWithParameterAndThenParameterReturning_whenCallWithNonMatchingParams_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .expects() + .method( 'methodUnderDouble' ) + .withParameter( 'this' ) + .thenParameter( 1 ) + .returning( 'TheReturn' ); + + Test.startTest(); + try { + classToDouble.methodUnderDouble( 'not-this', 1 ); + } catch ( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterInPositionAssertion( assertsDoubleController, CLASS_TO_DOUBLE + '.methodUnderDouble', 0, 'this', 'not-this', 'expects.method.withParameter.thenParameter.returning, when the method is called with non-matching parameters' ); + } + + // + // Allow any call tests + // + + /** + * @method allowsAnyCall + * @case when given false, no methods are defined, and a method is called + * @result will make the test fail + */ + @isTest + private static void allowsAnyCall_whenGivenFalseAndNoMethodsAreDefinedAndAMethodIsCalled_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Test.startTest(); + classToDoubleController + .allowsAnyCall( false ); + + try { + classToDouble.methodUnderDouble( 'OtherActualParam1', 1 ); + } catch ( TestException e ) {} + Test.stopTest(); + + String expectedAssertion = CLASS_TO_DOUBLE + '.methodUnderDouble was called more times than was expected'; + System.assertEquals( false, assertsDoubleController.get().latestCallOf( 'assert' ).parameter( 0 ), 'allowsAnyCall, when given false, no methods are defined and a method is called, will fail by calling assert with false' ); + Amoss_Asserts.assertContains( expectedAssertion, assertsDoubleController.get().latestCallOf( 'assert' ).parameter( 1 ), 'allowsAnyCall, when given false, no methods are defined and a method is called, will fail by calling assert with an assertion message that clearly describes the issue' ); + } + + /** + * @method allowsAnyCall + * @case when given true, no methods are defined, and a method is called + * @result will return null + */ + @isTest + private static void allowsAnyCall_whenGivenTrueAndAndNoMethodsAreDefinedAndAMethodIsCalled_returnNull() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Test.startTest(); + classToDoubleController + .allowsAnyCall( true ); + String returnFromDouble = classToDouble.methodUnderDouble( 'OtherActualParam1', 1 ); + Test.stopTest(); + + System.assertEquals( null, returnFromDouble, 'when given true, when no methods are defined, and a method is called, will return null' ); + } + + /** + * @method allowsAnyCall.true.expects.method.returning + * @case when the method is called more than once + * @result will not fail the test + */ + @isTest + private static void allowsAnyCall_true_expectsMethodReturning_whenCalledMoreThanOnce_pass() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .allowsAnyCall( true ) + .expects() + .method( 'methodUnderDouble' ) + .returning( 'TheReturn' ); + + Test.startTest(); + classToDouble.methodUnderDouble( 'this', 1 ); + classToDouble.methodUnderDouble( 'this', 1 ); + Test.stopTest(); + + classToDoubleController.verify(); + + System.assertEquals( new List(), assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 0 ), 'expects.method, when allowsAnyCall is true, and the expected method is called too many times, and verify is called, will call assertEquals with an empty expected remaining call stack' ); + System.assertEquals( new List(), assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 1 ), 'expects.method, when allowsAnyCall is true, and the expected method is called too many times, and verify is called, will call assertEquals with an empty actual remaining call stack' ); + } + + /** + * @method allowsAnyCall.false.expects.method.returning + * @case when the method is called more than once + * @result will fail the test + */ + @isTest + private static void allowsAnyCall_false_expectsMethodReturning_whenCalledMoreThanOnce_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .allowsAnyCall( false ) + .expects() + .method( 'methodUnderDouble' ) + .returning( 'TheReturn' ); + + Test.startTest(); + classToDouble.methodUnderDouble( 'this', 1 ); + + try { + classToDouble.methodUnderDouble( 'this', 1 ); + } catch ( TestException e ) {} + Test.stopTest(); + + String expectedAssertion = CLASS_TO_DOUBLE + '.methodUnderDouble was called more times than was expected'; + System.assertEquals( false, assertsDoubleController.get().latestCallOf( 'assert' ).parameter( 0 ), 'expects.method, when allowsAnyCall is called with false, and the method is called more times than expected, will fail by calling assert with false' ); + Amoss_Asserts.assertContains( expectedAssertion, assertsDoubleController.get().latestCallOf( 'assert' ).parameter( 1 ), 'expects.method, when allowsAnyCall is called with false, and the method is called more times than expected, will fail by calling assert with an assertion message that clearly describes the issue' ); + } + + /** + * @method allowsAnyCall.true.expects.method.returning + * @case when a different method is called + * @result will not fail the test + */ + @isTest + private static void allowsAnyCall_true_expectsMethodReturning_whenADiffMethodIsCalled_pass() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .allowsAnyCall( true ) + .expects() + .method( 'methodUnderDouble' ) + .returning( 'TheReturn' ); + + Test.startTest(); + classToDouble.methodUnderDouble( 'this', 1 ); + classToDouble.otherMethodUnderDouble( 'this', 1 ); + Test.stopTest(); + + classToDoubleController.verify(); + + System.assertEquals( new List(), assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 0 ), 'expects.method, when allowsAnyCall is true, and a method that is not expected is called, and verify is called, will call assertEquals with an empty expected remaining call stack' ); + System.assertEquals( new List(), assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 1 ), 'expects.method, when allowsAnyCall is true, and a method that is not expected is called, and verify is called, will call assertEquals with an empty actual remaining call stack' ); + } + + /** + * @method allowsAnyCall.true.expects.method.returning (shortcut) + * @case when a different method is called + * @result will not fail the test + */ + @isTest + private static void allowsAnyCallhortcut_true_expectsMethodReturning_whenADiffMethodIsCalled_pass() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .allowsAnyCall() + .expects() + .method( 'methodUnderDouble' ) + .returning( 'TheReturn' ); + + Test.startTest(); + classToDouble.methodUnderDouble( 'this', 1 ); + classToDouble.otherMethodUnderDouble( 'this', 1 ); + Test.stopTest(); + + classToDoubleController.verify(); + + System.assertEquals( new List(), assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 0 ), 'expects.method, when allowsAnyCall, and a method that is not expected is called, and verify is called, will call assertEquals with an empty expected remaining call stack' ); + System.assertEquals( new List(), assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 1 ), 'expects.method, when allowsAnyCall, and a method that is not expected is called, and verify is called, will call assertEquals with an empty actual remaining call stack' ); + } + + /** + * @method allowsAnyCall.false.expects.method.returning + * @case when a different method is called + * @result will fail the test + */ + @isTest + private static void allowsAnyCall_false_expectsMethodReturning_whenADiffMethodIsCalled_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .allowsAnyCall( false ) + .expects() + .method( 'methodUnderDouble' ) + .returning( 'TheReturn' ); + + Test.startTest(); + classToDouble.methodUnderDouble( 'this', 1 ); + + try { + classToDouble.otherMethodUnderDouble( 'this', 1 ); + } catch ( TestException e ) {} + Test.stopTest(); + + String expectedAssertion = CLASS_TO_DOUBLE + '.otherMethodUnderDouble was called more times than was expected'; + System.assertEquals( false, assertsDoubleController.get().latestCallOf( 'assert' ).parameter( 0 ), 'expects.method, when allowsAnyCall is called with false, and a method that is not expected is called, will fail by calling assert with false' ); + Amoss_Asserts.assertContains( expectedAssertion, assertsDoubleController.get().latestCallOf( 'assert' ).parameter( 1 ), 'expects.method, when allowsAnyCall is called with false, and a method that is not expected is called, will fail by calling assert with an assertion message that clearly describes the issue' ); + } + + /** + * @method allowsAnyCall.true.expects.method.returning + * @case when the method is called again with different parameters + * @result will not fail the test + */ + @isTest + private static void allowsAnyCall_true_expectsMethodReturning_whenCallWithDiffParams_pass() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .allowsAnyCall() + .expects() + .method( 'methodUnderDouble' ) + .withParameter( 'this' ) + .thenParameter( 1 ) + .returning( 'TheReturn' ); + + Test.startTest(); + classToDouble.methodUnderDouble( 'this', 1 ); + classToDouble.methodUnderDouble( 'that', 1 ); + Test.stopTest(); + + classToDoubleController.verify(); + + System.assertEquals( new List(), assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 0 ), 'expects.method, when allowsAnyCall is true, and the expected method is called too many times, with different parameters, and verify is called, will call assertEquals with an empty expected remaining call stack' ); + System.assertEquals( new List(), assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 1 ), 'expects.method, when allowsAnyCall is true, and the expected method is called too many times, with different parameters, and verify is called, will call assertEquals with an empty actual remaining call stack' ); + } + + /** + * @method allowsAnyCall.false.expects.method.returning + * @case when the method is called again with different parameters + * @result will fail the test + */ + @isTest + private static void allowsAnyCall_false_expectsMethodReturning_whenCallWithDiffParams_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .allowsAnyCall( false ) + .expects() + .method( 'methodUnderDouble' ) + .withParameter( 'this' ) + .thenParameter( 1 ) + .returning( 'TheReturn' ); + + Test.startTest(); + classToDouble.methodUnderDouble( 'this', 1 ); + + try { + classToDouble.methodUnderDouble( 'that', 1 ); + } catch ( TestException e ) {} + Test.stopTest(); + + String expectedAssertion = CLASS_TO_DOUBLE + '.methodUnderDouble was called more times than was expected'; + System.assertEquals( false, assertsDoubleController.get().latestCallOf( 'assert' ).parameter( 0 ), 'expects.method, when allowsAnyCall is called with false, and the method is called more times with different parameters, will fail by calling assert with false' ); + Amoss_Asserts.assertContains( expectedAssertion, assertsDoubleController.get().latestCallOf( 'assert' ).parameter( 1 ), 'expects.method, when allowsAnyCall is called with false, and the method is called more times with different parameters, will fail by calling assert with an assertion message that clearly describes the issue' ); + } + + /** + * @method allowsAnyCall.true.expects.method.returning.verify + * @case when the method is called only with different parameters + * @result will fail the test + */ + @isTest + private static void allowsAnyCall_true_expectsMethodReturning_whenCalledOnlyWithDiffParams_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .allowsAnyCall( true ) + .expects() + .method( 'methodUnderDouble' ) + .withParameter( 'this' ) + .thenParameter( 1 ) + .returning( 'TheReturn' ); + + Test.startTest(); + classToDouble.methodUnderDouble( 'that', 1 ); + + try { + classToDoubleController.verify(); + } catch ( TestException e ) { + // this is how the mock asserts object halts the test at the failed assertion... + } + Test.stopTest(); + + System.assertEquals( new List() , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 0 ), 'expects.method, when allowsAnyCall is true, and the expected method only once, with different parameters, and verify is called, will call assertEquals with an empty expected remaining call stack' ); + System.assertEquals( new List{ 'methodUnderDouble( this, 1 )' }, assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 1 ), 'expects.method, when allowsAnyCall is true, and the expected method only once, with different parameters, and verify is called, will call assertEquals with a non empty actual remaining call stack - and fail the test' ); + } + + /** + * @method allowsAnyCall.false.expects.method.returning + * @case when the method is only called with different parameters + * @result will fail the test + */ + @isTest + private static void allowsAnyCall_false_expectsMethodReturning_whenTheMethodIsOnlyCalledWithDiffParams_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .allowsAnyCall( false ) + .expects() + .method( 'methodUnderDouble' ) + .withParameter( 'this' ) + .thenParameter( 1 ) + .returning( 'TheReturn' ); + + Test.startTest(); + try { + classToDouble.methodUnderDouble( 'not-this', 1 ); + } catch ( TestException e ) {} + Test.stopTest(); + + checkForWrongParameterInPositionAssertion( assertsDoubleController, CLASS_TO_DOUBLE + '.methodUnderDouble', 0, 'this', 'not-this', 'expects.method.withParameter.thenParameter.returning, and allowsAnyCall with false, when the method is only called with non-matching parameters' ); + } + + /** + * @method allowsAnyCall.true.when.method.willReturn + * @case when a different method is called + * @result will not fail the test + */ + @isTest + private static void allowsAnyCall_true_whenMethodWillReturn_whenADiffMethodIsCalled_pass() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .allowsAnyCall( true ) + .when() + .method( 'methodUnderDouble' ) + .willReturn( 'TheReturn' ); + + Test.startTest(); + classToDouble.otherMethodUnderDouble( 'this', 1 ); + Test.stopTest(); + + classToDoubleController.verify(); + + System.assertEquals( new List(), assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 0 ), 'when.method, when allowsAnyCall is true, and a method that is not set up as a when is called, and verify is called, will call assertEquals with an empty expected remaining call stack' ); + System.assertEquals( new List(), assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 1 ), 'when.method, when allowsAnyCall is true, and a method that is not set up as a when is called, and verify is called, will call assertEquals with an empty actual remaining call stack' ); + } + + /** + * @method allowsAnyCall.false.when.method.willReturn + * @case when a different method is called + * @result will fail the test + */ + @isTest + private static void allowsAnyCall_false_whenMethodWillReturn_whenADiffMethodIsCalled_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .allowsAnyCall( false ) + .when() + .method( 'methodUnderDouble' ) + .willReturn( 'TheReturn' ); + + Test.startTest(); + try { + classToDouble.otherMethodUnderDouble( 'this', 1 ); + } catch ( TestException e ) {} + Test.stopTest(); + + String expectedAssertion = CLASS_TO_DOUBLE + '.otherMethodUnderDouble was called more times than was expected, and no matching "when" or "allows" exists'; + System.assertEquals( false, assertsDoubleController.get().latestCallOf( 'assert' ).parameter( 0 ), 'when.method, when allowsAnyCall is called with false, and a method that is not expected is called, will fail by calling assert with false' ); + Amoss_Asserts.assertContains( expectedAssertion, (String)assertsDoubleController.get().latestCallOf( 'assert' ).parameter( 1 ), 'when.method, when allowsAnyCall is called with false, and a method that is not expected is called, will fail by calling assert with an assertion message that clearly describes the issue' ); + } + + /** + * @method allowsAnyCall.true.when.method.willReturn + * @case when the method is called again with different parameters + * @result will not fail the test + */ + @isTest + private static void allowsAnyCall_true_whenMethodWillReturn_whenCallWithDiffParams_pass() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .allowsAnyCall( true ) + .when() + .method( 'methodUnderDouble' ) + .withParameter( 'this' ) + .thenParameter( 1 ) + .willReturn( 'TheReturn' ); + + Test.startTest(); + classToDouble.methodUnderDouble( 'that', 1 ); + Test.stopTest(); + + classToDoubleController.verify(); + + System.assertEquals( new List(), assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 0 ), 'when.method, when allowsAnyCall is true, and the when method is called with different parameters, and verify is called, will call assertEquals with an empty expected remaining call stack' ); + System.assertEquals( new List(), assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 1 ), 'when.method, when allowsAnyCall is true, and the when method is called with different parameters, and verify is called, will call assertEquals with an empty actual remaining call stack' ); + } + + /** + * @method allowsAnyCall.false.when.method.willReturn + * @case when the method is called again with different parameters + * @result will fail the test + */ + @isTest + private static void allowsAnyCall_false_whenMethodWillReturn_whenCallWithDiffParams_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .allowsAnyCall( false ) + .when() + .method( 'methodUnderDouble' ) + .withParameter( 'this' ) + .thenParameter( 1 ) + .willReturn( 'TheReturn' ); + + Test.startTest(); + try { + classToDouble.methodUnderDouble( 'that', 1 ); + } catch ( TestException e ) {} + Test.stopTest(); + + String expectedAssertion = CLASS_TO_DOUBLE + '.methodUnderDouble was called more times than was expected, and no matching "when" or "allows" exists'; + System.assertEquals( false, assertsDoubleController.get().latestCallOf( 'assert' ).parameter( 0 ), 'when.method, when allowsAnyCall is called with false, and the method is called with different parameters, will fail by calling assert with false' ); + Amoss_Asserts.assertContains( expectedAssertion, (String)assertsDoubleController.get().latestCallOf( 'assert' ).parameter( 1 ), 'when.method, when allowsAnyCall is called with false, and the method is called with different parameters, will fail by calling assert with an assertion message that clearly describes the issue' ); + } + + /** + * @method allowsAnyCall.true.allows.method.returning + * @case when a different method is called + * @result will not fail the test + */ + @isTest + private static void allowsAnyCall_true_allowsMethodReturning_whenADiffMethodIsCalled_pass() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .allowsAnyCall( true ) + .allows() + .method( 'methodUnderDouble' ) + .returning( 'TheReturn' ); + + Test.startTest(); + classToDouble.otherMethodUnderDouble( 'this', 1 ); + Test.stopTest(); + + System.assertEquals( 0, assertsDoubleController.countOf( 'assert' ), 'allows.method, when allowsAnyCall is true, and a method that is not explicitly allowed is called, will not fail the tests (no assert called)' ); + System.assertEquals( 0, assertsDoubleController.countOf( 'assertEquals' ), 'allows.method, when allowsAnyCall is true, and a method that is not explicitly allowed is called, will not fail the tests (no assert equals called)' ); + } + + /** + * @method allowsAnyCall.false.allows.method.returning + * @case when a different method is called + * @result will fail the test + */ + @isTest + private static void allowsAnyCall_false_allowsMethodReturning_whenADiffMethodIsCalled_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .allowsAnyCall( false ) + .allows() + .method( 'methodUnderDouble' ) + .returning( 'TheReturn' ); + + Test.startTest(); + try { + classToDouble.otherMethodUnderDouble( 'this', 1 ); + } catch ( TestException e ) {} + Test.stopTest(); + + String expectedAssertion = CLASS_TO_DOUBLE + '.otherMethodUnderDouble was called more times than was expected, and no matching "when" or "allows" exists'; + System.assertEquals( false, assertsDoubleController.get().latestCallOf( 'assert' ).parameter( 0 ), 'allows.method, when allowsAnyCall is called with false, and a method that is not explictly allowed is called, will fail by calling assert with false' ); + Amoss_Asserts.assertContains( expectedAssertion, (String)assertsDoubleController.get().latestCallOf( 'assert' ).parameter( 1 ), 'allows.method, when allowsAnyCall is called with false, and a method that is not explictly allowed is called, will fail by calling assert with an assertion message that clearly describes the issue' ); + } + + /** + * @method allowsAnyCall.true.allows.method.returning + * @case when the method is called with different parameters + * @result will not fail the test + */ + @isTest + private static void allowsAnyCall_true_allowsMethodReturning_whenCallWithDiffParams_pass() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .allowsAnyCall( true ) + .allows() + .method( 'methodUnderDouble' ) + .withParameter( 'this' ) + .thenParameter( 1 ) + .returning( 'TheReturn' ); + + Test.startTest(); + classToDouble.methodUnderDouble( 'that', 1 ); + Test.stopTest(); + + System.assertEquals( 0, assertsDoubleController.countOf( 'assert' ), 'allows.method, when allowsAnyCall is true, and the allowed method is called with different parameters, will not fail the test (no assert called)' ); + System.assertEquals( 0, assertsDoubleController.countOf( 'assertEquals' ), 'allows.method, when allowsAnyCall is true, and the allowed method is called with different parameters, will not fail the test (no assert equals called)' ); + } + + /** + * @method allowsAnyCall.false.allows.method.returning + * @case when the method is called with different parameters + * @result will fail the test + */ + @isTest + private static void allowsAnyCall_false_allowsMethodReturning_whenCallWithDiffParams_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController.setAsserts( assertsDouble ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + classToDoubleController + .allowsAnyCall( false ) + .allows() + .method( 'methodUnderDouble' ) + .withParameter( 'this' ) + .thenParameter( 1 ) + .returning( 'TheReturn' ); + + Test.startTest(); + try { + classToDouble.methodUnderDouble( 'that', 1 ); + } catch ( TestException e ) {} + Test.stopTest(); + + String expectedAssertion = CLASS_TO_DOUBLE + '.methodUnderDouble was called more times than was expected, and no matching "when" or "allows" exists'; + System.assertEquals( false, assertsDoubleController.get().latestCallOf( 'assert' ).parameter( 0 ), 'allows.method, when allowsAnyCall is called with false, and the method is called with different parameters, will fail by calling assert with false' ); + Amoss_Asserts.assertContains( expectedAssertion, (String)assertsDoubleController.get().latestCallOf( 'assert' ).parameter( 1 ), 'allows.method, when allowsAnyCall is called with false, and the method is called with different parameters, will fail by calling assert with an assertion message that clearly describes the issue' ); + } + + /** + * @method expectsNoCalls + * @case when already configured with an expects + * @result will throw an exception + */ + @isTest + private static void expectsNoCalls_whenAlreadyConfiguredWithExpects_throwAnException() { + + Boolean exceptionThrown = false; + String exceptionMessage; + try { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController + .expects() + .method( 'methodUnderDouble' ) + .also() + .expectsNoCalls(); + + } catch ( Amoss_Instance.Amoss_ExpectsNoCallsAndCallsConfiguredException e ) { + exceptionThrown = true; + exceptionMessage = e.getMessage(); + } + + System.assert( exceptionThrown, 'expectsNoCalls, when already configured with an expects, will throw an exception' ); + Amoss_Asserts.assertContains( 'Cannot state the double expectsNoCalls when expectations or whens have been defined', exceptionMessage, 'expectsNoCalls, when already configured with an expects, will throw an exception with a useful message' ); + } + + /** + * @method expectsNoCalls + * @case when already configured with an allows + * @result will throw an exception + */ + @isTest + private static void expectsNoCalls_whenAlreadyConfiguredWithAllows_throwAnException() { + + Boolean exceptionThrown = false; + String exceptionMessage; + try { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController + .allows() + .method( 'methodUnderDouble' ) + .also() + .expectsNoCalls(); + + } catch ( Amoss_Instance.Amoss_ExpectsNoCallsAndCallsConfiguredException e ) { + exceptionThrown = true; + exceptionMessage = e.getMessage(); + } + + System.assert( exceptionThrown, 'expectsNoCalls, when already configured with an allows, will throw an exception' ); + Amoss_Asserts.assertContains( 'Cannot state the double expectsNoCalls when expectations or whens have been defined', exceptionMessage, 'expectsNoCalls, when already configured with an allows, will throw an exception with a useful message' ); + } + + /** + * @method expectsNoCalls + * @case when already configured with a when + * @result will throw an exception + */ + @isTest + private static void expectsNoCalls_whenAlreadyConfiguredWithWhen_throwAnException() { + + Boolean exceptionThrown = false; + String exceptionMessage; + try { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController + .when() + .method( 'methodUnderDouble' ) + .also() + .expectsNoCalls(); + + } catch ( Amoss_Instance.Amoss_ExpectsNoCallsAndCallsConfiguredException e ) { + exceptionThrown = true; + exceptionMessage = e.getMessage(); + } + + System.assert( exceptionThrown, 'expectsNoCalls, when already configured with a when, will throw an exception' ); + Amoss_Asserts.assertContains( 'Cannot state the double expectsNoCalls when expectations or whens have been defined', exceptionMessage, 'expectsNoCalls, when already configured with a when, will throw an exception with a useful message' ); + } + + /** + * @method expectsNoCalls + * @case when already configured with allowsAnyCall + * @result will throw an exception + */ + @isTest + private static void expectsNoCalls_whenAlreadyConfiguredWithAllowsAnyCall_throwAnException() { + + Boolean exceptionThrown = false; + String exceptionMessage; + try { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController + .allowsAnyCall( true ) + .expectsNoCalls(); + + } catch ( Amoss_Instance.Amoss_ExpectsNoCallsAndCallsConfiguredException e ) { + exceptionThrown = true; + exceptionMessage = e.getMessage(); + } + + System.assert( exceptionThrown, 'expectsNoCalls, when already configured with allows any call, will throw an exception' ); + Amoss_Asserts.assertContains( 'Cannot state the double expectsNoCalls when allowsAnyCall has been set to true', exceptionMessage, 'expectsNoCalls, when already configured with allows any call, will throw an exception with a useful message' ); + } + + /** + * @method expects + * @case when already configured as expectsNoCalls + * @result will throw an exception + */ + @isTest + private static void expects_whenAlreadyConfiguredAsExpectsNoCalls_throwAnException() { + + Boolean exceptionThrown = false; + String exceptionMessage; + try { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController + .expectsNoCalls(); + + classToDoubleController + .expects(); + + } catch ( Amoss_Instance.Amoss_ExpectsNoCallsAndCallsConfiguredException e ) { + exceptionThrown = true; + exceptionMessage = e.getMessage(); + } + + System.assert( exceptionThrown, 'expects, when already configured as expectsNoCalls, will throw an exception' ); + Amoss_Asserts.assertContains( 'Cannot state the double expects a call when it has been stated that it expectsNoCalls', exceptionMessage, 'expects, when already configured as expectsNoCalls, will throw an exception with a useful message' ); + } + + /** + * @method allows + * @case when already configured as expectsNoCalls + * @result will throw an exception + */ + @isTest + private static void allows_whenAlreadyConfiguredAsExpectsNoCalls_throwAnException() { + + Boolean exceptionThrown = false; + String exceptionMessage; + try { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController + .expectsNoCalls(); + + classToDoubleController + .allows(); + + } catch ( Amoss_Instance.Amoss_ExpectsNoCallsAndCallsConfiguredException e ) { + exceptionThrown = true; + exceptionMessage = e.getMessage(); + } + + System.assert( exceptionThrown, 'allows, when already configured as expectsNoCalls, will throw an exception' ); + Amoss_Asserts.assertContains( 'Cannot state the double allows a call when it has been stated that it expectsNoCalls', exceptionMessage, 'allows, when already configured as expectsNoCalls, will throw an exception with a useful message' ); + } + + /** + * @method when + * @case when already configured as expectsNoCalls + * @result will throw an exception + */ + @isTest + private static void when_whenAlreadyConfiguredAsExpectsNoCalls_throwAnException() { + + Boolean exceptionThrown = false; + String exceptionMessage; + try { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController + .expectsNoCalls(); + + classToDoubleController + .when(); + + } catch ( Amoss_Instance.Amoss_ExpectsNoCallsAndCallsConfiguredException e ) { + exceptionThrown = true; + exceptionMessage = e.getMessage(); + } + + System.assert( exceptionThrown, 'when, when already configured as expectsNoCalls, will throw an exception' ); + Amoss_Asserts.assertContains( 'Cannot state the when on a double when it has been stated that it expectsNoCalls', exceptionMessage, 'when, when already configured as expectsNoCalls, will throw an exception with a useful message' ); + } + + /** + * @method allowsAnyCall + * @case when already configured as expectsNoCalls + * @result will throw an exception + */ + @isTest + private static void allowsAnyCall_whenAlreadyConfiguredAsExpectsNoCalls_throwAnException() { + + Boolean exceptionThrown = false; + String exceptionMessage; + try { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController + .expectsNoCalls(); + + classToDoubleController + .allowsAnyCall( true ); + + } catch ( Amoss_Instance.Amoss_ExpectsNoCallsAndCallsConfiguredException e ) { + exceptionThrown = true; + exceptionMessage = e.getMessage(); + } + + System.assert( exceptionThrown, 'allowsAnyCall, when already configured as expectsNoCalls, will throw an exception' ); + Amoss_Asserts.assertContains( 'Cannot state the double allowsAnyCall when it has been stated that it expectsNoCalls', exceptionMessage, 'allowsAnyCall, when already configured as expectsNoCalls, will throw an exception with a useful message' ); + } + + // + // getDouble and generateDouble behaviours + // + /** + * @method getDouble + * @case when called multiple times + * @result will return the same instance + */ + @isTest + private static void getDouble_whenCalledMultipleTimes_returnTheSameInstance() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + + Test.startTest(); + AmossTest_ClassToDouble classToDouble1 = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + AmossTest_ClassToDouble classToDouble2 = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + Test.stopTest(); + + System.assertEquals( classToDouble1, classToDouble2, 'getDouble, when called multiple times, will return the same instance' ); + } + + /** + * @method generateDouble + * @result will return a different instance to getDouble + */ + @isTest + private static void generateDouble_returnADiffInstToGetDouble() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + + Test.startTest(); + AmossTest_ClassToDouble classToDouble1 = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + AmossTest_ClassToDouble classToDouble2 = (AmossTest_ClassToDouble)classToDoubleController.generateDouble(); + Test.stopTest(); + + System.assertNotEquals( classToDouble1, classToDouble2, 'generateDouble, will return a different instance to getDouble' ); + } + + /** + * @method generateDouble + * @case when isFluent has been called + * @result will return a different instance to getDouble, where the double's methods return differentt instances too + */ + @isTest + private static void generateDouble_onAnIsFluent_returnsDiffInstsFromMethods() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController + .isFluent(); + + AmossTest_ClassToDouble classToDouble1 = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + AmossTest_ClassToDouble classToDouble2 = (AmossTest_ClassToDouble)classToDoubleController.generateDouble(); + + Test.startTest(); + AmossTest_ClassToDouble returnFromDouble1 = classToDouble1.fluentMethod(); + AmossTest_ClassToDouble returnFromDouble2 = classToDouble2.fluentMethod(); + Test.stopTest(); + + System.assertEquals( classToDouble1, returnFromDouble1, 'generateDouble, against an isFluent double, will return a double that responds to method calls as per the original - the original will still behave as per normal' ); + System.assertEquals( classToDouble2, returnFromDouble2, 'generateDouble, against an isFluent double, will return a double that responds to method calls as per the original' ); + System.assertNotEquals( returnFromDouble1, returnFromDouble2, 'generateDouble, against an isFluent double, will return a double that responds to method calls with a different instance to the orignal one' ); + } + + /** + * @method when.returns.generateDouble + * @case when the method is called + * @result will return the stated value + */ + @isTest + private static void generateDouble_whenReturnsGenerateDouble_returnSetUpValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController + .when( 'methodUnderDouble' ) + .returns( 'specifiedValue' ); + + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.generateDouble(); + + Test.startTest(); + String returnFromDouble = classToDouble.methodUnderDouble( '1', 2 ); + Test.stopTest(); + + System.assertEquals( 'specifiedValue', returnFromDouble, 'when.returns.generateDouble, when the method is called, will return the stated value' ); + } + + /** + * @method getDouble.generateDouble.countOf + * @case when the methods are called on the different instances + * @result will consolidate the calls into the one controller + */ + @isTest + private static void getDoubleGenerateDoubleGetCount_whenMethodsCalledOnEachInstanceAsSpecified_consolidateThem() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + AmossTest_ClassToDouble classToDouble2 = (AmossTest_ClassToDouble)classToDoubleController.generateDouble(); + + Test.startTest(); + classToDouble.methodUnderDouble( '1', 2 ); + classToDouble2.methodUnderDouble( '1', 2 ); + Test.stopTest(); + + System.assertEquals( 2, classToDoubleController.countOf( 'methodUnderDouble' ), 'getDouble.generateDouble.countOf, when the methods are called on the different instances, will consolidate the calls into the one controller' ); + } + + /** + * @method expects.getDouble.generateDouble.verify + * @case when the methods are called on the different instances + * @result will consolidate the calls into the one controller + */ + @isTest + private static void expectsGetDoubleGenerateDoubleVerify_whenMethodsCalledOnEachInstanceAsSpecified_consolidateThemAndNotFail() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController + .expects( 'methodUnderDouble' ) + .then().expects( 'methodUnderDouble' ); + + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + AmossTest_ClassToDouble classToDouble2 = (AmossTest_ClassToDouble)classToDoubleController.generateDouble(); + + Test.startTest(); + String returnFromDouble = classToDouble.methodUnderDouble( '1', 2 ); + String returnFromDouble2 = classToDouble2.methodUnderDouble( '1', 2 ); + Test.stopTest(); + + classToDoubleController.verify(); // both calls are registered against the same controller, and so this will pass + } + + /** + * @method createClone.when.countOf + * @case when the methods are called on the different instances + * @result will split the calls between the appropriate controllers + */ + @isTest + private static void createCloneWhenCountOf_whenTheMethodsAreCalledOnTheDiffInsts_RouteToTheDiffControllers() { + + Amoss_Instance classToDoubleController1 = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble1 = (AmossTest_ClassToDouble)classToDoubleController1.getDouble(); + + Amoss_Instance classToDoubleController2 = classToDoubleController1.createClone(); + AmossTest_ClassToDouble classToDouble2 = (AmossTest_ClassToDouble)classToDoubleController2.generateDouble(); + + Test.startTest(); + classToDouble1.methodUnderDouble( '1', 1 ); + classToDouble1.methodUnderDouble( '1', 2 ); + classToDouble1.methodUnderDouble( '1', 3 ); + + classToDouble2.methodUnderDouble( '1', 11 ); + classToDouble2.methodUnderDouble( '1', 12 ); + Test.stopTest(); + + System.assertEquals( 3, classToDoubleController1.countOf( 'methodUnderDouble' ), 'createClone.when.countOf, when the methods are called on the different instances, will split the calls between the appropriate controllers - including the call count on the original' ); + System.assertEquals( 2, classToDoubleController2.countOf( 'methodUnderDouble' ), 'createClone.when.countOf, when the methods are called on the different instances, will split the calls between the appropriate controllers - including the call count on the new controller' ); + + System.assertEquals( 1, classToDoubleController1.get().call(0).of( 'methodUnderDouble' ).parameter( 1 ), 'createClone.when.countOf, when the methods are called on the different instances, will split the calls between the appropriate controllers - including the parameter log on the original' ); + System.assertEquals( 12, classToDoubleController2.get().call(1).of( 'methodUnderDouble' ).parameter( 1 ), 'createClone.when.countOf, when the methods are called on the different instances, will split the calls between the appropriate controllers - including the parameter log on the new controller' ); + } + + /** + * @method createClone.expects.verify + * @case when the method is called as expected on the objects + * @result will not fail + */ + @isTest + private static void createCloneExpectsVerify_whenMethodsCalledAsExpected_pass() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController + .expects( 'methodUnderDouble' ); + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.getDouble(); + + Amoss_Instance newClassToDoubleController = classToDoubleController.createClone(); + AmossTest_ClassToDouble newClassToDouble = (AmossTest_ClassToDouble)newClassToDoubleController.getDouble(); + + Test.startTest(); + String returnFromDouble = classToDouble.methodUnderDouble( '1', 2 ); + String returnFromDouble2 = newClassToDouble.methodUnderDouble( '1', 2 ); + Test.stopTest(); + + classToDoubleController.verify(); + newClassToDoubleController.verify(); + } + + /** + * @method createClone + * @case when additional config is added to one of the controllers + * @result will not affect the other + */ + @isTest + private static void createClone_whenAdditionalConfigIsAdded_notAffectTheOther() { + + Amoss_Instance classToDoubleController1 = new Amoss_Instance( AmossTest_ClassToDouble.class ); + AmossTest_ClassToDouble classToDouble1 = (AmossTest_ClassToDouble)classToDoubleController1.getDouble(); + + Amoss_Instance classToDoubleController2 = classToDoubleController1.createClone(); + classToDoubleController2.expectsNoCalls(); + AmossTest_ClassToDouble classToDouble2 = (AmossTest_ClassToDouble)classToDoubleController2.getDouble(); + + Test.startTest(); + classToDouble1.methodUnderDouble( '1', 1 ); + Test.stopTest(); + + System.assertEquals( 1, classToDoubleController1.countOf( 'methodUnderDouble' ), 'createClone, when when additional config is added to the new, will not affect the original' ); + } + + /** + * @method createClone + * @case when additional config is added to one of the controllers + * @result will not affect the other + */ + @isTest + private static void createClone_whenAdditionalConfigIsAdded_notAffectTheOther2() { + + Amoss_Instance classToDoubleController1 = new Amoss_Instance( AmossTest_ClassToDouble.class ); + Amoss_Instance classToDoubleController2 = classToDoubleController1.createClone(); + + classToDoubleController1 + .when( 'methodUnderDouble' ) + .returns( 'ReturnFrom1' ); + AmossTest_ClassToDouble classToDouble1 = (AmossTest_ClassToDouble)classToDoubleController1.getDouble(); + + classToDoubleController2 + .when( 'methodUnderDouble' ) + .returns( 'ReturnFrom2' ); + AmossTest_ClassToDouble classToDouble2 = (AmossTest_ClassToDouble)classToDoubleController2.getDouble(); + + Test.startTest(); + String returnFrom1 = classToDouble1.methodUnderDouble( '1', 1 ); + String returnFrom2 = classToDouble2.methodUnderDouble( '1', 1 ); + Test.stopTest(); + + System.assertEquals( 'ReturnFrom1', returnFrom1, 'createClone, when when additional config is added to the new, will not affect the original' ); + System.assertEquals( 'ReturnFrom2', returnFrom2, 'createClone, when when additional config is added to the original, will not affect the new' ); + } + + /** + * @method isFluent.when.returns.generateDouble + * @case when the method is called + * @result will return the stated value + */ + @isTest + private static void generateDouble_isFluentWhenReturnsGenerateDouble_returnSetUpValue() { + + Amoss_Instance classToDoubleController = new Amoss_Instance( AmossTest_ClassToDouble.class ); + classToDoubleController + .isFluent() + .when( 'methodUnderDouble' ) + .returns( 'specifiedValue' ); + + AmossTest_ClassToDouble classToDouble = (AmossTest_ClassToDouble)classToDoubleController.generateDouble(); + + Test.startTest(); + String returnFromDouble = classToDouble.methodUnderDouble( '1', 2 ); + Test.stopTest(); + + System.assertEquals( 'specifiedValue', returnFromDouble, 'isFluent.when.returns.generateDouble, when the method is called, will return the stated value' ); + } + + // + // Limitation tests - If any of these tests begin to fail, then an important limitation + // has been removed and we may consider changing the functionality / documentation a little + // + /** + * @method getDouble + * @case when given an Sobject to create a test double for + * @result will throw an exception + */ + @isTest + private static void getDouble_whenGivenAnSobjectToCreateATestDoubleFor_throwAnException() { + + Amoss_Instance sobjectToDoubleController = new Amoss_Instance( Contact.class ); + + Boolean exceptionThrown = false; + String exceptionMessage; + + try { + Contact classUnderDouble = (Contact)sobjectToDoubleController.getDouble(); + } catch ( Exception e ) { + exceptionThrown = true; + exceptionMessage = e.getMessage(); + } + + System.assert( exceptionThrown, 'getDouble, when given an Sobject to create a test double for, will throw an exception' ); + } + + /** + * @method getDouble + * @case when given an inner class to create a test double for + * @result will throw an exception + */ + @isTest + private static void getDouble_whenGivenAnInnerClassToCreateATestDoubleFor_throwAnException() { + + Amoss_Instance innerClassToDoubleController = new Amoss_Instance( AmossTest_InstanceTest.InnerClassToDouble.class ); + + Boolean exceptionThrown = false; + String exceptionMessage; + + try { + AmossTest_InstanceTest.InnerClassToDouble classUnderDouble = (AmossTest_InstanceTest.InnerClassToDouble)innerClassToDoubleController.getDouble(); + classUnderDouble.publicMethod( 'StringParameter' ); + } catch ( Exception e ) { + exceptionThrown = true; + exceptionMessage = e.getMessage(); + } + + System.assert( exceptionThrown, 'getDouble, when given an inner class to create a test double for, will throw an exception' ); + } + + /** + * @method getDouble + * @case when given a System Type to create a test double for + * @result will throw an exception + */ + @isTest + private static void getDouble_whenGivenASystemTypeToCreateATestDoubleFor_throwAnException() { + + Amoss_Instance systemTypeToDoubleController = new Amoss_Instance( HttpRequest.class ); + + Boolean exceptionThrown = false; + String exceptionMessage; + + try { + HttpRequest classUnderDouble = (HttpRequest)systemTypeToDoubleController.getDouble(); + } catch ( Exception e ) { + exceptionThrown = true; + exceptionMessage = e.getMessage(); + } + + System.assert( exceptionThrown, 'getDouble, when given an system type to create a test double for, will throw an exception' ); + } + + /** + * @method getDouble + * @case when given a class with no public constructors to create a test double for + * @result will throw an exception + */ + @isTest + private static void getDouble_whenGivenAClassWithNoPublicConstructorToCreateATestDoubleFor_throwAnException() { + + Amoss_Instance singletonToDouble = new Amoss_Instance( AmossTest_SingletonToDouble.class ); + + Boolean exceptionThrown = false; + String exceptionMessage; + + try { + AmossTest_SingletonToDouble classUnderDouble = (AmossTest_SingletonToDouble)singletonToDouble.getDouble(); + } catch ( Exception e ) { + exceptionThrown = true; + exceptionMessage = e.getMessage(); + } + + System.assert( exceptionThrown, 'getDouble, when given a class with no public constructor to create a test double for, will throw an exception' ); + } + + // + // + // HttpCalloutMock tests + // + // + /** + * @method isACalloutMock + * @case when called + * @result will generate an HttpCalloutMock and register it + */ + @isTest + private static void isACalloutMock_willGenerateAnHttpCalloutMockAndRegisterIt() { + + new Amoss_Instance() + .isACalloutMock(); + + Test.startTest(); + HttpResponse response = new Http().send( new HttpRequest() ); + Test.stopTest(); + + System.assert( true, 'isACalloutMock will generate an HttpCalloutMock and register it, meaning HTTP requests can be made in the test without exceptions' ); + } + + /** + * @method isACalloutMock.respondsWith.statusCode + * @case when called + * @result will set the status code that is returned + */ + @isTest + private static void isACalloutMockRespondsWithStatusCode_willSetTheStatusCodeThatIsReturned() { + + new Amoss_Instance() + .isACalloutMock() + .when() + .respondsWith() + .statusCode( 404 ); + + Test.startTest(); + HttpResponse response = new Http().send( new HttpRequest() ); + Test.stopTest(); + + System.assertEquals( 404, response.getStatusCode(), 'isACalloutMock.respondsWith.statusCode, will set the status code that is returned on the generated HttpResponse' ); + } + + /** + * @method isACalloutMock.respondsWith.status + * @case when called + * @result will set the status that is returned + */ + @isTest + private static void isACalloutMockRespondsWithStatus_willSetTheStatusThatIsReturned() { + + new Amoss_Instance() + .isACalloutMock() + .when() + .respondsWith() + .status( 'Complete' ); + + Test.startTest(); + HttpResponse response = new Http().send( new HttpRequest() ); + Test.stopTest(); + + System.assertEquals( 'Complete', response.getStatus(), 'isACalloutMock.respondsWith.status, will set the status that is returned on the generated HttpResponse' ); + } + + /** + * @method isACalloutMock.respondsWith.body (string) + * @case when called + * @result will set the body that is returned + */ + @isTest + private static void isACalloutMockRespondsWithBodyString_willSetTheBodyThatIsReturned() { + + new Amoss_Instance() + .isACalloutMock() + .when() + .respondsWith() + .body( 'The body' ); + + Test.startTest(); + HttpResponse response = new Http().send( new HttpRequest() ); + Test.stopTest(); + + System.assertEquals( 'The body', response.getBody(), 'isACalloutMock.respondsWith.body( String ), will set the body that is returned on the generated HttpResponse' ); + } + + /** + * @method isACalloutMock.respondsWith.body (Object) + * @case when called + * @result will serialise the object and set the body that is returned + */ + @isTest + private static void isACalloutMockRespondsWithBodyObject_willSerialiseAndSetTheBodyThatIsReturned() { + + new Amoss_Instance() + .isACalloutMock() + .when() + .respondsWith() + .body( new Map{ 'parameter' => 'value' } ); + + Test.startTest(); + HttpResponse response = new Http().send( new HttpRequest() ); + Test.stopTest(); + + System.assertEquals( '{"parameter":"value"}', response.getBody(), 'isACalloutMock.respondsWith.body( Object ), will serialise the object and set the body that is returned on the generated HttpResponse' ); + } + + /** + * @method isACalloutMock.respondsWith.body (blob) + * @case when called + * @result will set the body that is returned + */ + @isTest + private static void isACalloutMockRespondsWithBodyBlob_willSetTheBodyThatIsReturned() { + + new Amoss_Instance() + .isACalloutMock() + .when() + .respondsWith() + .body( Blob.valueOf( 'The body' ) ); + + Test.startTest(); + HttpResponse response = new Http().send( new HttpRequest() ); + Test.stopTest(); + + System.assertEquals( 'The body', response.getBody(), 'isACalloutMock.respondsWith.body( Blob ), will set the body that is returned on the generated HttpResponse' ); + } + + /** + * @method isACalloutMock.respondsWith.header.setTo + * @case when called + * @result will set the headers that are returned + */ + @isTest + private static void isACalloutMockRespondsWithHeaderSetTo_willSetTheHeadersThatAreReturned() { + + new Amoss_Instance() + .isACalloutMock() + .when() + .respondsWith() + .header( 'one' ).setTo( 'oneValue' ) + .header( 'two' ).setTo( 'twoValue' ); + + Test.startTest(); + HttpResponse response = new Http().send( new HttpRequest() ); + Test.stopTest(); + + System.assertEquals( 'oneValue', response.getHeader( 'one' ), 'isACalloutMock.respondsWith.header.setTo, will set the headers that are returned on the generated HttpResponse' ); + System.assertEquals( 'twoValue', response.getHeader( 'two' ), 'isACalloutMock.respondsWith.header.setTo, will set the headers that are returned on the generated HttpResponse' ); + } + + /** + * @method isACalloutMock.respondsWith + * @case when multiple properties are set + * @result will set all of them on the response that is returned + */ + @isTest + private static void isACalloutMockRespondsWith_whenMultipleProperties_willSetThemAll() { + + new Amoss_Instance() + .isACalloutMock() + .when() + .respondsWith() + .statusCode( 200 ) + .status( 'Complete' ) + .body( 'A body' ); + + Test.startTest(); + HttpResponse response = new Http().send( new HttpRequest() ); + Test.stopTest(); + + System.assertEquals( 200 , response.getStatusCode(), 'isACalloutMock.respondsWith, when multiple properties are set, will set them all on the generated HttpResponse' ); + System.assertEquals( 'Complete', response.getStatus() , 'isACalloutMock.respondsWith, when multiple properties are set, will set them all on the generated HttpResponse' ); + System.assertEquals( 'A body' , response.getBody() , 'isACalloutMock.respondsWith, when multiple properties are set, will set them all on the generated HttpResponse' ); + } + + /** + * @method isACalloutMock.byDefault.respondsWith.status + * @case when called and doesn't match anything else + * @result will set the status that is returned + */ + @isTest + private static void isACalloutMockByDefaultRespondsWithStatusCode_willSetTheStatusCodeThatIsReturned() { + + new Amoss_Instance() + .isACalloutMock() + .byDefault() + .respondsWith() + .statusCode( 200 ) + .also().when() + .method( 'GET' ) + .respondsWith() + .statusCode( 404 ); + + Test.startTest(); + HttpResponse response = new Http().send( new HttpRequest() ); + Test.stopTest(); + + System.assertEquals( 200, response.getStatusCode(), 'isACalloutMock.byDefault.respondsWith.statusCode, and does not match a defined call, will set the status that is returned on the generated HttpResponse' ); + } + + /** + * @method isACalloutMock.byDefault.respondsWith.status + * @case when called and doesn't match anything else + * @result will set the status that is returned + */ + @isTest + private static void isACalloutMockByDefaultRespondsWithStatus_willSetTheStatusThatIsReturned() { + + new Amoss_Instance() + .isACalloutMock() + .byDefault() + .respondsWith() + .status( 'Complete' ) + .also().when() + .method( 'GET' ) + .respondsWith() + .status( 'Not Found' ); + + Test.startTest(); + HttpResponse response = new Http().send( new HttpRequest() ); + Test.stopTest(); + + System.assertEquals( 'Complete', response.getStatus(), 'isACalloutMock.byDefault.respondsWith.status, and does not match a defined call, will set the status that is returned on the generated HttpResponse' ); + } + + /** + * @method isACalloutMock.byDefault.respondsWith.body (string) + * @case when called and doesn't match anything else + * @result will set the body that is returned + */ + @isTest + private static void isACalloutMockByDefaultRespondsWithBodyString_willSetTheBodyThatIsReturned() { + + new Amoss_Instance() + .isACalloutMock() + .byDefault() + .respondsWith() + .body( 'The body' ) + .also().when() + .method( 'GET' ) + .respondsWith() + .body( 'The non default body' ); + + Test.startTest(); + HttpResponse response = new Http().send( new HttpRequest() ); + Test.stopTest(); + + System.assertEquals( 'The body', response.getBody(), 'isACalloutMock.byDefault.respondsWith.body( String ), and does not match a defined call, will set the body that is returned on the generated HttpResponse' ); + } + + /** + * @method isACalloutMock.byDefault.respondsWith.body (Object) + * @case when called and doesn't match anything else + * @result will serialise the object and set the body that is returned + */ + @isTest + private static void isACalloutMockByDefaultRespondsWithBodyObject_willSerialiseAndSetTheBodyThatIsReturned() { + + new Amoss_Instance() + .isACalloutMock() + .byDefault() + .respondsWith() + .body( new Map{ 'parameter' => 'value' } ) + .also().when() + .method( 'GET' ) + .respondsWith() + .body( new Map{ 'parameter' => 'non default value' } ); + + Test.startTest(); + HttpResponse response = new Http().send( new HttpRequest() ); + Test.stopTest(); + + System.assertEquals( '{"parameter":"value"}', response.getBody(), 'isACalloutMock.byDefault.respondsWith.body( Object ), and does not match a defined call, will serialise the object and set the body that is returned on the generated HttpResponse' ); + } + + /** + * @method isACalloutMock.byDefault.respondsWith.body (blob) + * @case when called and doesn't match anything else + * @result will set the body that is returned + */ + @isTest + private static void isACalloutMockByDefaultRespondsWithBodyBlob_willSetTheBodyThatIsReturned() { + + new Amoss_Instance() + .isACalloutMock() + .byDefault() + .respondsWith() + .body( Blob.valueOf( 'The body' ) ) + .also().when() + .method( 'GET' ) + .respondsWith() + .body( Blob.valueOf( 'The non default body' ) ); + + Test.startTest(); + HttpResponse response = new Http().send( new HttpRequest() ); + Test.stopTest(); + + System.assertEquals( 'The body', response.getBody(), 'isACalloutMock.byDefault.respondsWith.body( Blob ), and does not match a defined call, will set the body that is returned on the generated HttpResponse' ); + } + + /** + * @method isACalloutMock.byDefault.respondsWith.header.setTo + * @case when called and doesn't match anything else + * @result will set the header that is returned + */ + @isTest + private static void isACalloutMockByDefaultRespondsWithHeaderSetTo_willSetTheHeaderThatIsReturned() { + + new Amoss_Instance() + .isACalloutMock() + .byDefault() + .respondsWith() + .header( 'Authorised' ).setTo( 'default' ) + .also().when() + .method( 'GET' ) + .respondsWith() + .header( 'Authorised' ).setTo( 'non-default' ); + + Test.startTest(); + HttpResponse response = new Http().send( new HttpRequest() ); + Test.stopTest(); + + System.assertEquals( 'default', response.getHeader( 'Authorised' ), 'isACalloutMock.byDefault.respondsWith.header.setTo, and does not match a defined call, will set the header that is returned on the generated HttpResponse' ); + } + + /** + * @method isACalloutMock.respondsWith + * @case when multiple properties are set, is called and doesn't match anything else + * @result will set all of them on the response that is returned + */ + @isTest + private static void isACalloutMockByDefaultRespondsWith_whenMultipleProperties_willSetThemAll() { + + new Amoss_Instance() + .isACalloutMock() + .byDefault() + .respondsWith() + .statusCode( 200 ) + .status( 'Complete' ) + .body( 'A body' ) + .header( 'Authorised' ).setTo( 'true' ) + .also().when() + .method( 'GET' ) + .respondsWith() + .statusCode( 404 ) + .status( 'Not Found' ) + .body( 'No body' ) + .header( 'Authorised' ).setTo( 'false' ); + + Test.startTest(); + HttpResponse response = new Http().send( new HttpRequest() ); + Test.stopTest(); + + System.assertEquals( 200 , response.getStatusCode() , 'isACalloutMock.byDefault.respondsWith, when multiple properties are set, and does not match a defined call, will set them all on the generated HttpResponse' ); + System.assertEquals( 'Complete', response.getStatus() , 'isACalloutMock.byDefault.respondsWith, when multiple properties are set, and does not match a defined call, will set them all on the generated HttpResponse' ); + System.assertEquals( 'A body' , response.getBody() , 'isACalloutMock.byDefault.respondsWith, when multiple properties are set, and does not match a defined call, will set them all on the generated HttpResponse' ); + System.assertEquals( 'true' , response.getHeader( 'Authorised' ), 'isACalloutMock.byDefault.respondsWith, when multiple properties are set, and does not match a defined call, will set them all on the generated HttpResponse' ); + } + + /** + * @method isACalloutMock.respondsWith + * @case when multiple properties are set, is called and matches a definition + * @result will return the defined one instead + */ + @isTest + private static void isACalloutMockByDefaultRespondsWith_whenCallMatchesADefined_willReturnTheDefined() { + + new Amoss_Instance() + .isACalloutMock() + .byDefault() + .respondsWith() + .statusCode( 200 ) + .status( 'Complete' ) + .body( 'A body' ) + .header( 'Authorised' ).setTo( 'true' ) + .also().when() + .method( 'GET' ) + .respondsWith() + .statusCode( 404 ) + .status( 'Not Found' ) + .body( 'No body' ) + .header( 'Authorised' ).setTo( 'false' ); + + Test.startTest(); + HttpRequest request = new HttpRequest(); + request.setMethod( 'GET' ); + HttpResponse response = new Http().send( request ); + Test.stopTest(); + + System.assertEquals( 404 , response.getStatusCode() , 'isACalloutMock.byDefault.respondsWith, when the call matches a defined one, will return the defined response instead' ); + System.assertEquals( 'Not Found', response.getStatus() , 'isACalloutMock.byDefault.respondsWith, when the call matches a defined one, will return the defined response instead' ); + System.assertEquals( 'No body' , response.getBody() , 'isACalloutMock.byDefault.respondsWith, when the call matches a defined one, will return the defined response instead' ); + System.assertEquals( 'false' , response.getHeader( 'Authorised' ), 'isACalloutMock.byDefault.respondsWith, when the call matches a defined one, will return the defined response instead' ); + } + + /** + * @method isACalloutMock.handledBy (Amoss_MethodHandler) + * @case when call is made + * @result will be handled by the object specified + */ + @isTest + private static void isACalloutMockHandledByMethodHandler_whenHandledByAndCallIsMade_willBeHandledByObject() { + + HttpResponse mockResponse = new HttpResponse(); + mockResponse.setBody( 'FromTheHandler' ); + + Amoss_Instance methodHandlerController = new Amoss_Instance( Amoss_MethodHandler.class ); + methodHandlerController + .expects() + .method( 'handleMethodCall' ) + .returning( mockResponse ); + + new Amoss_Instance() + .isACalloutMock() + .when() + .handledBy( (Amoss_MethodHandler)methodHandlerController.generateDouble() ); + + Test.startTest(); + HttpResponse response = new Http().send( new HttpRequest() ); + Test.stopTest(); + + System.assertEquals( 'FromTheHandler', response.getBody(), 'isACalloutMock.handledBy (Amoss_MethodHandler), when a call is made, will be handled by the object specified' ); + } + + /** + * @method isACalloutMock.handledBy (StubProvider) + * @case when call is made + * @result will be handled by the object specified + */ + @isTest + private static void isACalloutMockHandledByStubProvider_whenHandledByAndCallIsMade_willBeHandledByObject() { + + new Amoss_Instance() + .isACalloutMock() + .when() + .handledBy( new MockHttpMethodHandlerUsingStubProvider() ); + + Test.startTest(); + HttpResponse response = new Http().send( new HttpRequest() ); + Test.stopTest(); + + System.assertEquals( 'FromTheHandler', response.getBody(), 'isACalloutMock.handledBy (StubProvider), when a call is made, will be handled by the object specified' ); + } + + class MockHttpMethodHandlerUsingStubProvider implements StubProvider { + public Object handleMethodCall( Object mockedObject, + String mockedMethod, + Type returnType, + List parameterTypes, + List parameterNames, + List parameters ) { + HttpResponse response = new HttpResponse(); + response.setBody( 'FromTheHandler' ); + return response; + } + } + + /** + * @method isACalloutMock.handledBy (HttpCalloutMock) + * @case when call is made + * @result will be handled by the object specified + */ + @isTest + private static void isACalloutMockHandledByHttpCalloutMock_whenHandledByAndCallIsMade_willBeHandledByObject() { + + HttpResponse mockResponse = new HttpResponse(); + mockResponse.setBody( 'FromTheHandler' ); + + Amoss_Instance methodHandlerController = new Amoss_Instance( Amoss_HttpCalloutMock.class ); + methodHandlerController + .expects() + .method( 'respond' ) + .returning( mockResponse ); + + new Amoss_Instance() + .isACalloutMock() + .when() + .handledBy( (HttpCalloutMock)methodHandlerController.generateDouble() ); + + Test.startTest(); + HttpResponse response = new Http().send( new HttpRequest() ); + Test.stopTest(); + + System.assertEquals( 'FromTheHandler', response.getBody(), 'isACalloutMock.handledBy (HttpCalloutMock), when a call is made, will be handled by the object specified' ); + } + + /** + * @method isACalloutMock.throws + * @case when called + * @result will throw the stated exception + */ + @isTest + private static void isACalloutMockThrows_whenCalled_willThrow() { + + new Amoss_Instance() + .isACalloutMock() + .when() + .throws( new CalloutException( 'An Exception' ) ); + + Boolean exceptionThrown = false; + String exceptionMessage; + + Test.startTest(); + try { + HttpResponse response = new Http().send( new HttpRequest() ); + } catch( CalloutException e ) { + exceptionThrown = true; + exceptionMessage = e.getMessage(); + } + Test.stopTest(); + + System.assertEquals( true, exceptionThrown, 'isACalloutMock.throws, when called, will throw the stated exception' ); + System.assertEquals( 'An Exception', exceptionMessage, 'isACalloutMock.throws, when called, will throw the stated exception' ); + } + + /** + * @method isACalloutMock.throwing + * @case when called + * @result will throw the stated exception + */ + @isTest + private static void isACalloutMockThrowing_whenCalled_willThrow() { + + new Amoss_Instance() + .isACalloutMock() + .when() + .throwing( new CalloutException( 'An Exception' ) ); + + Boolean exceptionThrown = false; + String exceptionMessage; + + Test.startTest(); + try { + HttpResponse response = new Http().send( new HttpRequest() ); + } catch( CalloutException e ) { + exceptionThrown = true; + exceptionMessage = e.getMessage(); + } + Test.stopTest(); + + System.assertEquals( true, exceptionThrown, 'isACalloutMock.throwing, when called, will throw the stated exception' ); + System.assertEquals( 'An Exception', exceptionMessage, 'isACalloutMock.throwing, when called, will throw the stated exception' ); + } + + /** + * @method isACalloutMock.willThrow + * @case when called + * @result will throw the stated exception + */ + @isTest + private static void isACalloutMockWillThrow_whenCalled_willThrow() { + + new Amoss_Instance() + .isACalloutMock() + .when() + .willThrow( new CalloutException( 'An Exception' ) ); + + Boolean exceptionThrown = false; + String exceptionMessage; + + Test.startTest(); + try { + HttpResponse response = new Http().send( new HttpRequest() ); + } catch( CalloutException e ) { + exceptionThrown = true; + exceptionMessage = e.getMessage(); + } + Test.stopTest(); + + System.assertEquals( true, exceptionThrown, 'isACalloutMock.willThrow, when called, will throw the stated exception' ); + System.assertEquals( 'An Exception', exceptionMessage, 'isACalloutMock.willThrow, when called, will throw the stated exception' ); + } + + /** + * @method isACalloutMock + * @case when no calls specified and a call is made + * @result will return an empty response + */ + @isTest + private static void isACalloutMock_whenNoCallsSpecifiedAndCallMade_returnEmptyResponse() { + + new Amoss_Instance() + .isACalloutMock(); + + Test.startTest(); + HttpResponse response = new Http().send( new HttpRequest() ); + Test.stopTest(); + + System.assertEquals( 0, response.getStatusCode(), 'isACalloutMock, when no calls specified and call made, will return an empty response' ); + } + + /** + * @method allowsAnyCallFalse.isACalloutMock + * @case when no calls specified and a call is made + * @result will fail the test + */ + @isTest + private static void allowsAnyCallFalseIsACalloutMock_whenNoCallsSpecifiedAndCallMade_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + + new Amoss_Instance() + .setAsserts( (Amoss_Asserts)assertsDoubleController.getDouble() ) + .allowsAnyCall( false ) + .isACalloutMock(); + + Test.startTest(); + try { + HttpResponse response = new Http().send( new HttpRequest() ); + } catch ( TestException e ) {} + Test.stopTest(); + + String exceptionMessage = (String)assertsDoubleController.get().latestCallOf( 'assert' ).parameter( 1 ); + + System.assertEquals( false, assertsDoubleController.get().latestCallOf( 'assert' ).parameter( 0 ), 'allowsAnyCallFalse.isACalloutMock, when no calls specified and a call is made, will fail the test' ); + Amoss_Asserts.assertContains( HTTP_CALLOUT_MOCK_UNEXPECTED_EXCEPTION_TEXT, exceptionMessage, 'allowsAnyCallFalse.isACalloutMock, when no calls specified and a call is made, will fail the test, with a message that explains the issue' ); + } + + /** + * @method isACalloutMock.allowsAnyCallFalse + * @case when no calls specified and a call is made + * @result will fail the test + */ + @isTest + private static void isACalloutMockAllowsAnyCallFalse_whenNoCallsSpecifiedAndCallMade_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + + new Amoss_Instance() + .setAsserts( (Amoss_Asserts)assertsDoubleController.getDouble() ) + .isACalloutMock() + .allowsAnyCall( false ); + + Test.startTest(); + try { + HttpResponse response = new Http().send( new HttpRequest() ); + } catch ( TestException e ) {} + Test.stopTest(); + + String exceptionMessage = (String)assertsDoubleController.get().latestCallOf( 'assert' ).parameter( 1 ); + + System.assertEquals( false, assertsDoubleController.get().latestCallOf( 'assert' ).parameter( 0 ), 'isACalloutMock.allowsAnyCallFalse, when no calls specified and a call is made, will fail the test' ); + Amoss_Asserts.assertContains( HTTP_CALLOUT_MOCK_UNEXPECTED_EXCEPTION_TEXT, exceptionMessage, 'isACalloutMock.allowsAnyCallFalse, when no calls specified and a call is made, will fail the test, with a message that explains the issue' ); + } + + /** + * @method isACalloutMock.when.method + * @case when called with matching method + * @result will return as specified + */ + @isTest + private static void isACalloutMockWhenMethod_whenTheMethodMatches_returnAsSpecified() { + + new Amoss_Instance() + .isACalloutMock() + .when() + .method( 'GET' ) + .respondsWith() + .statusCode( 200 ); + + Test.startTest(); + HttpRequest request = new HttpRequest(); + request.setMethod( 'GET' ); + HttpResponse response = new Http().send( request ); + Test.stopTest(); + + System.assertEquals( 200, response.getStatusCode(), 'isACalloutMock.when.method, when the method matches, will return as specified' ); + } + + /** + * @method isACalloutMock.allowsAnyCallFalse.when.method + * @case when called without matching method + * @result will fail the test + */ + @isTest + private static void isACalloutMockWhenMethod_whenCalledWithoutMatchingMethod_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + + new Amoss_Instance() + .setAsserts( (Amoss_Asserts)assertsDoubleController.getDouble() ) + .allowsAnyCall( false ) + .isACalloutMock() + .when() + .method( 'GET' ) + .respondsWith() + .statusCode( 200 ); + + Test.startTest(); + try { + HttpRequest request = new HttpRequest(); + request.setMethod( 'POST' ); + HttpResponse response = new Http().send( request ); + } catch ( TestException e ) {} + Test.stopTest(); + + String exceptionMessage = (String)assertsDoubleController.get().latestCallOf( 'assert' ).parameter( 1 ); + + System.assertEquals( false, assertsDoubleController.get().latestCallOf( 'assert' ).parameter( 0 ), 'isACalloutMock.allowsAnyCallFalse.when.method, when called without matching method, will fail the test' ); + Amoss_Asserts.assertContains( HTTP_CALLOUT_MOCK_UNEXPECTED_EXCEPTION_TEXT , exceptionMessage, 'isACalloutMock.allowsAnyCallFalse.when.method, when called without matching method, will fail the test, with a message that explains the issue' ); + Amoss_Asserts.assertContains( 'System.HttpRequest[Endpoint=null, Method=POST]', exceptionMessage, 'isACalloutMock.allowsAnyCallFalse.when.method, when called without matching method, will fail the test, with a message that contains the request' ); + } + + /** + * @method isACalloutMock.when.method + * @case when called without matching method + * @result will return an empty response + */ + @isTest + private static void isACalloutMockWhenMethod_whenCalledWithoutMatchingMethod_willReturnEmptyResponse() { + + new Amoss_Instance() + .isACalloutMock() + .when() + .method( 'GET' ) + .respondsWith() + .statusCode( 200 ); + + Test.startTest(); + HttpRequest request = new HttpRequest(); + request.setMethod( 'POST' ); + HttpResponse response = new Http().send( request ); + Test.stopTest(); + + System.assertEquals( 0, response.getStatusCode(), 'isACalloutMock.when.method, when called without matching method, will return an empty response' ); + } + + /** + * @method isACalloutMock.when.endpoint + * @case when called with matching endpoint + * @result will return as specified + */ + @isTest + private static void isACalloutMockWhenEndpoint_whenTheEndpointMatches_returnAsSpecified() { + + new Amoss_Instance() + .isACalloutMock() + .when() + .endpoint( 'http://www.example.com' ) + .respondsWith() + .statusCode( 200 ); + + Test.startTest(); + HttpRequest request = new HttpRequest(); + request.setEndpoint( 'http://www.example.com' ); + HttpResponse response = new Http().send( request ); + Test.stopTest(); + + System.assertEquals( 200, response.getStatusCode(), 'isACalloutMock.when.endpoint, when the endpoint matches, will return as specified' ); + } + + /** + * @method isACalloutMock.when.endpoint + * @case when called without matching endpoint + * @result will return an empty response + */ + @isTest + private static void isACalloutMockWhenEndpoint_whenCalledWithoutMatchingEndpoint_willReturnEmptyResponse() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + + new Amoss_Instance() + .isACalloutMock() + .when() + .endpoint( 'http://www.example.com' ) + .respondsWith() + .statusCode( 200 ); + + Test.startTest(); + HttpRequest request = new HttpRequest(); + request.setEndpoint( 'different' ); + HttpResponse response = new Http().send( request ); + Test.stopTest(); + + System.assertEquals( 0, response.getStatusCode(), 'isACalloutMock.when.endpoint, when called without matching endpoint, will return an empty response' ); + } + + /** + * @method isACalloutMock.when.endpoint.setTo + * @case when called with matching endpoint.setTo + * @result will return as specified + */ + @isTest + private static void isACalloutMockWhenEndpointSetTo_whenTheEndpointMatches_returnAsSpecified() { + + new Amoss_Instance() + .isACalloutMock() + .when() + .endpoint().setTo( 'http://www.example.com' ) + .respondsWith() + .statusCode( 200 ); + + Test.startTest(); + HttpRequest request = new HttpRequest(); + request.setEndpoint( 'http://www.example.com' ); + HttpResponse response = new Http().send( request ); + Test.stopTest(); + + System.assertEquals( 200, response.getStatusCode(), 'isACalloutMock.when.endpoint.setTo, when the endpoint matches, will return as specified' ); + } + + /** + * @method isACalloutMock.when.endpoint.setTo + * @case when called without matching endpoint + * @result will return an empty response + */ + @isTest + private static void isACalloutMockWhenEndpointSetTo_whenCalledWithoutMatchingEndpoint_willReturnEmptyResponse() { + + new Amoss_Instance() + .isACalloutMock() + .when() + .endpoint().setTo( 'http://www.example.com' ) + .respondsWith() + .statusCode( 200 ); + + Test.startTest(); + HttpRequest request = new HttpRequest(); + request.setEndpoint( 'different' ); + HttpResponse response = new Http().send( request ); + Test.stopTest(); + + System.assertEquals( 0, response.getStatusCode(), 'isACalloutMock.when.endpoint.setTo, when called without matching endpoint, will return an empty response' ); + } + + /** + * @method isACalloutMock.when.endpoint.set + * @case when called with matching endpoint.set + * @result will return as specified + */ + @isTest + private static void isACalloutMockWhenEndpointSet_whenTheEndpointIsSet_returnAsSpecified() { + + new Amoss_Instance() + .isACalloutMock() + .when() + .endpoint().set() + .respondsWith() + .statusCode( 200 ); + + Test.startTest(); + HttpRequest request = new HttpRequest(); + request.setEndpoint( 'http://www.example.com' ); + HttpResponse response = new Http().send( request ); + Test.stopTest(); + + System.assertEquals( 200, response.getStatusCode(), 'isACalloutMock.when.endpoint.set, when the endpoint set, will return as specified' ); + } + + /** + * @method isACalloutMock.when.endpoint.set + * @case when called without endpoint + * @result will fail the test + */ + @isTest + private static void isACalloutMockWhenEndpointSet_whenCalledWithoutEndpoint_willReturnEmptyResponse() { + + new Amoss_Instance() + .isACalloutMock() + .when() + .endpoint().set() + .respondsWith() + .statusCode( 200 ); + + Test.startTest(); + HttpRequest request = new HttpRequest(); + HttpResponse response = new Http().send( request ); + Test.stopTest(); + + System.assertEquals( 0, response.getStatusCode(), 'isACalloutMock.when.endpoint.set, when called without matching endpoint, will return an empty response' ); + } + + /** + * @method isACalloutMock.when.endpoint.containing + * @case when called with matching endpoint.containing + * @result will return as specified + */ + @isTest + private static void isACalloutMockWhenEndpointContaining_whenTheEndpointMatches_returnAsSpecified() { + + new Amoss_Instance() + .isACalloutMock() + .when() + .endpoint().containing( 'example' ) + .respondsWith() + .statusCode( 200 ); + + Test.startTest(); + HttpRequest request = new HttpRequest(); + request.setEndpoint( 'http://www.example.com' ); + HttpResponse response = new Http().send( request ); + Test.stopTest(); + + System.assertEquals( 200, response.getStatusCode(), 'isACalloutMock.when.endpoint.containing, when the endpoint matches, will return as specified' ); + } + + /** + * @method isACalloutMock.when.endpoint.containing + * @case when called without matching endpoint + * @result will return an empty response + */ + @isTest + private static void isACalloutMockWhenEndpointContaining_whenCalledWithoutMatchingEndpoint_wilLReturnEmptyResponse() { + + new Amoss_Instance() + .isACalloutMock() + .when() + .endpoint().containing( 'example' ) + .respondsWith() + .statusCode( 200 ); + + Test.startTest(); + HttpRequest request = new HttpRequest(); + request.setEndpoint( 'different' ); + HttpResponse response = new Http().send( request ); + Test.stopTest(); + + System.assertEquals( 0, response.getStatusCode(), 'isACalloutMock.when.endpoint.containing, when called without matching endpoint, will return an empty response' ); + } + + /** + * @method isACalloutMock.when.endpoint.matching + * @case when called with matching endpoint.matching + * @result will return as specified + */ + @isTest + private static void isACalloutMockWhenEndpointMatching_whenTheEndpointMatches_returnAsSpecified() { + + new Amoss_Instance() + .isACalloutMock() + .when() + .endpoint().matching( 'http.*com' ) + .respondsWith() + .statusCode( 200 ); + + Test.startTest(); + HttpRequest request = new HttpRequest(); + request.setEndpoint( 'http://www.example.com' ); + HttpResponse response = new Http().send( request ); + Test.stopTest(); + + System.assertEquals( 200, response.getStatusCode(), 'isACalloutMock.when.endpoint.matching, when the endpoint matches, will return as specified' ); + } + + /** + * @method isACalloutMock.when.endpoint.matching + * @case when called without matching endpoint + * @result will return an empty response + */ + @isTest + private static void isACalloutMockWhenEndpointMatching_whenCalledWithoutMatchingEndpoint_wilLReturnEmptyResponse() { + + new Amoss_Instance() + .isACalloutMock() + .when() + .endpoint().matching( 'http.*com' ) + .respondsWith() + .statusCode( 200 ); + + Test.startTest(); + HttpRequest request = new HttpRequest(); + request.setEndpoint( 'different' ); + HttpResponse response = new Http().send( request ); + Test.stopTest(); + + System.assertEquals( 0, response.getStatusCode(), 'isACalloutMock.when.endpoint.matching, when called without matching endpoint, will return an empty response' ); + } + + /** + * @method isACalloutMock.when.body + * @case when called with matching body + * @result will return as specified + */ + @isTest + private static void isACalloutMockWhenBody_whenTheBodyMatches_returnAsSpecified() { + + new Amoss_Instance() + .isACalloutMock() + .when() + .body( 'The body text' ) + .respondsWith() + .statusCode( 200 ); + + Test.startTest(); + HttpRequest request = new HttpRequest(); + request.setBody( 'The body text' ); + HttpResponse response = new Http().send( request ); + Test.stopTest(); + + System.assertEquals( 200, response.getStatusCode(), 'isACalloutMock.when.body, when the body matches, will return as specified' ); + } + + /** + * @method isACalloutMock.when.body + * @case when called without matching body + * @result will return an empty response + */ + @isTest + private static void isACalloutMockWhenBody_whenCalledWithoutMatchingBody_willReturnEmptyResponse() { + + new Amoss_Instance() + .isACalloutMock() + .when() + .body( 'The body text' ) + .respondsWith() + .statusCode( 200 ); + + Test.startTest(); + HttpRequest request = new HttpRequest(); + request.setBody( 'different' ); + HttpResponse response = new Http().send( request ); + Test.stopTest(); + + System.assertEquals( 0, response.getStatusCode(), 'isACalloutMock.when.body, when called without matching body, will return an empty response' ); + } + + /** + * @method isACalloutMock.when.body.setTo + * @case when called with matching body.setTo + * @result will return as specified + */ + @isTest + private static void isACalloutMockWhenBodySetTo_whenTheBodyMatches_returnAsSpecified() { + + new Amoss_Instance() + .isACalloutMock() + .when() + .body().setTo( 'The body text' ) + .respondsWith() + .statusCode( 200 ); + + Test.startTest(); + HttpRequest request = new HttpRequest(); + request.setBody( 'The body text' ); + HttpResponse response = new Http().send( request ); + Test.stopTest(); + + System.assertEquals( 200, response.getStatusCode(), 'isACalloutMock.when.body.setTo, when the body matches, will return as specified' ); + } + + /** + * @method isACalloutMock.when.body.setTo + * @case when called without matching body + * @result will return an empty response + */ + @isTest + private static void isACalloutMockWhenBodySetTo_whenCalledWithoutMatchingBody_willReturnEmptyResponse() { + + new Amoss_Instance() + .isACalloutMock() + .when() + .body().setTo( 'The body text' ) + .respondsWith() + .statusCode( 200 ); + + Test.startTest(); + HttpRequest request = new HttpRequest(); + request.setBody( 'different' ); + HttpResponse response = new Http().send( request ); + Test.stopTest(); + + System.assertEquals( 0, response.getStatusCode(), 'isACalloutMock.when.body.setTo, when called without matching body, will return an empty response' ); + } + + /** + * @method isACalloutMock.when.body.set + * @case when called with matching body.set + * @result will return as specified + */ + @isTest + private static void isACalloutMockWhenBodySet_whenTheBodyIsSet_returnAsSpecified() { + + new Amoss_Instance() + .isACalloutMock() + .when() + .body().set() + .respondsWith() + .statusCode( 200 ); + + Test.startTest(); + HttpRequest request = new HttpRequest(); + request.setBody( 'The body text' ); + HttpResponse response = new Http().send( request ); + Test.stopTest(); + + System.assertEquals( 200, response.getStatusCode(), 'isACalloutMock.when.body.set, when the body set, will return as specified' ); + } + + /** + * @method isACalloutMock.when.body.set + * @case when called without body + * @result will return an empty response + */ + @isTest + private static void isACalloutMockWhenBodySet_whenCalledWithoutBody_willReturnEmptyResponse() { + + new Amoss_Instance() + .isACalloutMock() + .when() + .body().set() + .respondsWith() + .statusCode( 200 ); + + Test.startTest(); + HttpRequest request = new HttpRequest(); + HttpResponse response = new Http().send( request ); + Test.stopTest(); + + System.assertEquals( 0, response.getStatusCode(), 'isACalloutMock.when.body.set, when called without matching body, will return an empty response' ); + } + + /** + * @method isACalloutMock.when.body.containing + * @case when called with matching body.containing + * @result will return as specified + */ + @isTest + private static void isACalloutMockWhenBodyContaining_whenTheBodyMatches_returnAsSpecified() { + + new Amoss_Instance() + .isACalloutMock() + .when() + .body().containing( 'text' ) + .respondsWith() + .statusCode( 200 ); + + Test.startTest(); + HttpRequest request = new HttpRequest(); + request.setBody( 'The body text' ); + HttpResponse response = new Http().send( request ); + Test.stopTest(); + + System.assertEquals( 200, response.getStatusCode(), 'isACalloutMock.when.body.containing, when the body matches, will return as specified' ); + } + + /** + * @method isACalloutMock.when.body.containing + * @case when called without matching body + * @result will return an empty response + */ + @isTest + private static void isACalloutMockWhenBodyContaining_whenCalledWithoutMatchingBody_willReturnEmptyResponse() { + + new Amoss_Instance() + .isACalloutMock() + .when() + .body().containing( 'example' ) + .respondsWith() + .statusCode( 200 ); + + Test.startTest(); + HttpRequest request = new HttpRequest(); + request.setBody( 'different' ); + HttpResponse response = new Http().send( request ); + Test.stopTest(); + + System.assertEquals( 0, response.getStatusCode(), 'isACalloutMock.when.body.containing, when called without matching body, will return an empty response' ); + } + + /** + * @method isACalloutMock.when.body.matching + * @case when called with matching body.matching + * @result will return as specified + */ + @isTest + private static void isACalloutMockWhenBodyMatching_whenTheBodyMatches_returnAsSpecified() { + + new Amoss_Instance() + .isACalloutMock() + .when() + .body().matching( 'T.*t' ) + .respondsWith() + .statusCode( 200 ); + + Test.startTest(); + HttpRequest request = new HttpRequest(); + request.setBody( 'The body text' ); + HttpResponse response = new Http().send( request ); + Test.stopTest(); + + System.assertEquals( 200, response.getStatusCode(), 'isACalloutMock.when.body.matching, when the body matches, will return as specified' ); + } + + /** + * @method isACalloutMock.when.body.matching + * @case when called without matching body + * @result will return an empty response + */ + @isTest + private static void isACalloutMockWhenBodyMatching_whenCalledWithoutMatchingBody_willReturnEmptyResponse() { + + new Amoss_Instance() + .isACalloutMock() + .when() + .body().matching( 'The.*text' ) + .respondsWith() + .statusCode( 200 ); + + Test.startTest(); + HttpRequest request = new HttpRequest(); + request.setBody( 'different' ); + HttpResponse response = new Http().send( request ); + Test.stopTest(); + + System.assertEquals( 0, response.getStatusCode(), 'isACalloutMock.when.body.matching, when called without matching body, will return an empty response' ); + } + + /** + * @method isACalloutMock.when.header.setTo + * @case when called with matching header.setTo + * @result will return as specified + */ + @isTest + private static void isACalloutMockWhenHeaderSetTo_whenTheHeaderMatches_returnAsSpecified() { + + new Amoss_Instance() + .isACalloutMock() + .when() + .header( 'Header' ).setTo( 'The header text' ) + .respondsWith() + .statusCode( 200 ); + + Test.startTest(); + HttpRequest request = new HttpRequest(); + request.setHeader( 'Header', 'The header text' ); + HttpResponse response = new Http().send( request ); + Test.stopTest(); + + System.assertEquals( 200, response.getStatusCode(), 'isACalloutMock.when.header.setTo, when the header matches, will return as specified' ); + } + + /** + * @method isACalloutMock.when.header.setTo + * @case when called without matching header + * @result will return an empty response + */ + @isTest + private static void isACalloutMockWhenHeaderSetTo_whenCalledWithoutMatchingHeader_willReturnEmptyResponse() { + + new Amoss_Instance() + .isACalloutMock() + .when() + .header( 'Header' ).setTo( 'The header text' ) + .respondsWith() + .statusCode( 200 ); + + Test.startTest(); + HttpRequest request = new HttpRequest(); + request.setHeader( 'Header', 'different' ); + HttpResponse response = new Http().send( request ); + Test.stopTest(); + + System.assertEquals( 0, response.getStatusCode(), 'isACalloutMock.when.header.setTo, when called without matching header, will return an empty response' ); + } + + + /** + * @method isACalloutMock.when.header.setTo + * @case when called without matching header set at all + * @result will return en empty response + */ + @isTest + private static void isACalloutMockWhenHeaderSetTo_whenCalledWithoutMatchingHeaderSetAtAll_willReturnEmptyResponse() { + + new Amoss_Instance() + .isACalloutMock() + .when() + .header( 'Header' ).setTo( 'The header text' ) + .respondsWith() + .statusCode( 200 ); + + Test.startTest(); + HttpRequest request = new HttpRequest(); + request.setHeader( 'Authorization', 'The header text' ); + HttpResponse response = new Http().send( request ); + Test.stopTest(); + + System.assertEquals( 0, response.getStatusCode(), 'isACalloutMock.when.header.setTo, when called without matching header set at all, will return an empty response' ); + } + + /** + * @method isACalloutMock.when.header.set + * @case when called with matching header.set + * @result will return as specified + */ + @isTest + private static void isACalloutMockWhenHeaderSet_whenTheHeaderIsSet_returnAsSpecified() { + + new Amoss_Instance() + .isACalloutMock() + .when() + .header( 'Header' ).set() + .respondsWith() + .statusCode( 200 ); + + Test.startTest(); + HttpRequest request = new HttpRequest(); + request.setHeader( 'Header', 'The header text' ); + HttpResponse response = new Http().send( request ); + Test.stopTest(); + + System.assertEquals( 200, response.getStatusCode(), 'isACalloutMock.when.header.set, when the header set, will return as specified' ); + } + + /** + * @method isACalloutMock.when.header.set + * @case when called without header + * @result will return an empty response + */ + @isTest + private static void isACalloutMockWhenHeaderSet_whenCalledWithoutHeader_willReturnEmptyResponse() { + + new Amoss_Instance() + .isACalloutMock() + .when() + .header( 'Header' ).set() + .respondsWith() + .statusCode( 200 ); + + Test.startTest(); + HttpRequest request = new HttpRequest(); + HttpResponse response = new Http().send( request ); + Test.stopTest(); + + System.assertEquals( 0, response.getStatusCode(), 'iisACalloutMock.when.header.set, when called without matching header, will return an empty response' ); + } + + /** + * @method isACalloutMock.when.header.containing + * @case when called with matching header.containing + * @result will return as specified + */ + @isTest + private static void isACalloutMockWhenHeaderContaining_whenTheHeaderMatches_returnAsSpecified() { + + new Amoss_Instance() + .isACalloutMock() + .when() + .header( 'Header' ).containing( 'text' ) + .respondsWith() + .statusCode( 200 ); + + Test.startTest(); + HttpRequest request = new HttpRequest(); + request.setHeader( 'Header', 'The header text' ); + HttpResponse response = new Http().send( request ); + Test.stopTest(); + + System.assertEquals( 200, response.getStatusCode(), 'isACalloutMock.when.header.containing, when the header matches, will return as specified' ); + } + + /** + * @method isACalloutMock.when.header.containing + * @case when called without matching header + * @result will return an empty response + */ + @isTest + private static void isACalloutMockWhenHeaderContaining_whenCalledWithoutMatchingHeader_willReturnEmptyResponse() { + + new Amoss_Instance() + .isACalloutMock() + .when() + .header( 'Header' ).containing( 'example' ) + .respondsWith() + .statusCode( 200 ); + + Test.startTest(); + HttpRequest request = new HttpRequest(); + request.setHeader( 'Header', 'different' ); + HttpResponse response = new Http().send( request ); + Test.stopTest(); + + System.assertEquals( 0, response.getStatusCode(), 'isACalloutMock.when.header.containing, when called without matching header, will return an empty response' ); + } + + /** + * @method isACalloutMock.when.header.matching + * @case when called with matching header.matching + * @result will return as specified + */ + @isTest + private static void isACalloutMockWhenHeaderMatching_whenTheHeaderMatches_returnAsSpecified() { + + new Amoss_Instance() + .isACalloutMock() + .when() + .header( 'Header' ).matching( 'T.*t' ) + .respondsWith() + .statusCode( 200 ); + + Test.startTest(); + HttpRequest request = new HttpRequest(); + request.setHeader( 'Header', 'The header text' ); + HttpResponse response = new Http().send( request ); + Test.stopTest(); + + System.assertEquals( 200, response.getStatusCode(), 'isACalloutMock.when.header.matching, when the header matches, will return as specified' ); + } + + /** + * @method isACalloutMock.when.header.matching + * @case when called without matching header + * @result will return an empty response + */ + @isTest + private static void isACalloutMockWhenHeaderMatching_whenCalledWithoutMatchingHeader_willReturnEmptyResponse() { + + new Amoss_Instance() + .isACalloutMock() + .when() + .header( 'Header' ).matching( 'The.*text' ) + .respondsWith() + .statusCode( 200 ); + + Test.startTest(); + HttpRequest request = new HttpRequest(); + request.setHeader( 'Header', 'different' ); + HttpResponse response = new Http().send( request ); + Test.stopTest(); + + System.assertEquals( 0, response.getStatusCode(), 'isACalloutMock.when.header.matching, when called without matching header, will return an empty response' ); + } + + /** + * @method isACalloutMock.when.compressed + * @case when called with compressed request + * @result will return as specified + */ + @isTest + private static void isACalloutMockWhenCompressed_whenCompressed_returnAsSpecified() { + + new Amoss_Instance() + .isACalloutMock() + .when() + .compressed() + .respondsWith() + .statusCode( 200 ); + + Test.startTest(); + HttpRequest request = new HttpRequest(); + request.setCompressed( true ); + HttpResponse response = new Http().send( request ); + Test.stopTest(); + + System.assertEquals( 200, response.getStatusCode(), 'isACalloutMock.when.compressed, when the request is compressed, will return as specified' ); + } + + /** + * @method isACalloutMock.when.compressed + * @case when called with a non compressed request + * @result will return an empty response + */ + @isTest + private static void isACalloutMockWhenCompressed_whenNotCompressed_returnEmptyResponse() { + + new Amoss_Instance() + .isACalloutMock() + .when() + .compressed() + .respondsWith() + .statusCode( 200 ); + + Test.startTest(); + HttpRequest request = new HttpRequest(); + request.setCompressed( false ); + HttpResponse response = new Http().send( request ); + Test.stopTest(); + + System.assertEquals( 0, response.getStatusCode(), 'isACalloutMock.when.compressed, when called with request that is not compressed, will return an empty response' ); + } + + /** + * @method isACalloutMock.when.compressed + * @case when called with a request with compression not set + * @result will return an empty response + */ + @isTest + private static void isACalloutMockWhenCompressed_whenCompressedNotSet_willReturnEmptyResponse() { + + new Amoss_Instance() + .isACalloutMock() + .when() + .compressed() + .respondsWith() + .statusCode( 200 ); + + Test.startTest(); + HttpRequest request = new HttpRequest(); + HttpResponse response = new Http().send( request ); + Test.stopTest(); + + System.assertEquals( 0, response.getStatusCode(), 'isACalloutMock.when.compressed, when called with request with compressed not set, will return an empty response' ); + } + + /** + * @method isACalloutMock.when.notCompressed + * @case when called with non compressed request + * @result will return as specified + */ + @isTest + private static void isACalloutMockWhenNotCompressed_whenNotCompressed_returnAsSpecified() { + + new Amoss_Instance() + .isACalloutMock() + .when() + .notCompressed() + .respondsWith() + .statusCode( 200 ); + + Test.startTest(); + HttpRequest request = new HttpRequest(); + request.setCompressed( false ); + HttpResponse response = new Http().send( request ); + Test.stopTest(); + + System.assertEquals( 200, response.getStatusCode(), 'isACalloutMock.when.notCompressed, when the request is not compressed, will return as specified' ); + } + + /** + * @method isACalloutMock.when.notCompressed + * @case when called with a compressed request + * @result will return an empty response + */ + @isTest + private static void isACalloutMockWhenNotCompressed_whenCompressed_returnEmptyResponse() { + + new Amoss_Instance() + .isACalloutMock() + .when() + .notCompressed() + .respondsWith() + .statusCode( 200 ); + + Test.startTest(); + HttpRequest request = new HttpRequest(); + request.setCompressed( true ); + HttpResponse response = new Http().send( request ); + Test.stopTest(); + + System.assertEquals( 0, response.getStatusCode(), 'isACalloutMock.when.notCompressed, when called with request that is compressed, will return an empty response' ); + } + + /** + * @method isACalloutMock.when.notCompressed + * @case when called with request with compressed not set + * @result will return as specified + */ + @isTest + private static void isACalloutMockWhenNotCompressed_whenCompressedNotSet_returnAsSpecified() { + + new Amoss_Instance() + .isACalloutMock() + .when() + .notCompressed() + .respondsWith() + .statusCode( 200 ); + + Test.startTest(); + HttpRequest request = new HttpRequest(); + HttpResponse response = new Http().send( request ); + Test.stopTest(); + + System.assertEquals( 200, response.getStatusCode(), 'isACalloutMock.when.notCompressed, when the request does not have compressed set, will return as specified' ); + } + + /** + * @method isACalloutMock.when + * @case when multiple properties set, and the call matches + * @result will return as specified + */ + @isTest + private static void isACalloutMockWhen_multiplePropertiesAndCallMatches_willReturnAsSpecified() { + + new Amoss_Instance() + .isACalloutMock() + .when() + .endpoint().setTo( 'end' ) + .body().setTo( 'The Body' ) + .header( 'Header' ).setTo( 'The Header' ) + .compressed() + .respondsWith() + .statusCode( 200 ); + + Test.startTest(); + HttpRequest request = new HttpRequest(); + request.setEndpoint( 'end' ); + request.setBody( 'The Body' ); + request.setHeader( 'Header', 'The Header' ); + request.setCompressed( true ); + HttpResponse response = new Http().send( request ); + Test.stopTest(); + + System.assertEquals( 200, response.getStatusCode(), 'isACalloutMock.when, when multiple properties set, and the call matches, will return as specified' ); + } + + /** + * @method isACalloutMock.when + * @case when multiple properties set, and the call does not match + * @result will return an empty response + */ + @isTest + private static void isACalloutMockWhen_multiplePropertiesAndCallDoesNotMatch_returnEmptyResponse() { + + new Amoss_Instance() + .isACalloutMock() + .when() + .method( 'GET' ) + .endpoint().setTo( 'end' ) + .body().setTo( 'The Body' ) + .header( 'Header' ).setTo( 'The Header' ) + .compressed() + .respondsWith() + .statusCode( 200 ); + + Test.startTest(); + HttpRequest request = new HttpRequest(); + request.setMethod( 'GET' ); + request.setEndpoint( 'end' ); + request.setBody( 'Different Body' ); + request.setHeader( 'Header', 'The Header' ); + request.setCompressed( true ); + HttpResponse response = new Http().send( request ); + Test.stopTest(); + + System.assertEquals( 0, response.getStatusCode(), 'isACalloutMock.when, when multiple properties set, and the call does not match, will return an empty response' ); + } + + /** + * @method isACalloutMock.when + * @case when multiple definitions set, and call matches one + * @result will return as specified + */ + @isTest + private static void isACalloutMockWhen_multipleDefinitionsAndCallMatchesOne_returnsSpecified() { + + new Amoss_Instance() + .isACalloutMock() + .when() + .endpoint().setTo( 'return200' ) + .respondsWith() + .statusCode( 200 ) + .also().when() + .endpoint().setTo( 'return404' ) + .respondsWith() + .statusCode( 404 ) + .also().when() + .endpoint().setTo( 'return500' ) + .respondsWith() + .statusCode( 500 ); + + Test.startTest(); + HttpRequest request = new HttpRequest(); + request.setEndpoint( 'return404' ); + HttpResponse response = new Http().send( request ); + Test.stopTest(); + + System.assertEquals( 404, response.getStatusCode(), 'isACalloutMock.when, when multiple definitions set, and a call matches one, will return as specified' ); + } + + /** + * @method isACalloutMock.when + * @case when multiple definitions set, and multiple calls match + * @result will return as specified + */ + @isTest + private static void isACalloutMockWhen_multipleDefinitionsAndMultipleCallsMatch_returnsSpecified() { + + new Amoss_Instance() + .isACalloutMock() + .when() + .endpoint().setTo( 'return200' ) + .respondsWith() + .statusCode( 200 ) + .also().when() + .endpoint().setTo( 'return404' ) + .respondsWith() + .statusCode( 404 ) + .also().when() + .endpoint().setTo( 'return500' ) + .respondsWith() + .statusCode( 500 ); + + Test.startTest(); + HttpRequest request = new HttpRequest(); + + request.setEndpoint( 'return404' ); + HttpResponse response404 = new Http().send( request ); + + request.setEndpoint( 'return500' ); + HttpResponse response500 = new Http().send( request ); + + request.setEndpoint( 'return200' ); + HttpResponse response200 = new Http().send( request ); + + Test.stopTest(); + + System.assertEquals( 404, response404.getStatusCode(), 'isACalloutMock.when, when multiple definitions set, and multiple calls made, will return as specified - 0' ); + System.assertEquals( 500, response500.getStatusCode(), 'isACalloutMock.when, when multiple definitions set, and multiple calls made, will return as specified - 1' ); + System.assertEquals( 200, response200.getStatusCode(), 'isACalloutMock.when, when multiple definitions set, and multiple calls made, will return as specified - 2' ); + } + + /** + * @method isACalloutMock.when.verifiedBy (Amoss_ValueVerifier) + * @case when called with request that matches the verification + * @result will return as specified + */ + @isTest + private static void isACalloutMockWhenVerifiedByValueVerifier_whenMatchesVerification_returnAsSpecified() { + + HttpRequest request = new HttpRequest(); + + Amoss_Instance customVerifierController = new Amoss_Instance( Amoss_ValueVerifier.class ); + customVerifierController + .expects( 'verify' ) + .withParameter().setTo( request ); + + Amoss_ValueVerifier customVerifier = (Amoss_ValueVerifier)customVerifierController.getDouble(); + + new Amoss_Instance() + .isACalloutMock() + .byDefault() + .respondsWith() + .statusCode( 404 ) + .also().when() + .verifiedBy( customVerifier ) + .respondsWith() + .statusCode( 200 ); + + Test.startTest(); + HttpResponse response = new Http().send( request ); + Test.stopTest(); + + customVerifierController.verify(); + + System.assertEquals( 200, response.getStatusCode(), 'isACalloutMock.when.verifiedBy (Amoss_ValueVerifier), when the verifier matches, will return as specified' ); + } + + /** + * @method isACalloutMock.when.verifiedBy (Amoss_ValueVerifier) + * @case when called with request that does not match the verification + * @result will not return that specification + */ + @isTest + private static void isACalloutMockWhenVerifiedByValueVerifier_whenDoesNotMatch_returnOther() { + + HttpRequest request = new HttpRequest(); + + Amoss_Instance customVerifierController = new Amoss_Instance( Amoss_ValueVerifier.class ); + customVerifierController + .expects( 'verify' ) + .withParameter().setTo( request ) + .throws( new Amoss_Instance.Amoss_EqualsAssertionFailureException() + .setExpected( 'expectedValue' ) + .setActual( 'actualValue' ) + .setAssertionMessage( 'Not the same value' ) ); + + Amoss_ValueVerifier customVerifier = (Amoss_ValueVerifier)customVerifierController.getDouble(); + + new Amoss_Instance() + .isACalloutMock() + .byDefault() + .respondsWith() + .statusCode( 404 ) + .also().when() + .verifiedBy( customVerifier ) + .respondsWith() + .statusCode( 200 ); + + Test.startTest(); + HttpResponse response = new Http().send( request ); + Test.stopTest(); + + customVerifierController.verify(); + + System.assertEquals( 404, response.getStatusCode(), 'isACalloutMock.when.verifiedBy (Amoss_ValueVerifier), when the verifier does not match, will not return what was specified for that call' ); + } + + + /** + * @method isACalloutMock.when.verifiedBy (Amoss_HttpRequestVerifier) + * @case when called with request that matches the verification + * @result will return as specified + */ + @isTest + private static void isACalloutMockWhenVerifiedByHttpRequestVerifier_whenMatchesVerification_returnAsSpecified() { + + HttpRequest request = new HttpRequest(); + + Amoss_Instance customVerifierController = new Amoss_Instance( Amoss_HttpRequestVerifier.class ); + customVerifierController + .expects( 'verify' ) + .withParameter().setTo( request ); + + Amoss_HttpRequestVerifier customVerifier = (Amoss_HttpRequestVerifier)customVerifierController.getDouble(); + + new Amoss_Instance() + .isACalloutMock() + .byDefault() + .respondsWith() + .statusCode( 404 ) + .also().when() + .verifiedBy( customVerifier ) + .respondsWith() + .statusCode( 200 ); + + Test.startTest(); + HttpResponse response = new Http().send( request ); + Test.stopTest(); + + customVerifierController.verify(); + + System.assertEquals( 200, response.getStatusCode(), 'isACalloutMock.when.verifiedBy (Amoss_HttpRequestVerifier), when the verifier matches, will return as specified' ); + } + + /** + * @method isACalloutMock.when.verifiedBy (Amoss_HttpRequestVerifier) + * @case when called with request that does not match the verification + * @result will not return that specification + */ + @isTest + private static void isACalloutMockWhenVerifiedByHttpRequestVerifier_whenDoesNotMatch_returnOther() { + + HttpRequest request = new HttpRequest(); + + Amoss_Instance customVerifierController = new Amoss_Instance( Amoss_HttpRequestVerifier.class ); + customVerifierController + .expects( 'verify' ) + .withParameter().setTo( request ) + .throws( new Amoss_Instance.Amoss_EqualsAssertionFailureException() + .setExpected( 'expectedValue' ) + .setActual( 'actualValue' ) + .setAssertionMessage( 'Not the same value' ) ); + + Amoss_HttpRequestVerifier customVerifier = (Amoss_HttpRequestVerifier)customVerifierController.getDouble(); + + new Amoss_Instance() + .isACalloutMock() + .byDefault() + .respondsWith() + .statusCode( 404 ) + .also().when() + .verifiedBy( customVerifier ) + .respondsWith() + .statusCode( 200 ); + + Test.startTest(); + HttpResponse response = new Http().send( request ); + Test.stopTest(); + + customVerifierController.verify(); + + System.assertEquals( 404, response.getStatusCode(), 'isACalloutMock.when.verifiedBy (Amoss_HttpRequestVerifier), when the verifier does not match, will not return what was specified for that call' ); + } + + /** + * @method isACalloutMock.expectsNoCalls + * @case when a callout is made is called + * @result will fail the test + */ + @isTest + private static void isACalloutMockExpectsNoCalls_whenACalloutIsMade_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance mockHttpController = new Amoss_Instance(); + mockHttpController.setAsserts( assertsDouble ); + + mockHttpController + .isACalloutMock() + .expectsNoCalls(); + + Test.startTest(); + try { + HttpResponse response = new Http().send( new HttpRequest() ); + } catch( TestException e ) {} + Test.stopTest(); + + System.assertEquals( '' , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 0 ), 'isACalloutMock.expectsNoCalls, when a callout is made, will fail by calling assertEquals with an empty string as expected' ); + + String expectedMethodCall = 'respond(System.HttpRequest[Endpoint=null, Method=null])'; + System.assertEquals( expectedMethodCall, assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 1 ), 'isACalloutMock.expectsNoCalls, when a callout is made, will fail by calling assertEquals with the method called as actual' ); + + String expectedAssertion = 'Did not expect any HTTP Callouts to be made'; + System.assertEquals( expectedAssertion, assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 2 ), 'isACalloutMock.expectsNoCalls, when a callout is made, will fail by calling assertEquals with an assertion message that clearly describes the issue' ); + } + + /** + * @method isACalloutMock.expects + * @case when request made that matches + * @result will return the specified + */ + @isTest + private static void isACalloutMockExpectsVerify_whenMatches_returns() { + + Amoss_Instance customVerifierController = new Amoss_Instance(); + + customVerifierController + .isACalloutMock() + .expects() + .method( 'GET' ) + .respondsWith() + .statusCode( 200 ); + + Test.startTest(); + HttpRequest request = new HttpRequest(); + request.setMethod( 'GET' ); + HttpResponse response = new Http().send( request ); + Test.stopTest(); + + customVerifierController.verify(); + + System.assertEquals( 200, response.getStatusCode(), 'isACalloutMock.expects.verify, when a call matches, returns' ); + } + + /** + * @method isACalloutMock.expects + * @case when request made that does not match + * @result will fail the test + */ + @isTest + private static void isACalloutMockExpects_whenDoesNoMatch_fails() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance customVerifierController = new Amoss_Instance(); + + customVerifierController + .setAsserts( assertsDouble ) + .isACalloutMock() + .expects() + .method( 'GET' ) + .respondsWith() + .statusCode( 200 ); + + Test.startTest(); + HttpRequest request = new HttpRequest(); + request.setMethod( 'POST' ); + + try { + HttpResponse response = new Http().send( request ); + } catch ( Exception e ) {} // NOPMD: catching the exception thrown by the mock assert + Test.stopTest(); + + checkForWrongParameterNamedAssertion( assertsDoubleController, 'respond', 'request', 'GET', 'POST', + 'isACalloutMock.expects, when a request made that does not match' ); + } + + /** + * @method isACalloutMock.expects.expects.verify + * @case when requests made in line with the expectations + * @result will pass + */ + @isTest + private static void isACalloutMockExpectsExpectsVerify_whenMultipleCallsAndMatches_passes() { + + Amoss_Instance customVerifierController = new Amoss_Instance(); + + customVerifierController + .isACalloutMock() + .expects() + .method( 'GET' ) + .respondsWith() + .statusCode( 200 ) + .then().expects() + .method( 'POST' ) + .respondsWith() + .statusCode( 404 ); + + Test.startTest(); + HttpRequest request1 = new HttpRequest(); + request1.setMethod( 'GET' ); + HttpResponse response1 = new Http().send( request1 ); + + HttpRequest request2 = new HttpRequest(); + request2.setMethod( 'POST' ); + HttpResponse response2 = new Http().send( request2 ); + Test.stopTest(); + + customVerifierController.verify(); + + System.assertEquals( 200, response1.getStatusCode(), 'isACalloutMock.expects.verify, when the calls match, returns - call 1' ); + System.assertEquals( 404, response2.getStatusCode(), 'isACalloutMock.expects.verify, when the calls match, returns - call 2' ); + } + + /** + * @method isACalloutMock.expects.expects.verify + * @case when only one call made + * @result will fail + */ + @isTest + private static void isACalloutMockExpectsExpectsVerify_whenOneCallMade_fail() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance customVerifierController = new Amoss_Instance(); + + customVerifierController + .setAsserts( assertsDouble ) + .isACalloutMock() + .expects() + .method( 'GET' ) + .respondsWith() + .statusCode( 200 ) + .then().expects() + .method( 'POST' ) + .respondsWith() + .statusCode( 404 ); + + Test.startTest(); + + HttpRequest request = new HttpRequest(); + request.setMethod( 'GET' ); + HttpResponse response = new Http().send( request ); + + try { + customVerifierController.verify(); + } catch ( Exception e ) {} // NOPMD: catching the exception thrown by the mock assert + + Test.stopTest(); + + String expectedAssertion = 'Expected more HTTP Callouts to be made'; + String expectedCall = 'respond( request => HttpRequest with the method POST )'; + + System.assertEquals( new List() , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 0 ), 'isACalloutMock.expects.expects.verify, when few calls were made than specified, will fail by calling assertEquals with an expected empty call stack' ); + System.assertEquals( new List{ expectedCall }, assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 1 ), 'isACalloutMock.expects.expects.verify, when few calls were made than specified, will fail by calling assertEquals with a call stack description that contains a message stating the calls that were expected' ); + System.assertEquals( expectedAssertion , assertsDoubleController.get().latestCallOf( 'assertEquals' ).parameter( 2 ), 'isACalloutMock.expects.expects.verify, when few calls were made than specified, will fail by calling assertEquals with an assertion message that clearly describes the issue' ); + } + + /** + * @method isACalloutMock.allows + * @case when multple requests made that match + * @result will return the specified + */ + @isTest + private static void isACalloutMockAllows_whenMultipleCallsThatMatch_returns() { + + Amoss_Instance customVerifierController = new Amoss_Instance(); + + customVerifierController + .isACalloutMock() + .allows() + .method( 'GET' ) + .respondsWith() + .statusCode( 200 ); + + Test.startTest(); + HttpRequest request = new HttpRequest(); + request.setMethod( 'GET' ); + HttpResponse response1 = new Http().send( request ); + HttpResponse response2 = new Http().send( request ); + HttpResponse response3 = new Http().send( request ); + Test.stopTest(); + + System.assertEquals( 200, response1.getStatusCode(), 'isACalloutMock.allows, when multiple calls made that match, returns (1)' ); + System.assertEquals( 200, response2.getStatusCode(), 'isACalloutMock.allows, when multiple calls made that match, returns (2)' ); + System.assertEquals( 200, response3.getStatusCode(), 'isACalloutMock.allows, when multiple calls made that match, returns (3)' ); + } + + /** + * @method isACalloutMock.allows + * @case when request made that does not match + * @result will fail the test + */ + @isTest + private static void isACalloutMockAllows_whenDoesNotMatch_fails() { + + Amoss_Instance assertsDoubleController = buildMockAssertionController(); + Amoss_Asserts assertsDouble = (Amoss_Asserts)assertsDoubleController.getDouble(); + + Amoss_Instance customVerifierController = new Amoss_Instance(); + + customVerifierController + .setAsserts( assertsDouble ) + .isACalloutMock() + .allows() + .method( 'GET' ) + .respondsWith() + .statusCode( 200 ); + + Test.startTest(); + HttpRequest request = new HttpRequest(); + request.setMethod( 'POST' ); + + try { + HttpResponse response = new Http().send( request ); + } catch ( Exception e ) {} // NOPMD: catching the exception thrown by the mock assert + Test.stopTest(); + + System.assertEquals( false, assertsDoubleController.get().latestCallOf( 'assert' ).parameter( 0 ), 'isACalloutMock.allows, when request made that does not match, will fail by calling assertEquals with an assertion message that clearly describes the issue' ); + Amoss_Asserts.assertContains( HTTP_CALLOUT_MOCK_UNEXPECTED_EXCEPTION_TEXT, assertsDoubleController.get().latestCallOf( 'assert' ).parameter( 1 ), 'isACalloutMock.allows, when request made that does not match, will fail by calling assertEquals with an assertion message that clearly describes the issue' ); + Amoss_Asserts.assertContains( 'respond(System.HttpRequest[Endpoint=null, Method=POST])', assertsDoubleController.get().latestCallOf( 'assert' ).parameter( 1 ), 'isACalloutMock.allows, when request made that does not match, will fail by calling assertEquals with an assertion message that clearly describes the issue' ); + } + + class InnerClassToDouble { + public String publicMethod( String parameterOne ) { + return 'returnString'; + } + } + + private static Amoss_Instance buildMockAssertionController() { + + Amoss_Instance assertsDoubleController = new Amoss_Instance( Amoss_Asserts.class ); + assertsDoubleController + .allows() + .method( 'assertEquals' ) + .handledBy( new MockAssertEqualsHandler() ) + .also().allows() + .method( 'assert' ) + .withParameter( true ) + .thenAnyParameter() + .also().allows() + .method( 'assert' ) + .withParameter( false ) + .thenAnyParameter() + .handledBy( new MockAssertHandler() ); + return assertsDoubleController; + } + + class MockAssertEqualsHandler implements Amoss_MethodHandler { + public Object handleMethodCall( List parameters ) { + if ( parameters[0] != parameters[1] ) { + throw new TestException( 'Unequal expected and actual in an assertion would normally halt the test, so we will too - ' + String.valueOf( parameters[0] ) + ' != ' + String.valueOf( parameters[1] ) + ', for: ' + String.valueOf( parameters[2] ) ); + } + return null; + } + } + + class MockAssertHandler implements Amoss_MethodHandler { + public Object handleMethodCall( List parameters ) { + if ( parameters[0] == false ) { + throw new TestException( 'False assertion would normally halt the test, so we will too, for: ' + String.valueOf( parameters[1] ) ); + } + return null; + } + } + + private static void checkForWrongParameterInPositionAssertion( Amoss_Instance assertsDoubleController, String method, Integer parameterPosition, Object expected, Object actual, String message ) { + checkForWrongParameterInPositionAssertion( assertsDoubleController, 'The value is not what was expected', method, parameterPosition, expected, actual, message ); + } + + private static void checkForWrongParameterFieldsInPositionAssertion( Amoss_Instance assertsDoubleController, String method, Integer parameterPosition, Object expected, Object actual, String message ) { + checkForWrongParameterInPositionAssertion( assertsDoubleController, 'The following fields were not set as expected', method, parameterPosition, expected, actual, message ); + } + + private static void checkForWrongParameterInPositionInstanceAssertion( Amoss_Instance assertsDoubleController, String method, Integer parameterPosition, Object expected, Object actual, String message ) { + checkForWrongParameterInPositionAssertion( assertsDoubleController, 'The value is the same, but not the same instance as was expected', method, parameterPosition, expected, actual, message ); + } + + private static void checkForNullSObjectInPositionInstanceAssertion( Amoss_Instance assertsDoubleController, String method, Integer parameterPosition, Object expected, Object actual, String message ) { + checkForWrongParameterInPositionAssertion( assertsDoubleController, 'Expected an sObject, and got NULL', method, parameterPosition, expected, actual, message ); + } + + private static void checkForWrongParameterNamedAssertion( Amoss_Instance assertsDoubleController, String method, String parameterName, Object expected, Object actual, String message ) { + checkForWrongParameterNamedAssertion( assertsDoubleController, 'The value is not what was expected', method, parameterName, expected, actual, message ); + } + + private static void checkForWrongParameterNamedInstanceAssertion( Amoss_Instance assertsDoubleController, String method, String parameterName, Object expected, Object actual, String message ) { + checkForWrongParameterNamedAssertion( assertsDoubleController, 'The value is the same, but not the same instance as was expected', method, parameterName, expected, actual, message ); + } + + private static void checkForWrongParameterInPositionAssertion( Amoss_Instance assertsDoubleController, String expectedDetailAssertion, String method, Integer parameterPosition, String message ) { + + System.assertEquals( false, assertsDoubleController.get().latestCallOf( 'assert' ).parameter( 0 ), message + ', will fail, by calling assert with false' ); + + String expectedAssertion = method + ' had a problem with the parameter value in position ' + String.valueOf( parameterPosition ); + String actualAssertion = (String)assertsDoubleController.get().latestCallOf( 'assert' ).parameter( 1 ); + + Amoss_Asserts.assertContains( expectedAssertion, actualAssertion, message + ', will fail, by calling assert with an assertion message that clearly describes the issue' ); + Amoss_Asserts.assertContains( expectedDetailAssertion, actualAssertion, message + ', will fail, by calling assert with an assertion message that clearly describes the issue in detail' ); + } + + // TODO: tidy the code duplication + private static void checkForWrongParameterInPositionAssertion( Amoss_Instance assertsDoubleController, String expectedDetailAssertion, String method, Integer parameterPosition, Object expected, Object actual, String message ) { + + checkForWrongParameterInPositionAssertion( assertsDoubleController, expectedDetailAssertion, method, parameterPosition, message ); + + String actualAssertion = (String)assertsDoubleController.get().latestCallOf( 'assert' ).parameter( 1 ); + + Amoss_Asserts.assertContains( 'Expected: ' + expected, actualAssertion, message + ', will fail, by calling assert with an assertion message that clearly stating the expected parameter value' ); + Amoss_Asserts.assertContains( 'Actual: ' + actual , actualAssertion, message + ', will fail, by calling assert with an assertion message that clearly stating the actual parameter value' ); + } + + private static void checkForWrongParameterNamedAssertion( Amoss_Instance assertsDoubleController, String expectedDetailAssertion, String method, String parameterName, Object expected, Object actual, String message ) { + + String expectedAssertion = method + ' had a problem with the parameter value for "' + String.valueOf( parameterName ) + '"'; + checkForEqualsAssertionFailure( assertsDoubleController, expectedDetailAssertion, expectedAssertion, expected, actual, message ); + } + + private static void checkForEqualsAssertionFailure( Amoss_Instance assertsDoubleController, String expectedDetailAssertion, String expectedAssertion, Object expected, Object actual, String message ) { + + System.assertEquals( false, assertsDoubleController.get().latestCallOf( 'assert' ).parameter( 0 ), message + ', will fail, by calling assert with false' ); + + String actualAssertion = (String)assertsDoubleController.get().latestCallOf( 'assert' ).parameter( 1 ); + + Amoss_Asserts.assertContains( expectedAssertion, actualAssertion, message + ', will fail, by calling assert with an assertion message that clearly describes the issue' ); + Amoss_Asserts.assertContains( expectedDetailAssertion, actualAssertion, message + ', will fail, by calling assert with an assertion message that clearly describes the issue in detail' ); + Amoss_Asserts.assertContains( 'Expected: ' + expected, actualAssertion, message + ', will fail, by calling assert with an assertion message that clearly stating the expected parameter value' ); + Amoss_Asserts.assertContains( 'Actual: ' + actual , actualAssertion, message + ', will fail, by calling assert with an assertion message that clearly stating the actual parameter value' ); + } + + @isTest + private static void test() { + + Amoss_Instance mockHttp = new Amoss_Instance(); + mockHttp + .isACalloutMock() + .expects() + .method( 'GET' ) + .endpoint().containing( 'accounts' ) + .header( 'Authorization' ).set() + .notCompressed() + .respondsWith() + .statusCode( 200 ) + .status( 'Complete' ) + .body( '[{"Name":"sForceTest1"}]' ) + .then().expects() + .method( 'GET' ) + .endpoint( 'http://api.example.com/accounts' ) + .header( 'Authorization' ).setTo( 'value' ) + .respondsWith() + .statusCode( 500 ) + .status( 'Error' ) + .body( '[{"Name":"sForceTest1"}]' ); + + HttpRequest request = new HttpRequest(); + request.setMethod( 'GET' ); + request.setEndpoint( 'http://api.example.com/accounts' ); + request.setHeader( 'Authorization', 'value' ); + + Http transport = new Http(); + HttpResponse response = transport.send( request ); + + System.assertEquals( 200, response.getStatusCode() ); + System.assertEquals( 'Complete', response.getStatus() ); + + response = transport.send( request ); + + System.assertEquals( 500, response.getStatusCode() ); + System.assertEquals( 'Error', response.getStatus() ); + + } + +} \ No newline at end of file diff --git a/framework/default/amoss_tests/default/classes/AmossTest_InstanceTest.cls-meta.xml b/framework/default/amoss_tests/default/classes/AmossTest_InstanceTest.cls-meta.xml new file mode 100644 index 00000000000..dd61d1f917e --- /dev/null +++ b/framework/default/amoss_tests/default/classes/AmossTest_InstanceTest.cls-meta.xml @@ -0,0 +1,5 @@ + + + 52.0 + Active + diff --git a/framework/default/amoss_tests/default/classes/AmossTest_InterfaceToDouble.cls b/framework/default/amoss_tests/default/classes/AmossTest_InterfaceToDouble.cls new file mode 100644 index 00000000000..9d53bf606c8 --- /dev/null +++ b/framework/default/amoss_tests/default/classes/AmossTest_InterfaceToDouble.cls @@ -0,0 +1,35 @@ +/* +MIT License + +Copyright (c) 2020 Robert Baillie + +https://github.com/bobalicious/amoss + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +/** + * + * Amoss test interface that exists purely so that AmossTest_InstanceTest has a guaranteed interface + * that it can create a Test Double for. + * + */ +public interface AmossTest_InterfaceToDouble { + String methodUnderDouble( String parameter1, Integer parameter2 ); +} \ No newline at end of file diff --git a/framework/default/amoss_tests/default/classes/AmossTest_InterfaceToDouble.cls-meta.xml b/framework/default/amoss_tests/default/classes/AmossTest_InterfaceToDouble.cls-meta.xml new file mode 100644 index 00000000000..dd61d1f917e --- /dev/null +++ b/framework/default/amoss_tests/default/classes/AmossTest_InterfaceToDouble.cls-meta.xml @@ -0,0 +1,5 @@ + + + 52.0 + Active + diff --git a/framework/default/amoss_tests/default/classes/AmossTest_SingletonToDouble.cls b/framework/default/amoss_tests/default/classes/AmossTest_SingletonToDouble.cls new file mode 100644 index 00000000000..4c28782947d --- /dev/null +++ b/framework/default/amoss_tests/default/classes/AmossTest_SingletonToDouble.cls @@ -0,0 +1,53 @@ +/* +MIT License + +Copyright (c) 2020 Robert Baillie + +https://github.com/bobalicious/amoss + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +/** + * + * Amoss test class that exists purely so that AmossTest_InstanceTest has a guaranteed class + * with no public constructors that it can attempt to create a Test Double for. + * + * It follows a standard Singleton pattern, just to illustrate the kind of class that is limited + * by the implementation of StubProvider + * + * If StubProvider supported the stubbing of internal classes, then this would be part of + * Amos_InstanceTest. Until it does, it will need to stay here. + * + */ +@isTest +public with sharing class AmossTest_SingletonToDouble { + + private static AmossTest_SingletonToDouble instance; + + private AmossTest_SingletonToDouble() { + } + + public static AmossTest_SingletonToDouble getInstance() { + if ( AmossTest_SingletonToDouble.instance == null ) { + AmossTest_SingletonToDouble.instance = new AmossTest_SingletonToDouble(); + } + return AmossTest_SingletonToDouble.instance; + } +} diff --git a/framework/default/amoss_tests/default/classes/AmossTest_SingletonToDouble.cls-meta.xml b/framework/default/amoss_tests/default/classes/AmossTest_SingletonToDouble.cls-meta.xml new file mode 100644 index 00000000000..dd61d1f917e --- /dev/null +++ b/framework/default/amoss_tests/default/classes/AmossTest_SingletonToDouble.cls-meta.xml @@ -0,0 +1,5 @@ + + + 52.0 + Active + diff --git a/framework/default/fflib-apex-extensions/default/classes/criteria/fflib_Comparator.cls b/framework/default/fflib-apex-extensions/default/classes/criteria/fflib_Comparator.cls new file mode 100644 index 00000000000..f37c7cd55da --- /dev/null +++ b/framework/default/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