[SalesForce] use testsetup for common methods in test class

As per my understanding and salesforce documentation, class annotated with @isTest should contain "only test methods". But there are some situations where we might need to have utility methods and these cannot be put in test method. For example, consider a requirement where account record has to be inserted by 2 different user profiles. Usually, I create the Account record in the following way:

static Account createAccRec() {
    Account a = new Account();
    a.name = 'test';    
    return a;
}

and then, actual test method looks like this:

@isTest
static void createAccountasAgent1() {
    user agent1 = [select id from user where profile.name = 'agent1' and isActive=true Limit 1];
    system.runAs(agent1) {
        insert a;
    }
    system.assertEquals(null, [select id from account where name='test'].Id);
}

@isTest
static void createAccountasAgent2() {
    user agent1 = [select id from user where profile.name = 'agent2' and isActive=true Limit 1];
    system.runAs(agent2) {
        insert a;
    }
    system.assertNotEquals(null, [select id from account where name='test'].Id);
}

So, now this test class has 2 proper test methods and one static utility method to create data. Not sure if this will affect over all code usage but I tried to modify the non test method using @TestSetup as follows so that now this test class has only test methods:

@testsetup
static void createAccount() {
    Account a = new Account();
    a.name = 'test';
}

However, using @TestSetup followed by actual test method does not work and throws an error:

Variable does not exist : a

Is there a way to overcome this issue?

Best Answer

I disagree strongly with your assessment of how @IsTest annotated classes should work.

As per my understanding and salesforce documentation, class annotated with "@isTest" sshould [sic] have "only test methods".

As you note, you need to be able to write test utilities. For example the following is perfectly acceptable:

@IsTest
public with sharing class AccountTestUtility
{
    public Account buildAccountWithName(String name)
    {
        return new Account(Name=name);
    }
}

It could certainly be made more generic and flexible, but there's no problem with the method not being a test method.


As for coverage, the R&D team should be working on fixing this Known Issue: Non-test methods in a test class are counted as part of Apex code Coverage. Note, however, that @TestSetup annotation is not a workaround, and suffers the same limitation (bug).


Regardless, when you use @TestSetup, the only thing you can do is create records that will exist when your tests run. Static and instance variables you set in this method will not still be set when your tests run.

If you would like to make it possible to have static variables you can set from @TestSetup methods, vote for this Idea: Remember static variables set during testSetup annotated methods.

However, it looks unlikely to be released. The last comment from the product team:

We intentionally clear out static variables between each test method. If we did not, each test would cease to be an independent trial. You could modify the static in one test method, which would make the order in which tests operate relevant to the results. This is precisely what you don't want - data dependent tests.

If you want information that is common to all tests, it can be inserted in the test setup method and queried in each test method. 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.

Related Topic