Skip to content

Commit

Permalink
Http Mock Router 1.1
Browse files Browse the repository at this point in the history
  • Loading branch information
pkozuchowski committed Nov 10, 2024
1 parent 059eeeb commit 9fc3e73
Show file tree
Hide file tree
Showing 37 changed files with 1,046 additions and 71 deletions.
3 changes: 1 addition & 2 deletions .forceignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,4 @@ package.xml
**/profiles/
# LWC Jes
**/__tests__/**
triggerHandler.md
force-app/main/**
triggerHandler.md
1 change: 1 addition & 0 deletions docs/Collection.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ sf project deploy start \
-o sfdxOrg
```


---
# Documentation

Expand Down
93 changes: 70 additions & 23 deletions docs/HttpCalloutMockRouter.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,28 @@
*Configuration-driven, endpoint pattern-based router for Http Mocks.*

[Source](https://github.com/pkozuchowski/Apex-Opensource-Library/tree/master/force-app/commons/httpMocks)
[Install In Sandbox](https://test.salesforce.com/packaging/installPackage.apexp?p0=04t08000000UK7EAAW)
[Install In Production](https://login.salesforce.com/packaging/installPackage.apexp?p0=04t08000000UK7EAAW)
[Install In Sandbox](https://test.salesforce.com/packaging/installPackage.apexp?p0=04tJ6000000LVAGIA4)
[Install In Production](https://login.salesforce.com/packaging/installPackage.apexp?p0=04tJ6000000LVAGIA4)

```bash
sf project deploy start -d force-app/commons/httpMocks -o sfdxOrg
```

---
# Documentation
HTTP Callout Router is a configuration-driven framework for mocking Http Callouts that matches mocks by endpoint pattern and HTTP method.
Http Callout Mock Router is a configuration-driven framework for mocking Http Callouts, that matches mocks by endpoint pattern and HTTP method.
Let's consider the following configuration for Salesforce REST API:

#### Variables
#### Mocks
![http-router-example.png](/img/http-router-example.png)

| DeveloperName | Pattern |
|---------------|----------------------|
| sfId | ([0-9a-zA-Z]{15,18}) |
| sObjectType | \w+ |
Reusable patterns visible in curly braces are defined in **Http Callout Mock Variables** as follows:

#### Mocks
| Developer Name | Default | Methods | Endpoint | Status Code | Status | Response | Static Resource | Apex Class |
|-----------------------------------|---------|------------|------------------------------------------------------|-------------|--------------|------------------------------------------------------------------------|------------------|-------------------------------------|
| SampleAPI_Get_Beers_Ale_OK || GET | callout:SampleAPI/beers/ale | 200 | OK | [{"price":"$16.99","name":"Founders All Day IPA","rating":4,"id":1}] | | |
| SampleAPI_Update_Beers_Ale_OK || POST,Patch | callout:SampleAPI/beers/ale | 200 | OK | | | |
| SampleAPI_Common_401_Unauthorized || GET | callout:SampleAPI/.* | 401 | Unauthorized | [{"message":"Invalid access token. Please pass a valid access token"}] | | |
| SF_REST_Query || GET | callout:SfRestAPI/query/.* | 200 | OK | {"records":[{"Name":"Test Account"}]} | | |
| SF_REST_Query_Empty || GET | callout:SfRestAPI/query/.* | 200 | OK | {"records":[]} | | |
| SF_REST_SObject_Describe || GET | callout:SfRestAPI/sobjects/{{sObjectType}}/describe/ | 200 | OK | | | SalesforceMocks.SObjectDescribeMock |
| SF_REST_SObject_Row_Get_Account || GET | callout:SfRestAPI/sobjects/Account/{{sfId}} | 200 | OK | | Mocks_SF_Account | |
| DeveloperName | Pattern |
|---------------|----------------------------------------|
| recordId | ^[a-zA-Z0-9]{15}([a-zA-Z0-9]{3})?$ |
| datetime | ^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$ |
| | |

Router loads default (*Default__c=true*) configuration and substitutes all endpoint variables (*{{variable}}*) with their values defined in
HttpCalloutMockVariable__mdt custom metadata.
Expand Down Expand Up @@ -60,9 +53,9 @@ tests uses OrgMocks class.
1. Router respond method checks mocks from custom metadata and any other mocks defined in code.
1. Each mock is checked for handled HTTP Methods, and if the endpoint matches with the request.
1. The first mock that matches response will have its response returned according to the settings.
- If StaticResource__c field is provided, mock will respond with it's content
- If ApexClass__c is provided, mock creates instance of this class and returns it. The class should implement HttpCalloutMock interface.
- If Response__c field is provided, it's returned as callout response body.
- If StaticResource__c field is provided, mock will respond with its content
- If ApexClass__c is provided, mock creates an instance of this class and returns it. The class should implement HttpCalloutMock interface.
- If Headers__c field is provided - it's split by new lines and colons and added to response body headers.
1. The response is handled.

Expand All @@ -86,11 +79,57 @@ Each mock is registered under a unique developer name. We can utilize this name
Consider this example:

```apex
Test.setMock(HttpCalloutMock.class, new OrgMocks()
.overrideMock('SF_REST_Query', HttpMocks.config('SF_REST_Query_Empty')));
Test.setMock(HttpCalloutMock.class, HttpMocks.config()
.overrideMock('SF_Query_Account', 'SF_Query_Account_Empty')
.overrideMock('SF_Account_Get', new MyMockClass())
);
```
`SF_REST_Query` mock was replaced, and now it will respond with mock registered under name `SF_REST_Query_Empty`.
`SF_Account_Get` mock will be replaced by MyMockClass—an implementation of HttpCalloutMock.


## Requests and Responses
You can check issued Http Requests and returned responses using the following methods:
```apex
List<HttpRequest> requests = HttpMocks.getRequests();
List<HttpResponse> responses = HttpMocks.getResponses();
```
This is helpful when request and response are not directly exposed in unit tests, but we want to check if the payload is correct.

---
# Interfaces

## HttpMocks

| Modifier and Type | Method and Description |
|---------------------------|------------------------------------------------------------------------------------------------------------------------------|
| static List<HttpRequest> | **getRequests()**<br/> Returns list of handled HttpRequests in the order they were issued. |
| static List<HttpResponse> | **getResponses()**<br/> Returns list of returned HttpResponses in order they were returned.* |
| static HttpCalloutMock | **json(Integer statusCode, String status, Object jsonObject)**<br/> Returns mock with serialized JSON object as response. |
| static HttpCalloutMock | **staticResource(Integer statusCode, String status, String staticResource)**<br/> Returns mock with Static Resource as body. |
| static HttpCalloutMock | **text(Integer statusCode, String status, String body)**<br/> Returns mock with plain text body. |
| static HttpCalloutMock | **config()**<br/> Returns router with all custom metadata mocks loaded. |
| static HttpCalloutMock | **config(String customMetadataName)**<br/> Returns mock loaded from custom metadata by given developer name. |
| static HttpCalloutMock | **config(HttpCalloutMock__mdt customMetadata)**<br/> Returns mock loaded from custom metadata record. |

## HttpCalloutChainMock Interface
Extension of HttpCalloutMock, which adds `handles` method - This method checks if this mock class should handle incoming request.
```apex
public interface HttpCalloutChainMock extends HttpCalloutMock {
Boolean handles(HttpRequest request);
}
```

## HttpCalloutMockRouter

| Modifier and Type | Method and Description |
|-----------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------|
| HttpCalloutMockRouter | **mock(String name, String methods, String endpointPattern, HttpCalloutMock mock)**<br/> Register HttpCalloutMock for given Http method and endpoint pattern. |
| HttpCalloutMockRouter | **mock(String name, HttpCalloutChainMock handler)**<br/> Register HttpCalloutChainMock implementation. |
| HttpCalloutMockRouter | **overrideMock(String name, String overrideMetadataName)**<br/> Replaces mock registered under given name with different mock loaded from custom metadata |
| HttpCalloutMockRouter | **overrideMock(String name, HttpCalloutMock mock)**<br/> Replaces mock registered under given name with different mock |
| HttpCalloutMockRouter | **variable(String name, String regexp)**<br/> Registers regexp variable which will can be referenced in endpoint. |
| HttpCalloutMockRouter | **variables(Map<String, String> vars)**<br/> Registers regexp variables which will can be referenced in endpoint. |

---
# Custom Metadata
Expand Down Expand Up @@ -215,6 +254,14 @@ public class MyCustomMock implements HttpCalloutMock {

---
# Change Log
### 1.1.0
- Added shorthand override method Http
```apex
public HttpCalloutMockRouter overrideMock(String name, String overrideMetadataName)
```
- Added HttpMocks methods to return issued requests and responses.
### 1.0.2
- Fixed bug in HTTP Headers specified in metadata, where header value would not be parsed correctly if it contained colon, which would cause issues for Location headers.
- Fixed bug in HTTP Headers specified in metadata, where header value would not be parsed correctly if it contained colon, which would cause issues for Location
headers.
- Added help text to custom metadata fields.
Binary file modified docs/img/http-router-example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion docs/index.html
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="theme-color" content="#000000"><meta name="description" content="Open source library for Salesforce. Salesforce Frameworks, Accelerators, Components, Utilities."><link rel="manifest" href="/manifest.json"><link rel="shortcut icon" href="/cloud.svg"><link rel="stylesheet" href="/salesforce-lightning-design-system.min.css"><title>Apex Libra - Library of Salesforce Accelerators, Frameworks, Utilities</title><script type="text/javascript">!function(n){if("/"===n.search[1]){var a=n.search.slice(1).split("&").map((function(n){return n.replace(/~and~/g,"&")})).join("?");window.history.replaceState(null,null,n.pathname.slice(0,-1)+a+n.hash)}}(window.location)</script><script defer="defer" src="/static/js/main.2d530ebc.js"></script><link href="/static/css/main.daa7645d.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div style="display:none"><h1>Apex Commons</h1><h1>Salesforce Frameworks</h1><h1>Salesforce Accelerators</h1><h2>Trigger Handler</h2><h2>Lambda</h2><h2>Collection</h2><h2>XML</h2><h2>Selectors</h2><h2>Selector Layer</h2><h1>LWC</h1></div><div id="root" class="fullHeight"></div></body></html>
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="theme-color" content="#000000"><meta name="description" content="Open source library for Salesforce. Salesforce Frameworks, Accelerators, Components, Utilities."><link rel="manifest" href="/manifest.json"><link rel="shortcut icon" href="/cloud.svg"><link rel="stylesheet" href="/salesforce-lightning-design-system.min.css"><title>Apex Libra - Library of Salesforce Accelerators, Frameworks, Utilities</title><script type="text/javascript">!function(n){if("/"===n.search[1]){var a=n.search.slice(1).split("&").map((function(n){return n.replace(/~and~/g,"&")})).join("?");window.history.replaceState(null,null,n.pathname.slice(0,-1)+a+n.hash)}}(window.location)</script><script defer="defer" src="/static/js/main.2d530ebc.js"></script><link href="/static/css/main.daa7645d.css" rel="stylesheet"><script defer="defer" src="/static/js/main.fa12e836.js"></script><link href="/static/css/main.daa7645d.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div style="display:none"><h1>Apex Commons</h1><h1>Salesforce Frameworks</h1><h1>Salesforce Accelerators</h1><h2>Trigger Handler</h2><h2>Lambda</h2><h2>Collection</h2><h2>XML</h2><h2>Selectors</h2><h2>Selector Layer</h2><h1>LWC</h1></div><div id="root" class="fullHeight"></div></body></html>
30 changes: 30 additions & 0 deletions docs/news/24_11_10.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# 2024/07/18

---
# News
Http Mock Router now features a new shorthand method to override mocks with custom metadata:

Previously:
```apex
Test.setMock(HttpCalloutMock.class, HttpMocks.config()
.overrideMock('SF_REST_Query', HttpMocks.config('SF_REST_Query_Empty')));
```

Now becomes:
```apex
Test.setMock(HttpCalloutMock.class, HttpMocks.config()
.overrideMock('SF_REST_Query', 'SF_REST_Query_Empty'));
```

Also, HttpMocks has 2 new methods that will give you access to handled HttpRequests and returned HttpResponses:
```apex
/**
* @return List of handled HttpRequests in the order they were issued.
*/
public static List<HttpRequest> getRequests();
/**
* @return List of returned HttpResponses in order they were returned.
*/
public static List<HttpResponse> getResponses();
```
2 changes: 2 additions & 0 deletions docs/static/js/main.fa12e836.js

Large diffs are not rendered by default.

148 changes: 148 additions & 0 deletions docs/static/js/main.fa12e836.js.LICENSE.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
/*
object-assign
(c) Sindre Sorhus
@license MIT
*/

/*!
Copyright (c) 2018 Jed Watson.
Licensed under the MIT License (MIT), see
http://jedwatson.github.io/classnames
*/

/*!
Copyright (c) 2015 Jed Watson.
Based on code that is Copyright 2013-2015, Facebook, Inc.
All rights reserved.
*/

/*!
* Adapted from jQuery UI core
*
* http://jqueryui.com
*
* Copyright 2014 jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*
* http://api.jqueryui.com/category/ui-core/
*/

/*!
* Determine if an object is a Buffer
*
* @author Feross Aboukhadijeh <https://feross.org>
* @license MIT
*/

/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */

/**
* @license React
* react-dom.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

/**
* @license React
* react-is.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

/**
* @license React
* react-jsx-runtime.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

/**
* @license React
* react.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

/**
* @license React
* scheduler.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

/**
* @remix-run/router v1.7.2
*
* Copyright (c) Remix Software Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE.md file in the root directory of this source tree.
*
* @license MIT
*/

/**
* Prism: Lightweight, robust, elegant syntax highlighting
*
* @license MIT <https://opensource.org/licenses/MIT>
* @author Lea Verou <https://lea.verou.me>
* @namespace
* @public
*/

/**
* React Router DOM v6.14.2
*
* Copyright (c) Remix Software Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE.md file in the root directory of this source tree.
*
* @license MIT
*/

/**
* React Router v6.14.2
*
* Copyright (c) Remix Software Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE.md file in the root directory of this source tree.
*
* @license MIT
*/

/** @license React v17.0.2
* react-jsx-runtime.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

/** @license React v17.0.2
* react.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
4 changes: 4 additions & 0 deletions force-app/commons/httpMocks/classes/HttpCalloutChainMock.cls
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,9 @@
* SOFTWARE.
*/
public interface HttpCalloutChainMock extends HttpCalloutMock {

/**
* This method checks if incoming request should be handled by this mock class.
*/
Boolean handles(HttpRequest request);
}
Loading

0 comments on commit 9fc3e73

Please sign in to comment.