Skip to main content

Get Data for Your Tests

Learning Objectives

After completing this unit, you’ll be able to:

  • Describe why creating data for tests is a best practice.
  • Create data specifically for unit tests.

Why Create Test Data?

A lot of developers are passionate about test data, and for good reason. Creating test data is one of the most important aspects of writing useful unit tests.

Watch this video to learn more about why we create test data as part of our unit tests.

Salesforce records that are created in test methods aren’t committed to the database. They’re rolled back when the test finishes execution. This rollback behavior is handy for testing because you don’t have to clean up your test data after the test executes.

By default, Apex tests don’t have access to preexisting data in the org, except for access to setup and metadata objects, such as the User or Profile objects. Creating test data makes your tests more robust and prevents failures that are caused by missing or changed data in the org. You can create test data directly in your test method, or by using a utility test class as you’ll find out later.

Note

Even though it’s not a best practice to do so, there are times when a test method needs access to preexisting data. To access org data, annotate the test method with @IsTest(SeeAllData=true). The test method examples in this unit don’t access org data and therefore don’t use the SeeAllData parameter.

Two crucial aspects of a useful test are that it’s descriptive and that every run results in the same outcome. A test that you run multiple times should always either fail or pass, unless you’ve changed the underlying code. Likewise, it should always be clear why a test failed.

For these reasons, it’s tempting to use existing data from your production org, such as accounts or contacts, to run your tests. But that’s not a great idea. The Salesforce Platform prevents developers from editing Apex classes in production orgs, so to write or edit tests, you have to use a development environment. That is, you have to use either a sandbox snapshot of your production org, a scratch org, or a Developer Edition org.

Naturally, the data in your production org changes over time and will be different from the data in your developer environment. When your tests run, either in a developer environment or in production, they have access only to the data in that org. So if you write tests using existing data, you can end up with deployment problems, because the data available in one environment isn’t always available in another environment.

That’s why it’s important for every test to create its own records. There are several methods for creating test data. In this unit we discuss four of them.

  • Brute force
  • Test factories
  • @TestSetup methods
  • CSV data files

Brute Force

This method consists of you creating and inserting the necessary records manually on each test.

  1. Open VS Code.
  2. In the Explorer sidebar, right-click the folder classes, then choose SFDX: Create Apex Class.
  3. Enter DataGenerationTests as the name and accept the default directory.
  4. Replace the contents of the class with the following code.
    @IsTest
    private class DataGenerationTests {
      @IsTest
      static void testBruteForceAccountCreation() {
        // GIVEN
        List<Account> accts;
        // WHEN
        Test.startTest();
          accts = [SELECT Id FROM Account];
        Test.stopTest();
        // THEN
        Assert.isTrue(accts.size() > 0, 'Was expecting to find at least one account');
      }
    }
  5. Click File | Save.
  6. Right-click the DataGenerationTests file, then choose SFDX: Deploy Source To Org. If the SFDX: Deploy Source to Org option is unavailable, ensure you authorized your Trailhead Playground.
  7. Click the Run Test button that appears on the testBruteForceAccountCreation method of the Apex Class you have just created and deployed.
  8. Note the test result outcome in the Output panel. Uh oh, our Apex test failed! 😯

Because this test creates no accounts, the test run fails. To fix that, let's look at what the test is doing.

A Note on Test.startTest

Part of what makes the Salesforce platform so great are the governor limits Salesforce has built into it. These governor limits prevent any one user or org from using all the system resources. The limits ensure, for instance, that Apex code has only 10 seconds to execute.

Because these limits apply even to unit test execution, it can be challenging to test code in the allotted time. If, for example, you need to insert and query a lot of records while setting up your test, you could hit a governor limit when you actually call the code being tested. That can lead to a false-failure, where your test fails, but not because your code is wrong.

Using the Test.startTest and Test.stopTest methods lets you tell the platform when you’re calling the code being tested. Functionally, this isolates your tested code by resetting governor limits from the startTest() call until the stopTest() call. For more information on governor limits, check out the link in the Resources section.

If you want your test to pass, you need to create some accounts.

  1. Go back to your test class in VS Code.
  2. Insert a new line after List<Account> accts;, and on this line, place the following code.
    Account acct = new Account(name='McTesterson LLC');
    insert acct;
  3. Save your class and repeat steps 5 through 8 above.

This time, the test should pass.

This is effectively brute-forcing the solution. Before you call the underlying code that you’re testing, you’re creating the necessary data by creating and inserting objects. Brute force can work well, but it can lead to lengthy tests that are hard to follow. This is especially true when you start generating records that are related to other records and contain lots of field details.

Test Factories

To make your tests simpler and easier to follow, you can move your data creation into a reusable class. These reusable classes, called data factories, contain methods that model the creation of one or more objects. You don’t even have to create your own test factory! A number of open-source test factories are available online.

Let’s see what a basic test factory looks like, and how to use it.

  1. Open VS Code.
  2. In the Explorer sidebar, right-click the folder classes, then choose SFDX: Create Apex Class.
  3. Name the class TestFactory and accept the default directory.
  4. Replace the contents of the TestFactory class with the following code.
    @IsTest
    public class TestFactory {
      public static Account getAccount(String name, Boolean doInsert) {
        Account acct = new Account(name = name);
        if(doInsert) {
          insert acct;
        }
        return acct;
      }
      public static Contact getContact(
        Id accountId,
        String firstName,
        String lastName,
        Boolean doInsert
      ) {
        Contact con = new Contact(firstName = firstName, lastName = lastName, accountId = accountId);
        if(doInsert) {
          insert con;
        }
        return con;
      }
      public static void generateAccountWithContacts(Integer numContacts) {
        Account acct = getAccount('default account ltd', true);
        List<Contact> contacts = new List<Contact>();
        for(Integer i = 0; i < numContacts; i++) {
          String contactName = 'Contact ' + i;
          contacts.add(getContact(acct.id, contactName, contactName, false));
        }
        insert contacts;
      }
      public static Opportunity[] generateOppsForAccount(
        Id accountId,
        Decimal amount,
        Integer numOpps
      ) {
        List<Opportunity> opps = new List<Opportunity>();
        for(Integer i = 0; i < numOpps; i++) {
          Opportunity opp = new Opportunity();
          opp.name = 'Account ' + i;
          opp.accountId = accountId;
          opp.amount = amount;
          opp.closeDate = Date.today().addDays(5);
          opp.stageName = 'Prospecting';
          opps.add(opp);
        }
        return opps;
      }
      public static Account[] generateAccountsWithOpps(Integer numAccts, Integer numOppsPerAcct) {
        List<Account> accts = new List<Account>();
        for(Integer i=0;i<numAccts;i++) {
          Account acct = getAccount('TestAccount' + i, false);
          accts.add(acct);
        }
        insert accts;
        // For each account just inserted, add opportunities
        List<Opportunity> allOpps = new List<Opportunity>();
        for(Integer j=0;j<numAccts;j++) {
          Account acct = accts[j];
          List<Opportunity> opps = generateOppsForAccount(acct.id, 1000.00, numOppsPerAcct);
          allOpps.addAll(opps);
        }
        // Insert all opportunities for all accounts.
        insert allOpps;
        return accts;
      }
      public static User generateUser(String profileName) {
        User userTest = new User(
          ProfileId = [SELECT Id FROM Profile WHERE Name = :profileName].Id,
          LastName = 'Last',
          Email = 'Cpt.Awesome@awesomesauce.com',
          Username = 'Cpt.Awesome.' + DateTime.now().getTime() + '@awesomesauce.com',
          CompanyName = 'Testing Co',
          Title = 'Captain',
          Alias = 'alias',
          TimeZoneSidKey = 'America/Los_Angeles',
          EmailEncodingKey = 'UTF-8',
          LanguageLocaleKey = 'en_US',
          LocaleSidKey = 'en_US'
        );
        insert userTest;
        return userTest;
      }
    }
  5. Click File | Save.
  6. Right-click the file you’re working on, then choose SFDX: Deploy Source To Org.

Code Highlights

The test factory above isn’t very sophisticated, but you get the idea. The test factory class is built from a group of methods that generate and optionally insert data for use in testing. Notice the first line: @IsTest? That means you can use the class and its methods only in the context of unit tests. Additionally, classes annotated with @IsTest don’t count against your organization limits on the amount of Apex you can use.

Your class offers methods to create accounts, contacts, and, more importantly, an account with five contacts. That is where data factories can really start to shine—in creating networks of related objects with a single line of code.

To use your test factory, modify your DataGenerationTests.

  1. Open VS Code.
  2. In the Explorer sidebar, click the folder classes.
  3. Select the class DataGenerationTests and open it.
  4. Place the following code snippet after your first test.
    @IsTest
    static void testUseTestFactoryToCreateAccountsWithContacts() {
      // GIVEN
      List<Account> accts;
      List<Contact> contacts;
      TestFactory.generateAccountWithContacts(5);
      // WHEN
      Test.startTest();
        accts = [SELECT Id FROM Account];
        contacts = [SELECT Id FROM Contact];
      Test.stopTest();
      // THEN
      Assert.isTrue(accts.size() > 0, 'Was expecting to find at least one account');
      Assert.areEqual(5, contacts.size(), 'Was expecting to find 5 contacts');
    }
  5. Click File | Save.
  6. Right-click the file you’re working on, then choose SFDX: Deploy Source To Org.
  7. Click the Run Test button that appears on the testUseTestFactoryToCreateAccountsWithContacts method of the Apex Class you have just created and deployed.

Code Highlights

As in your first test, you’re checking to ensure that your test is creating test data. In this case, however, you’re using your test factory class to create the data.

TestSetup Methods

When we create tests, we want to keep them as succinct and easy to follow as possible. Separating data creation into a test factory helps, but we can go one step further.

Multiple test methods in the same class often have similar data requirements. Because of this, the Salesforce Platform offers a way for you to annotate methods in your test class as @TestSetup methods. The platform calls these methods automatically before every individual test method. Because they’re called before every test method in the class, they’re perfect for creating test data. Note, however, that the data is reset in between each and every test method's execution.

Let’s create a @TestSetup method.

  1. Go back to your DataGenerationTests class in VS Code.
  2. Add the following setup method at the top of the file, just under the line saying private.
    @TestSetup
    static void dataCreation() {
      // GIVEN
      Account acct = TestFactory.getAccount('Muddy Waters Inc.', true);
      Contact con = TestFactory.getContact(acct.id, 'Muddy', 'Waters', true);
      List<Opportunity> opps = TestFactory.generateOppsForAccount(acct.id, 1400.00, 1);
      insert opps;
    }
  3. Click File | Save.
  4. Right-click the file you’re working on, then choose SFDX: Deploy Source To Org.
  5. Click the Run All Tests button that appears on the DataGenerationTests class.

Uh-oh, adding this @TestSetup method broke the second test method. Specifically, the assertion failed with the error message: “Assertion Failed: Was expecting to find 5 contacts.”

The test failed because your @TestSetup method creates a contact before the platform runs the test method. That contact, combined with the 5 contacts your test method creates, means that your test’s query finds 6, not 5, contacts. These additional records demonstrate the platform’s execution of @TestSetup methods prior to actually executing test methods. They also demonstrate the ability of the @TestSetup method to create records for testing.

To fix your failing test, modify the assertions to say 6, rather than 5.

  1. Go back to your DataGenerationTests class in VS Code.
  2. Find the line of code that reads:
    Assert.areEqual(5, contacts.size(), 'Was expecting to find 5 contacts');
  3. Modify that line of code to specify 6 contacts, like this.
    Assert.areEqual(6, contacts.size(), 'Was expecting to find 6 contacts');
  4. Save the file and deploy it to your org.
  5. Click the Run All Tests button that appears on the DataGenerationTests class.

Code Highlights

This @TestSetup method is pretty basic, but it does incorporate some of what you’ve already done; namely, use your test factory to generate data.

Lets add a new testMethod to look for opportunities.

  1. Go back to your DataGenerationTests class in VS Code.
  2. Create a new test method with the following code:
    @IsTest
    static void testAtTestSetupMethodsRule() {
      // WHEN
      Test.startTest();
        List<Opportunity> opps = [SELECT Id, AccountId FROM Opportunity];
      Test.stopTest();
      // THEN
      Assert.areEqual(1, opps.size(), 'Expected test to find a single Opp');
    }
  3. Save the file and deploy it to your org.
  4. Click the Run All Tests button that appears on the DataGenerationTests class.

Code Highlights

Notice this third test method passes, even though it’s not creating data. In this test method, you’re relying on the platform to pre-execute the @TestSetup method, which creates the opportunity you need for the test to pass. Note, even though the @TestSetup method creates the test data, you still have to query for that data within your test. This can be confusing when you first start using @TestSetup methods.

CSV Data Files

Using the @TestSetup methods allows for concise, focused tests, but you can go yet another step further with your test data. You can create and upload a CSV file as a static resource and generate test data directly from that CSV.

Imagine a scenario where you can expand the scope of your tests without changing a line of code. CSV data lets you add to or modify the data inputs your code deals with, without deploying code.

  1. Using Visual Studio Code, paste the following into a file and save it as accountData.csv.
    Name,Website,Phone,BillingStreet,BillingCity,BillingState,BillingPostalCode,BillingCountry
    sForceTest1,http://www.sforcetest1.com,(415) 901-7000,The Landmark @ One Market,San Francisco,CA,94105,US
    sForceTest2,http://www.sforcetest2.com,(415) 901-7000,The Landmark @ One Market Suite 300,San Francisco,CA,94105,US
    sForceTest3,http://www.sforcetest3.com,(415) 901-7000,1 Market St,San Francisco,CA,94105,US
  2. In Setup, type Static Resources in the Quick Find box and select Static Resources.
  3. Click New.
  4. Name your static resource accountData.
  5. Click Choose File and select accountData.csv.
  6. Select Public for Cache Control.
  7. Click Save.

Now create a new test class in VS Code.

  1. Open VS Code.
  2. In the Explorer sidebar, right-click the folder classes, then choose SFDX: Create Apex Class.
  3. Name the class CSVTests and replace its contents with the following code.
    @IsTest
    private class CSVTests {
      @TestSetup
      static void loadTestDataFromStaticResource() {
        // GIVEN
        List<sObject> accounts = Test.loadData(Account.SObjectType, 'accountData');
      }
      @IsTest
      static void testLoadAccountsFromStaticResource() {
        // WHEN
        Test.startTest();
          List<Account> accts = [SELECT ID FROM Account];
        Test.stopTest();
        // THEN
        Assert.isTrue(accts.size() == 3, 'Expected 3 accounts');
      }
    }
  4. Click File | Save.
  5. Right-click the file you’re working on, then choose SFDX: Deploy Source To Org.
  6. Click the Run All Tests button that appears on the CSVTests class.

Code Highlights

This code still uses an @TestSetup method. But instead of using a testFactory or brute forcing it, you’re simply loading the CSV file. This makes for super-clean tests and lets you add to or modify your test data without deploying code.

Resources

在 Salesforce 帮助中分享 Trailhead 反馈

我们很想听听您使用 Trailhead 的经验——您现在可以随时从 Salesforce 帮助网站访问新的反馈表单。

了解更多 继续分享反馈