Coder Social home page Coder Social logo

jamessimone / apex-dml-mocking Goto Github PK

View Code? Open in Web Editor NEW
69.0 5.0 20.0 1.5 MB

DML mocking, CRUD mocking, dependency injection framework for Salesforce.com (SFDC) using Apex

License: MIT License

Apex 97.92% PowerShell 2.08%
apex salesforce salesforce-developers salesforce-development apex-framework apex-class apex-test sfdc salesforcedx salesforce-apex

apex-dml-mocking's Introduction

Apex DML Mocking

Welcome to the SFDX project home for blazing fast Apex unit tests! For your consideration, this is an example of how to implement the full CRUD (Create Read Update Delete) mocking implementation within your own Salesforce orgs. You can find out more information by perusing:

  • force-app for implementation details
  • example-app for an example Account Handler with mocking set up

Writing tests that scale as the size of your organization grows is an increasingly challenging problem in the Salesforce world. It's not uncommon in large companies for deploys to last several hours; the vast majority of that time is spent running tests to verify that your code coverage is good enough for the deploy to succeed. Tests don't need to take that long.

This repo shows you how you can mock your SOQL queries and DML statements in Apex by using lightweight wrappers that are dependency injected into your business logic objects. This allows you to replace expensive test setup and test teardown with a fake database. I've used this method to cut testing time down by 90%+ -- in a small org, with only a few hundred tests, running tests and deploying can be done in under five minutes (easily). In large orgs, with many hundreds or thousands of tests, overall testing time tends to scale more linearly with organizational complexity; there are additional optimizations that can be done in these orgs to keep deploys in the 10 minute(s) range.

Access Level & DML Option Setting

Both IDML and IRepository instances returned by the framework support the method IDML setOptions(Database.DMLOptions options, System.AccessLevel accessLevel);. Note that if DML options are not set by default, this framework uses true for the allOrNone value when performing DML, as that is consistent with the standard for calling DML operations without specifying that property. Please also note that DML options are not "expired" after having been set -- if you are using an instance of IRepository or IDML and are performing multiple DML operations using that same instance, the DML options that have been set will continue to apply to subsequent operations until setOptions is re-called, or a new instance is initialized. DML options are not shared between instances; they are not statically set. Passing null for the DML options value will only update the access level.

By default, all operations are run using System.AccessMode.SYSTEM_MODE. You can either override this (for IDML and IRepository instances) by calling setOptions, as shown above, or by calling IRepository setAccessLevel(System.AccessLevel accessLevel); on IRepository instances. Like DML options, the access level that is set for an instance is then the one used for subsequent operations involving that repository instance.

DML Mocking Basics

Try checking out the source code for the DML wrapping classes:

SOQL Mocking Basics

Take a look at the following classes to understand how you can replace raw SOQL in your code with testable (and extendable) strongly typed queries:

Then, move on to the more complicated examples:

While opinionated in implementation, these classes are also just scratching the surface of what's possible when taking advantage of the Factory pattern in combination with the Repository pattern, including full support for:

  • strongly typed subqueries (queries returning children records)
  • strongly typed parent-level fields
  • the ability to easily extend classes like Repository to include things like limits, order bys, etc ...

Dependency Injection Basics

The "Factory" pattern is of particular importance for DML mocking, because it allows you to have only one stub in your code for deciding whether or not to use mocks when running tests; crucially, the stub is only available when tests are being run: you cannot mock things in production-grade code.

You can have as many Factories as you'd like. I like to break my Factories out by responsibility:

  • A factory for Trigger handlers
  • A factory for basic classes
  • The RepoFactory for CRUD related objects

It's a pretty standard approach. You might choose to break things down by (business) domain. There's no right way.

Package-Based Development

These repository (as of 18 May 2023) has been slightly reworked to provide better support for package-based development. The updates are primarily to show how the example-app folder can be in a completely separate package while still allowing for strongly-typed references (and package-specific factories and repo factories) to be referenced properly. For a concrete example, check out:


More Information

For more information on these patterns and how to use them, consider the free resources I've published under The Joys Of Apex. Thanks for your time!

apex-dml-mocking's People

Contributors

andrewjarrett avatar dependabot[bot] avatar jamessimone avatar jim3692 avatar rygramer avatar theysen avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

apex-dml-mocking's Issues

Add License File

Hi,

Would it be possible to add a license file to this repo, just to be 100% sure if we can use this code in our projects or not?

DMLMock Issue

DML mock is not working for WorkOrderLineItem standard object.. For the other sobjects it is working fine.

Feature request: Support DMLOptions and AccessLevel for writes

Your Repository class support SYSTEM / USER mode for queries, but not for writes (unless I missed it). Additionally, there's no clean way to set DML options for the entire batch (it has to be set on the individual SObjects).

To avoid the ugliness of having 1000 overloads, just supporting
insert(recordsToInsert, dmlOptions, accessLevel)

for example (along with the update, etc. versions) would be sufficient; the single-record use case can just wrap in a list, and the allOrNone true/false overload is redundant with being able to set that via DMLOptions.

Suggestion: add call of trim() to this line

Hi James,
awesome work on this repo,

I've recently played around specificly with this Query.cls class

return '\'' + String.escapeSingleQuotes(input) + '\'';

And I used it to create a where clause with a Set of Ids to finally get a query like this:

SELECT 
	Id,
	Name
FROM Account  
WHERE Id IN ('0010O000028pTbxQAE','0011l00000jpAV3AAM','0013X00002fmFN1QAM')

But I discovered, that the method mentioned puts a leading space in front of the second and all following parts of the list (for whatever reason), so the query looks like this:

SELECT 
	Id,
	Name
FROM Account  
WHERE Id IN ('0010O000028pTbxQAE',' 0011l00000jpAV3AAM',' 0013X00002fmFN1QAM')

This obviously leads to an error: "System.QueryException: invalid ID field"

So what I've done in my modification of your code - I've added a call to trim().

 return '\'' + String.escapeSingleQuotes(input).trim() + '\''; 

What's your opinion? Maybe trim() is just a quick fix?

Source: Apex Developer Guide String Class trim

System.NullPointerException in AccountHandlerTests

Hi James,
I tried to run the AccountHandlerTests but throwing System.NullPointerException and I have no idea where to fix it.
Could you please advise ? I tried to play around with ExampleFactory and AccountHandlerTests but still can't figure out the solution.
Sorry that I am a noob and just started learning apex programming.

Add readme of how to create all your classes to SFDC

I really wanted to try use your dml mocking framework in my sandbox.
I've tried creating apex classes one by one in these order :

  1. DML classes
  2. TestingUtils
  3. Factory classes
  4. Repo classes

However at step 3 when creating the RepoFactory class, I get this error:
force-app/main/default/classes/factory/RepoFactory.cls Invalid type: FieldLevelHistoryRepo (8:36)

So I tried creating the FieldLevelHistoryRepo class , I get this error :

force-app/main/default/classes/repository/FieldLevelHistoryRepo.cls  Invalid type: AggregateRepository (1:68)                                                                  
force-app/main/default/classes/repository/FieldLevelHistoryRepo.cls  @Override specified for non-overriding method: Set<String> FieldLevelHistoryRepo.addSelectFields() (32:42)

So I tried creating the AggregateRepository class, I get this error :

force-app/main/default/classes/repository/AggregateRepository.cls  Invalid type: Repository (1:66)                                                                             
force-app/main/default/classes/repository/AggregateRepository.cls  @Override specified for non-overriding method: String AggregateRepository.getFinalQuery(List<Query>) (82:29)
force-app/main/default/classes/repository/AggregateRepository.cls  @Override specified for non-overriding method: Set<String> AggregateRepository.addSelectFields() (65:42)   

So I'm essentially stuck trying to create all these classes effectively. Help pls?

  • Is there a better way than creating these apex classes one by one?
  • Is there a more simplified framework that doesn't involve Aggregation, FieldHistory?

I have an issue in AccountHandlerTests

Hello James,

I just push all the classes in a new Salesforce Org, and I launched the method it_should_insert_new_opps_when_accounts_are_inserted.

And I had this error :
image

Thanks for your help.

Salesforce String gets shortened

List<String> setInner = predValue.substring(1, predValue.length() -1).split(',');

Hi @jamessimone
I have the next funny thing for you:

       String predValue = String.valueOf(predicate);
        //fun fact - you can detect a list
        //but you can't detect a set!
        if(predValue.startsWith('{') && predValue.endsWith('}')) {
            List<String> setInner = predValue.substring(1, predValue.length() -1).split(',');
            isSet = setInner.size() > 1;
            return this.getPredicate(setInner);
        }

the variable "predValue" gets shortened ... I've just played around with a Set of more than 30 Ids ... this piece of code takes 11 ids, where is last value is '...' (which is why it failed and I actually discovered it)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.