Apex 测试(3)

学习目标

完成本单元后,您将能够:

  • 创建一个测试工具类。
  • 使用测试实用程序方法为各种测试用例设置测试数据。
  • 执行一个类中的所有测试方法。

为Apex测试创建测试数据

使用测试实用程序类为测试数据设置添加可重用的方法。

先决条件

如果您还没有这样做,请完成上一个单元“测试Apex触发器”中的先决条件。

添加一个测试工具类

让我们通过用一个实用程序类方法的调用替换测试数据创建来重构以前的测试方法。首先,您需要创建测试实用程序类。

TestDataFactory类是一种特殊的类,它是一个公共类,它是用isTest注释的,只能从正在运行的测试中访问。测试工具类包含可以被测试方法调用来执行有用任务的方法,例如设置测试数据。测试工具类从组织的代码大小限制中排除。

要添加TestDataFactory类:

  1. 在开发者控制台中,点击 File | New | Apex Class, 然后输入TestDataFactory作为类名,然后单击OK。
  2. 用下面的代码替换默认的类体。
    @isTest
    public class TestDataFactory {
        public static List<Account> createAccountsWithOpps(Integer numAccts, Integer numOppsPerAcct) {
            List<Account> accts = new List<Account>();
            
            for(Integer i=0;i<numAccts;i++) {
                Account a = new Account(Name='TestAccount' + i);
                accts.add(a);
            }
            insert accts;
            
            List<Opportunity> opps = new List<Opportunity>();
            for (Integer j=0;j<numAccts;j++) {
                Account acct = accts[j];
                // 对于刚插入的每个帐户,添加机会
                for (Integer k=0;k<numOppsPerAcct;k++) {
                    opps.add(new Opportunity(Name=acct.Name + ' Opportunity ' + k,
                                           StageName='Prospecting',
                                           CloseDate=System.today().addMonths(1),
                                           AccountId=acct.Id));
                }
            }
            // 插入所有帐户的所有机会
            insert opps;
            
            return accts;
        }
    }
    此测试实用程序类包含一个静态方法createAccountsWithOpps(),该方法接受numAccts参数中保存的帐户数量以及为每个帐户(保存在numOppsPerAcct参数中)创建的相关机会数量。方法中的第一个循环创建了指定数量的帐户并将其存储在accts列表变量中。在第一个循环之后,将调用insert()DML语句在数据库的列表中创建所有帐户。

第二个循环创造了机会。由于每个机会组都链接到一个客户,所以外部循环通过客户进行迭代,并包含一个嵌套的循环,为当前客户创建相关的机会。下次运行嵌套循环时,使用add()方法将机会添加到同一个列表中。使用AccountId字段将机会链接到其父帐户。创建的所有机会总数是机会数量与客户数量的乘积(numOppsPerAcct * numAccts)。接下来,在循环之外有效地调用insert()DML语句,以便仅在一个调用中为所有帐户创建集合中的所有机会。

最后,这个方法返回新帐户的列表。

注意

尽管此方法不会返回相关的机会,但您可以通过编写SOQL查询来获取这些记录,该查询利用Account和Opportunity之间的现有关系,例如Testing Apex Triggers中的触发器中使用的查询。

调用测试数据创建的实用程序方法

现在您已经添加了测试实用程序类,请修改测试类以利用此类。在TestAccountDeletion类中,替换以// Test data setup开始的块,并以insert opp结束:

        // 测试数据设置
        // 通过调用实用程序方法创建一个拥有一个机会的帐户
        Account[] accts = TestDataFactory.createAccountsWithOpps(1,1);
TestDataFactory.createAccountsWithOpps(1,1)调用返回的数组包含一个Account sObject。

这是修改后的测试方法。更短的版本!

@isTest
private class TestAccountDeletion {

    @isTest static void TestDeleteAccountWithOneOpportunity() {
        // 测试数据设置
        // 通过调用实用程序方法创建一个拥有一个机会的帐户
        Account[] accts = TestDataFactory.createAccountsWithOpps(1,1);
        
        // 执行测试
        Test.startTest();
        Database.DeleteResult result = Database.delete(accts[0], false);
        Test.stopTest();

        // 验证删除是否应该被触发器停止,
        // 检查我们是否收到错误
        System.assert(!result.isSuccess());
        System.assert(result.getErrors().size() > 0);
        System.assertEquals('不能删除有相关机会的帐户.',
                             result.getErrors()[0].getMessage());
    }        
}

测试不同的条件

一种测试方法不足以测试触发器的所有可能输入。我们需要测试一些其他条件,例如何时删除没有机会的客户。我们还需要使用批量数量的记录来测试相同的方案,而不是只记录一个记录。这里是包含三个附加测试方法的测试类的更新版本。保存此类更新的版本。

@isTest
private class TestAccountDeletion {

    @isTest static void TestDeleteAccountWithOneOpportunity() {
        // 测试数据设置
        // 通过调用实用程序方法创建一个拥有一个机会的帐户
        Account[] accts = TestDataFactory.createAccountsWithOpps(1,1);
        
        // Perform test
        Test.startTest();
        Database.DeleteResult result = Database.delete(accts[0], false);
        Test.stopTest();

        // 验证删除是否应该被触发器停止,
        // 检查我们是否收到错误
        System.assert(!result.isSuccess());
        System.assert(result.getErrors().size() > 0);
        System.assertEquals('不能删除有相关机会的帐户',
                             result.getErrors()[0].getMessage());
    }
    
    @isTest static void TestDeleteAccountWithNoOpportunities() {
        // 测试数据设置
        // 通过调用实用程序方法创建一个没有机会的帐户
        Account[] accts = TestDataFactory.createAccountsWithOpps(1,0);
        
        // 执行测试
        Test.startTest();
        Database.DeleteResult result = Database.delete(accts[0], false);
        Test.stopTest();

        //验证删除是否成功
        System.assert(result.isSuccess());
    }
    
    @isTest static void TestDeleteBulkAccountsWithOneOpportunity() {
        // 测试数据设置
        // 通过调用一个实用程序方法创建每个客户
        Account[] accts = TestDataFactory.createAccountsWithOpps(200,1);
        
        // 执行测试
        Test.startTest();
        Database.DeleteResult[] results = Database.delete(accts, false);
        Test.stopTest();

        // 验证每个记录。
        // 在这种情况下,删除应该已经被触发器停止,
        // 检查我们是否收到错误
        for(Database.DeleteResult dr : results) {
            System.assert(!dr.isSuccess());
            System.assert(dr.getErrors().size() > 0);
            System.assertEquals('不能删除有相关机会的帐户',
                                 dr.getErrors()[0].getMessage());
        }
    }
    
    @isTest static void TestDeleteBulkAccountsWithNoOpportunities() {
        // 测试数据设置
        // 通过调用实用程序方法创建没有机会的帐户
        Account[] accts = TestDataFactory.createAccountsWithOpps(200,0);
        
        // 执行测试
        Test.startTest();
        Database.DeleteResult[] results = Database.delete(accts, false);
        Test.stopTest();

        // 对于每条记录,验证删除是否成功
        for(Database.DeleteResult dr : results) {
            System.assert(dr.isSuccess());
        }
    }
}
运行所有测试方法

最后一步是在我们的测试类中运行测试方法,现在该类包含更全面的测试,并被重构为使用测试数据工厂。由于您已经在TestAccountDeletion类中运行了测试,因此您可以重新运行此测试类以运行其所有测试方法。

  1. 要执行相同的测试运行,请单击测试选项卡,选择您的测试运行,然后单击Test | Rerun.
  2. 通过展开最新的测试运行来检查“测试”选项卡中的结果。测试运行应报告所有四个测试通过!