There are quite a few blogs describing a means to build Salesforce URL’s to standard pages for creating records, that can be applied to Custom Buttons in order to pre-populate field values when a user clicks a button. For example replacing the New button on a Related list.
- Salesforce URL Hacking to Prepopulate Fields on a Standard Page Layout
- Hack to find field ids – allows a default UI “New” page to be pre-populated
- Salesforce URL Hacking (Properly)
- Create a Custom ‘New’ Button on a Related List to Return to the Parent Record After Clicking Save
IMPORTANT NOTE: The URL format Salesforce uses for the standard UI is not a supported API and thus subject to change. Though it has become quite common practice in order to improve the usability of the standard UI.
Making things a little more Robust
Other than URL format assumption, there is an area of further fragility shared amongst the solutions I’ve seen to-date. Which is how to obtain and manage the custom field Id’s needed on the URL’s (since they don’t take field API names). Either via discovering these manually or dynamically via using REGEX parsing of the HTML source code of the Salesforce standard pages. Although there is a Salesforce IdeaExchange submission to have Salesforce expose these Id’s via Apex Describe, it apparently has not been addressed… or so it would seem that is….
Tooling API support for Custom Object and Custom Fields
Probably the least obvious aspect of the Tooling API is its ability to actually query (via REST or SOAP) the CustomObject and CustomField objects (which are not accessible via Apex SOQL). As we know every object in Salesforce has an ID, this got me wondering if in this case its the same ID’s needed by the approaches used above. And thus if so, would serve as a more supported and less risky means to obtain the custom field Id’s. I have also wanted to start building out a Apex wrapper for the Tooling API for a while now. The following Apex code shows the library in action…
UPDATE: Please use the Apex code that wraps the REST version of the API in order to be compatible with the statements in this blog.
// Constructs the Tooling API wrapper (default constructor uses user session Id) ToolingAPI toolingAPI = new ToolingAPI(); // Query CustomObject object by DeveloperName (note no __c suffix required) List<ToolingAPI.CustomObject> customObjects = (List<ToolingAPI.CustomObject>) toolingAPI.query('Select Id, DeveloperName, NamespacePrefix From CustomObject Where DeveloperName = \'Test\'').records; // Query CustomField object by TableEnumOrId (use CustomObject Id not name for Custom Objects) ToolingAPI.CustomObject customObject = customObjects[0]; Id customObjectId = customObject.Id; List<ToolingAPI.CustomField> customFields = (List<ToolingAPI.CustomField>) toolingAPI.query('Select Id, DeveloperName, NamespacePrefix, TableEnumOrId From CustomField Where TableEnumOrId = \'' + customObjectId + '\'').records; // Dump field names (reapply the __c suffix) and their Id's System.debug(customObject.DeveloperName + '__c : ' + customObject.Id); for(ToolingAPI.CustomField customField : customFields) System.debug( customObject.DeveloperName + '__c.' + customField.DeveloperName + '__c : ' + customField.Id);
This results in the following list of fields and Id’s against my test custom object…
00:27:38.845 (845882069)|USER_DEBUG|[45]|DEBUG|Test__c : 01IG00000021cXoMAI 00:27:38.846 (846249350)|USER_DEBUG|[47]|DEBUG|Test__c.A_Number__c : 00NG0000009Y0I9MAK 00:27:38.846 (846305290)|USER_DEBUG|[47]|DEBUG|Test__c.Colours__c : 00NG0000009prwyMAA 00:27:38.846 (846328856)|USER_DEBUG|[47]|DEBUG|Test__c.Date__c : 00NG0000009BrnxMAC 00:27:38.846 (846513094)|USER_DEBUG|[47]|DEBUG|Test__c.Message__c : 00NG0000009Y0IOMA0 00:27:38.846 (846535746)|USER_DEBUG|[47]|DEBUG|Test__c.MultiPick__c : 00NG000000AcULrMAN 00:27:38.846 (846558753)|USER_DEBUG|[47]|DEBUG|Test__c.MyCheckbox__c : 00NG0000009Y5C8MAK 00:27:38.846 (846741056)|USER_DEBUG|[47]|DEBUG|Test__c.RichText__c : 00NG0000009XaRJMA0 00:27:38.846 (846816183)|USER_DEBUG|[47]|DEBUG|Test__c.Text__c : 00NG0000009prxwMAA
Summary
This blog serves as a means to demonstrate an initial starting point for a Apex wrapper around the Tooling API (the GitHub repo has the full code with tests to deploy direct via the usual link). Be sure to setup the appropriate Remote Site setting for the Tooling API end point (easiest way is to run the code and note the URL given in the exception). Finally a means for for the various authors of the solutions above to update their code. Enjoy!
January 5, 2014 at 12:12 pm
Very nice. For managed packages, it would be cool to run this code from the InstallHandler and dump the field ids in a list custom setting for convenient/fast access. Can you see any problems with that approach?
January 5, 2014 at 2:51 pm
So long as the appropriate Remote Site setting was added before hand this is possible. I’m going to try to convince Salesforce to white list its own servers one day, since it seems an odd requirement to ask the user to permit calls to the Salesforce servers.
January 5, 2014 at 6:52 pm
Thanks.
I agree with you about Salesforce white listing its own servers. I have an app that uses OATH and supports Facebook, Google or Salesforce itself as the possible authorization sources. But for the Salesforce one, getting an email address involves a 302 being returned with a location that looks like it could be just about any Salesforce instance, meaning a Remote Site might need adding for all 17 NA instances…
January 7, 2014 at 12:10 pm
Reblogged this on Sutoprise Avenue, A SutoCom Source.
Pingback: Hack to find field ids – allows a default UI “New” page to be pre-populated | Force 201
January 16, 2014 at 8:30 pm
When I implemented this solution I found that the Tooling API does not return an exact match to the html input Id. Here are two examples:
Tooling API ID = 00NK00000013TUFMA2
HTML input ID = 00NK00000013TUF
Tooling API ID = 00NU0000001Cxs4MAC
HTML input ID = 00NU0000001Cxs4
If you truncate the last three characters of the Tooling API ID the code works and auto-populates the form fields.
January 16, 2014 at 8:40 pm
Yes thanks for this update Jeff, the last three characters are intended to make the Id case insensitive, where this is required, see this for a more detailed explanation. http://salesforce.stackexchange.com/questions/1653/what-are-salesforce-ids-composed-of
February 11, 2014 at 1:18 am
Hi Andrew,
Thanks for your hard work – You may be aware of this already but the source on GitHub at the moment is installing with errors.
Failures:
classes/ToolingAPIDemo.cls(36,4):Method does not exist or incorrect signature: [ToolingAPI].queryCustomObject(String)
classes/ToolingAPITest.cls(81,3):Invalid type: ToolingAPI.CustomObjectQueryResult
I know its a work in progress but thought Id let you know.
Best,
Don
February 11, 2014 at 1:45 am
Ah yes, sorry about that, do you want me to dig out the working version at the time the blog was posted and put it in a Gist? I probably should do this anyway tbh.
February 11, 2014 at 10:38 am
That would be awesome!
Best,
Don
February 11, 2014 at 10:52 am
I just grabbed a prior commit from Jan 4th – I just loaded into sf but haven’t tested but I should be good – thanks!
February 11, 2014 at 1:33 pm
Cool, good to know, i’ve made a note to sort this out for future blog readers, thanks for keeping me honest! Btw, what is it your doing out of interest? (its cool if you don’t want to share btw).
February 11, 2014 at 8:55 pm
I’ve updated the repo to fix the demo class and test class, feel free to try again if you want the latest.
February 25, 2014 at 1:52 pm
Hey Andy,
I think the code example is still broken. I plan to use this class to prepopulate a native Salesforce page via URL dynamically.
February 25, 2014 at 11:35 pm
The repo is valid, let me check the blog sample code though, sorry about this.
February 25, 2014 at 11:42 pm
Ok yeah, apologies forgot to update the code sample in the blog, it now matches the latest in the repository. Have fun!
Pingback: Finding Visualforce field ids | Force 201
Pingback: Going Native with the Apex UML Tool and Tooling API! | Andy in the Cloud
April 18, 2014 at 7:06 am
Hi Andy,
Thanks for the info about the Tooling API. I checked out your code example for custom object/fields and it works great for a sample custom object in my Org. However, I have some custom fields in the Lead standard object and from what I found from the Salesforce Tooling API doc, the API does not seem to support standard objets – which is a bummer.
The workaround is that I am using the listMetaData method from your excellent Metadata API apex wrapper, and I am able to get the Ids of custom fields in the Lead object. The only issue with this method is that it gives the Ids of all the fields in the Org, thus is probably not the most efficient way to get to find the Id of a specific custom field. Please let me know if there is a better way to find the Ids.
Thanks again for the API classes. I find them very useful.
Madhav
April 18, 2014 at 1:59 pm
Hmm that is a bummer, can you raise an issue on the GitHub repo and I’ll take a look see if i can improve this for you, as yeah your probably going to run into 3MB HTTP response limit and/or HTTP callout timeout governors with listMetadata.
https://github.com/afawcett/apex-toolingapi
June 2, 2014 at 11:29 am
Hi Andrew Fawcett,
I displayed all custom objects in visualforce page using wrapper class with delete and edit command links.when i click delete command link how to delete a custom object from database?( i try to delete custom object using key prefix but visualforce error invalid id )
please help me…………
June 2, 2014 at 11:48 am
Checking the Salesforce documentation, it does not appear that delete is supported for CustomObject’s, nor for CustomField sorry.
June 2, 2014 at 12:08 pm
Hi Andrew Fawcett,
can you just send me that link ?
June 2, 2014 at 1:00 pm
Sorry not at this moment in time
June 4, 2014 at 6:42 am
Hi Andrew Fawcett,
Using tooling api i try to delete custom objects and custom field from visualforce page.Is it possible to delete custom objects and custom fields directly from visualforce page?
please help me…
June 4, 2014 at 12:30 pm
Really sorry to report but this is not possible with the Tooling API.
June 4, 2014 at 12:46 pm
Hi Andrew Fawcett,
is there any otherway to delete custom objects or fileds from the vf page, please help me on this?
pleadse help me……….
June 4, 2014 at 12:52 pm
In theory you could use the Metadata API’s “deploy” method by providing a zip file that contains a destructivePackage.xml containing the custom object or field you want to delete. This is only in theory i have never tried it for VF. You could start by adapting the Deploy demo controller in the Apex Metadata API library. Sorry I cannot be of much more help at this time it is a busy time. If you are struggling create an issue on GitHub under the Apex Metadada API repository and i will see if i can build an example in the future. Thanks, hope this helps!
June 5, 2014 at 1:30 pm
Hi Andrew Fawcett,
I created fields using visualforce page(through metadata api).How to add created fields from pagelayout to fieldset through coding(Dynamically)and how to iterate fieldset in an visualforce page dynamically (through coding).
please help me………..
June 6, 2014 at 10:38 am
Hi Andrew Fawcett,
I deleted one field from visualforce page using metadata api,but I displayed list of fields using wrapper class and related command link Del.I try to delete one field but all fields will be deleted from database and following error will be came
System.LimitException: rajesh:Too many callouts: 11
Error is in expression ‘{!doDelete}’ in page rajesh:createfields
Class.rajesh.MetadataService.MetadataPort.deleteMetadata: line 8470, column 1
Class.rajesh.MetadataController11.doDelete: line 15, column 1
line 15: MetadataService.AsyncResult[] results = service.deleteMetadata(new List { customField });
How to solve above Error.
please help me…………
June 6, 2014 at 6:47 pm
This is a governor limit of the platform. You can work better within it by passing multiple fields on line 15 and don’t call deleteMetadada in a loop.
June 7, 2014 at 6:50 am
Hi Andrew Fawcett,
you know i created fields using metadata api but created fileds Field-Level Security for Profile place visible and editable checkboxes not checked(any profile not checked i am a administrator).Every time i will goto Setup->Manage->users>administrator->custom field level security->related object name and click view and checked to metadata created fields visible and editable.How to check this visible and editable Using metadata api dynamically .
please help me………..
June 7, 2014 at 6:54 am
Create a Permission Set using Apex DML and assign to the user
June 7, 2014 at 6:57 am
sorry here Read-only insted of editable
June 7, 2014 at 6:57 am
Custom permission sets are read and wrote
June 7, 2014 at 7:02 am
Hi Andrew Fawcett,
please send me related link i don’t no how to Create a Permission Set using Apex DML and assign to the user.
please help me………
June 7, 2014 at 7:06 am
Please try using Google sometimes. Here are some links i found by typing Permission Set Apex for example. https://gist.github.com/daveespo/10746358 http://www.salesforce.com/us/developer/docs/api/Content/sforce_api_objects_permissionset.htm
June 7, 2014 at 12:34 pm
Hi Andrew Fawcett,
Using visualforce page everytime i will create new object. Is it possible to add permission set everytime created object dynamically using this permission set?………..
please help me…………..
June 7, 2014 at 1:38 pm
Yes that should be ok
June 11, 2014 at 6:31 am
Hi Andrew Fawcett,
Actually i try that link but there is no result.Using the above link i con’t able to do anything.I search in google also there is no information related to fields visible and Read-only permissions related to administrator profile.There is any related link is available.Please send me
please help me……………….
June 13, 2014 at 8:41 am
Hi Andrew Fawcett,
all are told this possibility only metadata api.How to give field visible permission using metadata api perticular profile ?
i find below links
http://stackoverflow.com/questions/11438593/salesforce-field-level-security-for-user-object
http://salesforce.stackexchange.com/questions/30240/how-do-modify-all-data-and-view-all-data-affect-field-level-security-setting
https://developer.salesforce.com/page/Enforcing_CRUD_and_FLS
http://www.salesforce.com/us/developer/docs/apexcode/Content/apex_classes_perms_enforcing.htm
please help me…………
June 14, 2014 at 10:47 am
Hi Andrew Fawcett,
I solved above problem using metadata api
thanks
June 14, 2014 at 11:45 am
That’s great well done! I am curious what kind of tool are you building, what end user problem does it solve?
June 16, 2014 at 2:08 pm
I tried to use that in a managed package controller and as non-admin user I get a
UP2GO_ITE.ToolingAPI.ToolingAPIException: INVALID_TYPE : sObject type ‘CustomEntityDefinition’ is not supported.
Class.UP2GO_ITE.ToolingAPI.submitRestCall: line 1841, column 1
Class.UP2GO_ITE.ToolingAPI.submitRestCall: line 1782, column 1
Class.UP2GO_ITE.ToolingAPI.submitRestCall: line 1774, column 1
Class.UP2GO_ITE.ToolingAPI.query: line 105, column 1
Class.UP2GO_ITE.TravelCtrlExt.getExpenseObjectId: line 125, column 1
Class.UP2GO_ITE.TravelCtrlExt.getRelatedListId: line 109, column 1
I checked the RemoteSiteSettings. Everything is ok?!
June 16, 2014 at 4:11 pm
Thanks, would you mind raising a item on the GitHub repo for the project and include some sample code, such as the query your running.
June 17, 2014 at 10:44 am
Sure. I created https://github.com/afawcett/apex-toolingapi/issues/19
June 19, 2014 at 7:24 am
Hi Andrew Fawcett,
I created checkbox using metadata api.My requirement is checkbox creation time i gave some values with comma separated like A,B,C,D.How to display created checkbox field as look like following in an visualforce page
FieldName
Checkbox A
Checkbox B
Check box C
Checkbox D using metadata api
Please Help me………………..
June 20, 2014 at 5:34 pm
This is blog post about Tooling API, not Metadata API, unless you have a question or comment related to the blog post content.
i think it is best you either post your questions on the GitHub repositories related to the libaraires Apex Metadata API (https://github.com/financialforcedev/apex-mdapi/issues?state=open) or on Salesforce StackExchange (http://salesforce.stackexchange.com/).
Finally I think this question is more general and you could get help from a broader selection of people on http://salesforce.stackexchange.com/. I really want to help you, but i find your questions hard to understand and to broad. The StackExchange approach will help you to be more specific and ask more general quesitons. I also hang out on that site as well! 🙂
November 9, 2014 at 6:08 pm
Hi Andy,
Thanks for all the hard work with these APIs. They’re awesome. However, I’m have a problem querying for WorkflowRules by LastModifiedDate. Here is the line in my code that’s giving me problems:
List workflowRules = (List)toolingAPI.query(‘SELECT TableEnumOrId, LastModifiedDate FROM WorkflowRule Where LastModifiedDate > ‘ + json.serialize(dt).replace(‘”‘, ”) ).records;
I’ve tried a few different approaches to this, but each have given me errors. I’m assuming that I’m just missing the easy answer here.
What is the correct way to achieve this?
Thanks, Bob
November 10, 2014 at 9:23 am
Hi Bob, can i ask that you raise an issue in the GitHub repo and include some more details on what kind of errors your getting, along with your source code sample. Either one of the other contributors or myself will take a look. https://github.com/afawcett/apex-toolingapi/issues. Thanks.
December 4, 2014 at 12:17 am
Hello Andy,
Have a question about meta data for standard object… Can we use tooling API to get the ID of a field on standard object? The custom Object does not seem to give me details of Standard Object.
December 5, 2014 at 9:39 am
I see you have raised a GitHub issue on this, i’ll respond there, thanks.
Pingback: How do I prepopulate fields on a Standard layout? | CL-UAT
Pingback: Workaround for URL Hack to prepopulate fields on standard page layout | DL-UAT
June 19, 2015 at 8:05 pm
Andrew,
I’m not sure how you do it, but pretty much every time I have searched Google for something Salesforce related I end up on your site and finding the answer.
June 19, 2015 at 8:16 pm
LOL, thanks! I’m not sure either tbh, just happy to be helping! 🙂
August 18, 2015 at 7:11 pm
Awesome Stuff!!!
The Code as given does not work; at least not for me;
2 issues – CustomObject & CustomField are defined in ToolingAPIWSDL and not ToolingAPI as mentioned in the code;
Typecasting does not save and compile properly
I even tried using the overloaded function ToolingAPI.query passing the class of the records; this compiles fine but during runtime it fails with a null point exception
12:01:11.971 (971445424)|METHOD_ENTRY|[84]|01pR0000000CQTS|ToolingAPI.castQueryResultToSubclass(ToolingAPIWSDL.QueryResult, String)
12:01:11.971 (971467885)|SYSTEM_METHOD_ENTRY|[166]|String.isEmpty(String)
12:01:11.971 (971482283)|SYSTEM_METHOD_EXIT|[166]|String.isEmpty(String)
12:01:11.971 (971495574)|METHOD_ENTRY|[167]|01pR0000000CQTS|ToolingAPI.castSobjectArrToSubclass(List, String)
12:01:11.971 (971514875)|SYSTEM_METHOD_ENTRY|[173]|System.Type.forName(String)
12:01:11.972 (972434482)|SYSTEM_METHOD_EXIT|[173]|System.Type.forName(String)
12:01:11.972 (972446798)|SYSTEM_METHOD_ENTRY|[173]|System.Type.newInstance()
12:01:11.972 (972680329)|SYSTEM_METHOD_EXIT|[173]|System.Type.newInstance()
12:01:11.972 (972790904)|METHOD_EXIT|[167]|01pR0000000CQTS|ToolingAPI.castSobjectArrToSubclass(List, String)
12:01:11.972 (972800092)|METHOD_EXIT|[84]|01pR0000000CQTS|ToolingAPI.castQueryResultToSubclass(ToolingAPIWSDL.QueryResult, String)
12:01:11.972 (972806709)|METHOD_EXIT|[28]|01pR0000000CQTS|ToolingAPI.query(String, String)
12:01:11.972 (972825933)|CONSTRUCTOR_EXIT|[1]|01pR0000000CQTI|(String)
12:01:11.972 (972837404)|SYSTEM_MODE_EXIT|false
12:01:11.972 (972925525)|FATAL_ERROR|System.NullPointerException: Attempt to de-reference a null object
Class.ToolingAPI.castSobjectArrToSubclass: line 174, column 1
Class.ToolingAPI.castQueryResultToSubclass: line 167, column 1
Class.ToolingAPI.query: line 84, column 1
changing to sObject_x instead of CustomObject or CustomField works for me as I need the Developer Name and the Id’s…
Does anyone have a clue on the errors and typecasting
August 20, 2015 at 7:47 pm
Are you using the rest branch code from the repo? If so please raise an issue on the GitHub repo and I will try to look this weekend, though I am quite busy with Dreamforce prep. There is another who works on this I can also alert him to help
September 29, 2015 at 9:36 pm
Is there a test class for the latest code in the master? I got everything working but are having a hard time writing the test class.
September 30, 2015 at 12:06 pm
Not if you don’t see one no, for which code specifically?
October 5, 2015 at 3:14 pm
As described in this Issue of the Apex wrapper, such code can currently not be called by non-admin users. This definitly reduces it’s usefulness. Isn’t there an alternative using the Metadata API and its Apex wrapper?
October 5, 2015 at 3:16 pm
Forgot the issue URL: https://github.com/afawcett/apex-toolingapi/issues/19
November 3, 2015 at 10:28 pm
Got the Restful version working great. Having trouble with permissions. All is good when user profile = sys admin. Any other user profiles get a http 400 error. Any ideas on correct permission settings?
November 4, 2015 at 7:59 am
Try Author Apex?
November 4, 2015 at 7:59 am
It’s a pretty high perm requirement API, there is also some info in the GitHub issue list on this as well.
February 19, 2016 at 2:34 pm
Hi Andy I cant seem to get this to work. If you have a spare minute Id appreciate it
February 24, 2016 at 7:56 pm
Can you elaborate on what code is failing and with what message?
Pingback: URL hacking. Automatically fill field values on chilid page layouts | Bartosz Michał Borowiec
August 22, 2016 at 1:03 am
Hi. Trying to install the REST version and I get “Oops, something went wrong…” when clicking on Login to Salesforce …
August 22, 2016 at 8:21 am
Ah the deploy too does not support branches. Not bet anyway. For now you will have to download the code and deploy manually via ant or an IDE
September 22, 2016 at 4:13 am
Hi. I am using the code provided in the article for constructing the Tooling API wrapper in an Apex class and I get Error: Compile Error: Invalid type: ToolingAPI.
Could you advice what I’m missing? Do I need to enable anything in Salesforce to use ToolingAPI? Thank you
September 26, 2016 at 9:31 am
Yes you need to deploy some additional Apex code into your org. The Tooling API in Apex is not a standard feature. For this article you will need the REST API Apex wrapper here, https://github.com/afawcett/apex-toolingapi/tree/apex-toolingapi-rest
(note that the Deploy to Salesforce button does not work for branches yet, so you will have to download and manually upload the classes)
April 20, 2017 at 10:41 am
Hi Andy,
We have written some processes using process builder and since there is an enforced limit of 1000 flow interviews that can be invoked per an hour, I would like to know the no.of current running flows in my org for performance scaling. Please tell me how to use tooling API to fetch the no.of process flows executed in my org per hour. I am totally new to tooling API, so looking forward to some guidance.
April 20, 2017 at 10:55 pm
This is not something the tooling API returns. However the limits API does share general limits information. https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_limits.htm I did just look and it does not appear to show Process Builder limits sadly, anyway have a closer look yourself as this is if any the API that would give this info.
September 10, 2017 at 6:57 am
Hi , I am trying to get all child objects. I mean which objects are the child objects for the object defined in ParentSobject field. I am trying to use “RelationshipDomain” tooling Api object to get that. Could anyone guide me how to use this object?
September 18, 2017 at 12:29 pm
Not sure on that object, you can do this in Apex Describe though or no?
December 7, 2017 at 12:54 am
Hi Andy,
How can I get latest tooling REST API class with latest features e.g. Layout, Global Picklist etc.?
December 7, 2017 at 6:45 pm
This wrapper is maintained manually, unlike the Apex md one, so these types need to be coded in manually to add Support. Sorry
February 28, 2018 at 10:53 pm
Hi Andrew,
I’m getting compile error, please help!
COMPILE ERROR: Invalid type: ToolingAPI.CustomObject
LINE: 5 COLUMN: 1
March 9, 2018 at 9:21 pm
Make sure to try the Rest api branch
Pingback: API to Create CustomField – Agustina odeian
August 2, 2019 at 1:38 am
please fix the deplyment link https://githubsfdeploy.herokuapp.com/?owner=afawcett&repo=apex-toolingapi%2Ftree%2Fapex-toolingapi-rest
Pingback: Tooling API | MST Solutions
April 5, 2021 at 5:14 pm
Andrew, the deployment link is not working anymore.
April 12, 2021 at 8:57 pm
Ah ok – what error do you try?
July 13, 2022 at 4:14 am
Hi sir
I wants to Get Field Labels Of an Object .How Can I get to show Field Labels In Drop Down ? if You have Any Solution Please mention ASAP.
November 28, 2022 at 10:05 pm
You do not need to use Tooling API for this. Ypu can do it in regular Apex – https://github.com/apex-enterprise-patterns/fflib-apex-common-samplecode/blob/master/sfdx-source/apex-common-samplecode/test/classes/service/AccountsServiceTest.cls