3

Some time ago, I asked about this topic in the post Should be Triggers inactive by default in Unit Tests? However, my question wasn't precise enough.

I'd like to pose the question once again, this time being more precise:

Should triggers be inactive by default during the creation of test data?

Let's assume that I need to create test data and I'm unable to mock my tests. When creating test data in the @TestSetup method, it shouldn't trigger the execution of trigger logic. This can sometimes pose problems due to long-running logic, which might affect deployment times and could even lead to SOQL/DML limit exceptions before the tests begin, especially in large projects with multiple teams.

Next disadvantage: Integration logic within triggers. I would like to test my controller that has a simple query, but I need to mock integration since triggers contain it.

Do you have any concerns regarding this approach?

Talk is cheap, show me the code:

@IsTest
private class MyControllerTest {

   @TestSetup
   static void setup() {
       TriggerFramework.disableAllTriggers();
       // create Account
       // create Contact
       // etc
       TriggerFramework.enableAllTriggers();
   }

   @IsTest
   static void getAccountsTest() {
      // get test data 

      Test.startTest();
      // my test
      Test.stopTest();

      // asserts
   }
}

Perhaps the Test Data Creation framework could include a built-in function like TriggerFramework.disableAllTriggers();, so that developers wouldn't need to explicitly invoke TriggerFramework.disableAllTriggers(); and TriggerFramework.enableAllTriggers(); every time in @TestSetup.

9
  • 2
    Just an opinion than an answer, so posting as a comment. For me, the only advantage of these approach is to test how triggers will behave when dealing with historical data that were created in an org beforehand. In any other cases, triggers should not be disabled. I understand the concern, but some flows/prcesses can be triggered by changes in triggers in consequent update, for example (and if you create your test data as it will look like after all trigger logic even, it won't trigger necessary flows/processes). Commented Aug 28, 2023 at 7:56
  • Thank you for your comment! You're absolutely correct. However, I primarily focus on simple logic. For instance, I need to test a method in my controller that involves a straightforward query and perhaps some logic to handle it. My intention isn't to validate other processes, triggers, etc. I simply want to test my concise method. Naturally, all trigger handlers should possess their own tests, ensuring that the triggers are consistently active. This way, we can be certain that the essential flows and processes are functioning as anticipated. Commented Aug 28, 2023 at 8:07
  • Understood you. Still, in my opinion, if your test classes do not hit Apex CPU Limit, then I'd rather suggest wait a little longer than add new condition logic to your trigger handlers, which will increase running time on the real cases on production. Commented Aug 28, 2023 at 8:14
  • The production code will not have additional logic that make it running longer, no, no. We are talking here only about unit test data creation. Commented Aug 28, 2023 at 8:19
  • 1
    If you change your unit testing approach to entirely avoid inserting data, except where you want to explicitly test or include trigger execution, this question become moot. And you will find your unit tests much, much faster. This does require appropriate structuring of your code, including separate classes (or virtual methods) for queries and DML and shunning static methods, plus some form of dependency injection, all to allow mocking, but this is a good way to work in general.
    – Phil W
    Commented Aug 28, 2023 at 10:04

1 Answer 1

2

This is opinion-based and solely from my own experience

In general, I never disable triggers before test methods but this is because every project I work on has been converted to the Enterprise Patterns model (fflib) that enables me to, at test time ..

  • Define and inject mock selectors so the code I'm testing can be presented with mock Sobjects (as if a real SOQL query were executed)
  • Define and inject a mock UnitOfWork such that my code-under-test "registers" to do DML and such registration can be verified using ApexMocks methods (or Amoss-equivalents if you like that framework)
  • Define and inject mock service objects so that (given the services are separately unit tested), I can verify that the service is called with the right arguments (ApexMocks again) and/or have a service return a mock result

Now, as per your comments, you have projects not so adapted to mockable, dependency-injectable objects. In that case, disabling triggers is roughly equivalent to mocking SObjects that would be returned by a Selector layer. That is, you're deciding what minimal set of values you need in the database for your code-under-test's SOQL to return.

  • You still need to pass Validation Rules (VR)
  • Your workflows, Process builders, and flows will still execute. This could include callouts (which would have to be mocked)
  • You may easily find that (especially without mocked services and units of work) that code under test fails because you didn't create a wide enough span of field values in test setup.

You could consider (something else I've done to support data repair) is to gate all triggers and point-and-click automation with a do-not-run gate (in my orgs, this is based on a hierarchical custom setting assigned to only a special "service-account-type" user). Here, you only need pass VRs. Thus, rather than disable triggers in test methods, your whole code+automation base can execute in a "without consequences data load"

Finally (again, opinion-based)

The fflib Enterprise Patterns model (or ones like it) is so powerful at addressing unit test issues. If all your code is either a domain, service, or selector, and all DML is routed through the Unit Of Work layer, virtually every unit testing issue can be addressed through dependency injection of mock versions of these layers. (yes, there are exceptions to this). Integration testing can be a mix of real DML and mocked services/selectors.

1
  • +1 for the hierarchical custom settings, we are using this approach too, very convenient Commented Aug 28, 2023 at 20:12

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .