1

I'm trying to understand something so I can use @testSetup annotation wisely. As i read here:

https://developer.salesforce.com/docs/atlas.en-us.202.0.apexcode.meta/apexcode/apex_testing_testsetup_using.htm

By setting up records once for the class, you don’t need to re-create records for each test method. Also, because the rollback of records that are created during test setup happens at the end of the execution of the entire class, the number of records that are rolled back is reduced

But if you run a test class, you can see that all of the DMLs are recreate for each test method and not just once.

So what is the purpose of using it if I need to add an extra SOQL to retrieve the data I created in each test method? why not just using it in the test class itself and use it directly? just to reduce a code duplication?

Edit: Seems like I was confused because of what I read here:

https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_annotation_testsetup.htm

If a test class contains a test setup method, the testing framework executes the test setup method first, before any test method in the class.

Thanks for your comment Phil.

1
  • 1
    The (somewhat) well known workaround to stop @testSetup from consuming governor limits for your test methods is to use Test.startTest() and Test.stopTest() in your @testSetup method. Turns out that you can use that in @testSetup and still be able to use it in each individual test method.
    – Derek F
    Jan 3, 2022 at 12:15

3 Answers 3

7

The Test Setup method runs once and only once for the given test class (at least when running all tests; I'm not sure what happens if you run tests selectively, by method name).

When a test class is to be executed, if there's a setup method this is called once, always before any of the test methods are invoked (with these later running sequentially or in parallel, based on your settings). At the end of the setup execution the unit testing infrastructure effectively sets a database "save point". Each individual test is then run with the database set to that "save point". After all (relevant) tests are run, the database is reset to the state it was before running the setup method.

Note that each test is run in an isolated transaction on top of the "save point". It is for this reason that static variables set in the setup method are not retained.

This has several advantages:

  1. The expensive DML operations are performed just once for a given test class (see comment above). This makes unit tests run faster. Sure, you have to run SOQL queries to retrieve the required record IDs you previously inserted, but this is far less expensive so you still win.
  2. The setup is performed with some of its own governor limits (take a look at this Q&A for more info).
  3. Since database isolation is a bit flaky in unit tests in certain cases (for example, when inserting custom settings records), this can cause DML errors when running tests in parallel (which you will typically want to do). By reducing the number of times data is inserted you minimize the opportunity to be tripped up by this.

The only real disadvantage is complexity in retrieving the IDs and data which necessarily happens in one or more other methods outside the setup (you could vote for this idea, btw). We address this by having specific method(s), that we organize in the code to be next to the test setup method, that "get" the required record(s).

2
  • Well so it seems I didn't understand this sentence well: developer.salesforce.com/docs/atlas.en-us.apexcode.meta/… - "If a test class contains a test setup method, the testing framework executes the test setup method first, before any test method in the class". Thanks!!
    – Benj
    Jan 3, 2022 at 14:28
  • @Benj a minor correction: if you use Test.startTest before doing anything else n your testSetup method, you will get your full governor limits for each unit test called.
    – sfdcfox
    Jan 3, 2022 at 19:27
2

testSetup methods only run once for all the tests in a test class. The logs are duplicated so you can get a full debug session, such as with the debug log replay function in VS Code. Using testSetup will drastically improve performance of you tests, since DML operations can be very expensive. Make sure you use Test.startTest at the very beginning of your testSetup method, or any governor limits you use will be counted against you at the start of each unit test.

0

One benefit is the one you mentioned - less duplicated code, but it also should be faster. Here you can read:

The idea here is not to reduce the number of SOQL queries, it is to reduce the amount of data being inserted into the system. If you insert 1000 records in test setup, run fifteen test methods, and you run a query 15 times to get the 1000 records each time, that's still less expensive (and faster) than inserting 1000 records 15 times.

2
  • But you run the testSetup for each test method anyway, so it means you are inserting all data again for each test method, isn't it?
    – Benj
    Jan 3, 2022 at 11:38
  • @Benj that's not the case. If you have a test class with multiple tests and a testSetup method, and run the entire test class, the testSetup is executed once. Each test does its thing and rolls back the changes.
    – Szosteczka
    Jan 4, 2022 at 9:56

You must log in to answer this question.

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