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:
- 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.
- The setup is performed with some of its own governor limits (take a look at this Q&A for more info).
- 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).
@testSetup
from consuming governor limits for your test methods is to useTest.startTest()
andTest.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.