DEX455

Apex 测试完全指南:单元测试、代码覆盖与 Mock 框架

课程介绍

Apex测试完全指南:测试理念(TDD推荐/部署要求75%覆盖率+触发器覆盖+编译成功/隐私保护)、测试什么(单条+200批量/正向+负向+边界/runAs权限)、@isTest(SeeAllData+isParallel)/测试数据瞬时化/@testSetup共享数据+Test.loadData CSV、@TestVisible访问私有成员、runAs(user)(混合DML豁免/记录所有权)、startTest+stopTest(Governor Limit重置/异步同步执行/最多1Batch)、Limits监控、SOSL测试(Test.setFixedSearchResults)、并行isParallel(无数据依赖)、代码覆盖率(75%部署/System.debug+测试不计入/Developer Console按类查看/未覆盖行可能是死代码)、Stub API(StubProvider+handleMethodCall/Test.createStub/运行时动态生成无需打包)、集成测试(@IntegrationTest/放宽callout+允许事务内提交/仅Scratch Org/@TearDown清理/不能与@IsTest混合)。

课程章节

本课程共有 21 个章节

  • 1

    Testing Apex

    第 440 页

    同学们,我们今天来讲讲Apex里的测试框架,这就像是给你的代码做健康检查。 你写了一段程序,怎么知道它一定没问题呢?不能光靠感觉,得用测试来验证。Salesforce给我们提供了一个专门的环境,让你能编写单元测试,自动运行这些测试,然后看结果对不对,还能算出你的代码被测试覆盖了多少,这叫代码覆盖率。 为什么测试这么重要?因为它是长期开发成功的关键。哪怕你今天代码跑得欢,明天改了一个地方就可能崩掉。测试就像安全带,能帮你及时发现问题。 我特别建议大家,不要等代码全写完了才补测试,而是边写代码边写测试,这叫测试驱动开发。好处是,你在写代码的同时就在验证逻辑,强迫自己想清楚需求,代码质量会更高。 那么,在Salesforce平台上,如果要部署到生产环境,或者打包发布到AppExchange,有什么硬性要求呢? 首先,你的Apex代码,整体的单元测试覆盖率必须达到至少75%。注意,是所有测试都跑完加起来算的覆盖率,不是每个类单独75%。 第二,所有的测试必须成功完成,不能有任何失败的测试。一个红灯都不能有。 第三,每一个触发器,哪怕就几行,也得有测试来覆盖它,不能裸奔。 最后,所有的类和触发器都必须编译成功,这是最基本的,编译不过什么都别谈。 所以,这些要求就像是考试及格的分数线,逼着我们养成写好测试的习惯。记住了,测试不是负担,是给你信心,让你放心改代码、加功能的保障。 好,关于测试框架的基本概念和要求,我们就讲到这里。下节课我们动手写第一个单元测试。

    查看详情
  • 2

    Understanding Testing in Apex — Why Test?

    第 441 页

    各位同学,我们这节课来聊聊测试,它真的是应用程序成功的关键——尤其是当你的应用要部署给客户去使用的时候,绝对不能马虎。 那测试到底怎么做?在Salesforce里,主要有两种方法。第一种是通过界面直接操作,也就是点点按钮、填填表单,这种方式很重要,但它没办法覆盖所有可能出现的场景。第二种是批量测试,我们在Apex代码里可以一次性传递最多200条记录,来模拟大量数据同时处理时的情况。这样一来,就能检查触发器、批量作业是不是也能稳得住。 在把代码部署到生产环境之前,有个硬性要求:你的单元测试必须覆盖至少75%的Apex代码,而且所有测试方法都要跑通,不能有失败。这75%是硬杠杠,达不到就没法部署。 但要记住,你不能只盯着这个数字。即使覆盖到了75%,你也要保证业务流程里的每一种用例都测到了。什么意思呢?比如正常的happy path要测——这就是正向用例;也要测当数据不对、用户操作出错时,代码能不能正确处理——这是负面用例。另外,单个记录的处理和批量200条记录一起涌进来的情况,也都要验证。这样你才能真正放心,代码上线不会出乱子。 简单说,测试只做表面功夫不行,得把各种可能都想到,既测正常、也测异常,既测单条、也测批量,这样才能保应用稳稳当当交付给客户。 好了,这就是今天关于测试的几个核心要点,大家理解了之后,动手多写写测试类,体会会更深刻。

    查看详情
  • 3

    What to Test in Apex

    第 442 页

    同学们,这节课我们聊聊 Salesforce 里怎么写测试才够“扎实”。 Salesforce 官方其实给了非常实用的建议,它不是让你随便写几个断言就完事儿了,而是希望你的测试能真正模拟现实世界中用户的各种操作。 我们一条一条来看。 首先,你要针对 ,单个记录的操作, 来写测试。什么意思呢?比如用户只修改了一条客户信息,那你的测试就要确保在这种情况下,程序能正常工作。 然后,别光测一条,还要测 ,批量操作,。尤其是 1 到 200 条记录这个区间。因为在 Salesforce 里,触发器、批次作业经常会一次处理很多记录,如果你只测一条,可能代码在批量时直接报错,而你还不知道。所以一定要覆盖到批量的场景。 接下来是 ,正向和负向行为,。正向就是一切正常,用户做了正确的事,程序给出正确的结果。负向就是当用户做错事时,你的程序能正确阻止,并且给出,正确的验证错误消息,。你还得专门写测试来断言:哎,这个负向操作确实抛出了异常,而且异常信息就是我设计的那句话。这才能确保当用户输错数据时,他看到的是清晰友好的提示。 还有一点特别重要:,要模拟受限制的用户场景,。很多代码对系统管理员跑得顺顺利利,但换成普通用户、权限不够的时候,可能就崩溃了。所以你要用不同 Profile 的用户去跑测试,看看他们在自己的权限下是不是也能正常使用。 最后,我单独强调一个技术细节:,条件和三进制运算符,。在 Salesforce 的代码覆盖率要求里,一个 `if` 条件,或者一个 `? :` 三进制表达式,如果你只测了条件为真的那条路,它是,不完全算被覆盖,的。你必须同时执行正分支和负分支,也就是既测条件成立,也测条件不成立,这行代码才会被标记为完全覆盖。所以设计测试数据时,要故意制造两种走向的数据,确保所有逻辑分支都被走过。 这样一套组合拳下来——单条、批量、正负行为、错误消息、限制用户、全分支覆盖——才能真正保证你的应用不论碰到什么现实场景,都能稳得住,不出乱子。 好,这节课就到这里,下次咱们动手练起来。

    查看详情
  • 4

    What Are Apex Unit Tests?

    第 443 页

    同学们,这节课我们来聊聊 Salesforce 里的单元测试。你可能会想,单元测试到底是什么呢?简单来说,单元测试就是专门用来验证某段代码能不能正常工作的类方法。注意,它们跟普通方法不一样,有几个特别的规矩:首先,它们不接受任何参数;其次,它们从来不会真的往数据库里提交数据;还有,它们也不会发送电子邮件。也就是说,单元测试只专注检查逻辑,不会对外界产生任何副作用。 那在 Apex 里怎么定义一个单元测试方法呢?我们用 `@IsTest` 这个注解来标记它,而且这个方法必须放在一个同样被 `@IsTest` 修饰的类里面。以前我们用的是 `testMethod` 关键字,现在完全被 `@IsTest` 代替了,所以你看到老代码里的 `testMethod`,知道它是同一个意思就行。 测试类本身可以是 public 或者 private,这个访问修饰符无所谓,怎么写都不会影响测试的执行。但是有一个必须遵守的规则:被 `@IsTest` 标记的类必须是顶层类,它不能是接口,也不能是枚举。这个要记牢。 最后跟你说一个非常实用的好处:用 `@IsTest` 标记的类,它的代码量是不会算进你 Salesforce 组织那 6MB 的代码大小限制里的。也就是说,你写再多测试代码,也不会占用宝贵的生产代码空间,所以放心大胆地把测试写全、写好。 好了,这个点很简单,记住它的规矩和好处,咱们接着往下。

    查看详情
  • 5

    Unit Test Considerations

    第 444 页

    同学们,今天这节课我们来聊聊Apex测试方法的一些重要限制,这些限制虽然看起来多,但理解了它们,写起测试来就会顺手很多。 首先,从API 28.0版本开始,所有测试方法都必须放在专门的测试类里,不能再随意放在普通类中了。这么做是为了更好地组织代码,让测试和业务逻辑分清楚。 其次,测试方法是在隔离的环境里运行的,所以它不能直接去调用真正的Web服务。如果你想测Web服务相关的逻辑,就得使用模拟调用,也就是我们常说的Mock,来假装有外部服务的返回结果。同样的道理,测试代码里也不能真的发出电子邮件,Salesforce会自动屏蔽掉真实的邮件发送,以免在测试时意外地发出邮件。 再来,测试中创建的数据,实际上并不会真正提交到数据库。测试过程就像一个沙箱,结束之后所有数据都会被自动回滚。所以,你完全不用费心去删除测试数据,Salesforce已经帮你处理好了。但这也带来一个问题:有些记录只有在父记录提交到数据库后才会产生,比如字段历史跟踪或者FeedTrackedChange这种变更追踪记录,它们依赖于真实的提交状态,所以无法在测试里直接创建。遇到这种情况,你只能把验证点放在相关的父记录上。 还有一点容易踩的坑:静态成员变量。你在一个测试方法里修改了某个静态变量的值,下一个测试方法是不会看到这个新值的。因为各个测试方法之间是完全独立的,每次运行都会重新初始化静态变量。 最后,一定要记得检查你的测试代码,确保它不会触碰到Salesforce的调节器限制,像是DML语句的总次数、SOQL查询的行数、CPU时间等等。一旦在测试中超限,整个测试就会失败,这就得不偿失了。 把这些关键的限制牢牢记在心里,你的测试编写就会更稳定、更可靠。好,这个Slide的内容我们就讲到这。

    查看详情
  • 6

    Accessing Private Test Class Members

    第 445 页

    同学们,今天咱们来聊一个在写测试代码时特别实用的小工具——`@TestVisible` 注解。 你们可能已经发现了,在 Salesforce 里写单元测试,测试方法必须放在用 `@isTest` 标记的单独测试类里,不能和被测试的业务类混在一起。这样做的好处是代码结构清晰,但在升级旧代码时会碰到一个小麻烦:如果业务类里面有些方法是私有的、或者有些成员变量是私有或受保护的,测试类就访问不到它们了。毕竟测试类是独立的外部类呀。 这时候 `@TestVisible` 就派上用场了。它是一个注解,你可以把它放在私有或受保护的成员前面,比如成员变量、内部类(特别是有构造函数的内部类)、私有方法,甚至是自定义的私有异常上。标记了 `@TestVisible` 之后,这些原本对外隐藏的成员,就可以在测试方法里被访问到,但要注意,这只是测试上下文里的特权,生产运行的时候它们依然保持原有的访问限制,所以安全性不会丢。 这个功能在升级旧版 API 时尤其救命。举个例子,从 API 28.0 开始,Salesforce 强制要求测试方法必须放在单独的 `@isTest` 类里。假如你手里有个老类,以前把测试方法和业务逻辑写在了一起,现在要升级到新版本,就得把测试抽出去,可那些私有方法怎么测呢?一个一个改成 public 不光麻烦,还可能破坏封装。这时候直接在原类里给需要的私有成员加上 `@TestVisible`,外面的测试类就能顺顺当当调用了,省时又省力。 记住了,`@TestVisible` 能修饰的东西包括:成员变量、内部类(如果内部类有构造函数,那它也能用)、私有方法,还有私有自定义异常。基本上那些隐藏在类深处的、原本测试触不到的角落,用一个注解就能照亮。 好了,关于 `@TestVisible` 我们就讲到这里。下次写测试碰到私有成员访问不了的问题,先别急着改访问修饰符,想想这个方便的小工具。

    查看详情
  • 7

    Understanding Test Data

    第 446 页

    各位同学,咱们现在看到这一页,讲的是Apex 测试数据一些非常重要的性质。你写测试的时候,数据到底怎么回事,这个必须搞清楚,不然测试结果会跟你预想的完全不一样。 咱们一条一条来看,我尽量用大白话解释。 首先第一条,测试数据是临时的,它根本不会真的写进数据库。你看啊,咱们测试方法里插入的那些账户、联系人、机会,当下好像存在了,断言也能查得到,但只要测试方法一跑完,这些数据就自动回滚了,就好像从来没发生过一样。你不需要去写任何删除清理的代码,平台自己就帮你把痕迹抹干净了。这非常方便,也保证了生产数据库的干净。 接下来这条更关键。从 API 版本 24.0 开始,测试方法默认是有一道“数据隔离墙”的。啥意思呢?就是你的测试代码,它看不到组织里本来就存在的那些业务数据,比如你生产环境里已经有的客户、商机,测试里是查不到的。它只能看到自己在测试方法里当场创建的那些数据。这就像是把测试关在了一个只有它自己搭的小房间里,外面真实世界它看不见。 但是这条规则有个例外:像用户、简档、组织这些设置或元数据对象,它是可以访问到的。因为这些是基础配置,不是日常交易数据,所以允许测试直接使用。比如你测试里可以直接用 [SELECT Id FROM User WHERE ...] 来获取一个已有的用户,这个没问题。 那假如你测试的是一个更高版本的类,但它底层调了一个老版本的类呢?也就是如果 API 24.0+ 的测试调用了一个用 API 23.0 写的类里的方法,怎么办?规则很简单:谁调用谁来限制。虽然是那个老类的方法在跑,但数据访问的限制还是按调用者、也就是测试自己的版本来执行。也就是说,那个老类原本也许能看到组织数据,但在新版测试的调用下,它也一样得遵守隔离墙,同样看不见组织数据。这个要小心。 还有一点经常踩坑的,就是在测试里发出的 Visualforce 请求。这种请求是会打破数据隔离的。因为 Visualforce 页面运行的时候,可以访问组织的完整数据,你如果在测试里模拟一个 Visualforce 请求,它就能跳出去看到真实数据了。那测试的可靠性就受影响了,所以一般不建议在测试里搞这种操作,或者你得特意准备对应的数据。 最后一点,特别提醒,就是使用“数据筒仓”测试策略时,千万不要在测试数据和真实组织数据之间共享记录 ID。比如你不能从组织里拿一个真实客户的 ID,然后在测试里直接指着它说“给我更新这条记录”,因为测试里根本看不见那条记录。你更不应该让测试故意生成一个和组织已有 ID 相同的记录,然后做跨数据的关联,这会导致混乱。测试就只能操作它自己新建的那些 ID,老老实实在它的小隔间里玩。 好,这一页的重点就是这些。大家记住:测试数据是临时的、隔离的,别想着靠已有数据,自己动手创建,自给自足才最稳。有什么问题咱们再讨论。

    查看详情
  • 8

    Using isTest(SeeAllData=True)

    第 447 页

    同学们,今天我们来看一个在写测试类时非常重要的注解:`@IsTest(SeeAllData=true)`。这个注解啊,就像给测试开了一扇大门,让它可以访问到整个组织里所有已经存在的数据。 通常我们写测试的时候,数据都是隔离的,只能看到测试里自己创建的那些记录。但有时候,你可能会想利用组织中已经存在的一些现成数据进行测试,那就可以用 `SeeAllData=true`。这个注解可以加在整个测试类上,也可以单独加在某个测试方法上。 注意了,它有严格的限制:它只是让你能“看”到真实数据,也就是可以做查询,但是你不能用它来创建、修改或者删除已有的记录。换句话说,即使你看到了这些数据,测试里对它们做的任何更改,在测试结束后都会被自动回滚,不会真的影响到生产数据。所以你放心,它只是读权限,不会留下任何痕迹。 那最佳实践怎么说的呢?这里特别强调,如果要用 `SeeAllData=true`,最好是通过数据筒仓模式来组织测试,也就是把测试划分成不同的数据访问范围,有控制地使用这个权限,不要每处都滥用,否则测试会变得依赖于组织里的具体数据,维护起来很头疼。 另外,注解的作用范围也有讲究。如果测试类上写的是 `@IsTest(SeeAllData=true)`,那类里面所有的方法都会继承这个设置,都能看到真实数据。但是,如果某个具体方法上也加了 `@IsTest(SeeAllData=false)`,那这个方法就会覆盖类的设置,只看懂自己创建的数据。反过来也一样,类默认是false,方法写true就能覆盖成可看真实数据。 还有一个容易踩坑的点:`SeeAllData=true` 不能和 `IsFlow=true` 在同一个方法上同时用。因为它们的运行机制不兼容,所以不要这样组合。 总结一下,这个注解给了测试一种“只读”全局数据的权限,但一定要谨慎使用,确保测试的独立性和可重复性。好了,这个概念很简单但细节很多,大家记住关键点就行。

    查看详情
  • 9

    Loading Test Data — Test.loadData & Utility Classes

    第 448 页

    同学们,今天我们来讲讲怎么在测试中更高效地准备数据。有时候,你想用一堆现成的数据来做测试,一条条地在代码里创建很麻烦。这时候,Salesforce 给我们提供了一个特别方便的方法:,Test.loadData,。 你只要提前把数据准备好,存成一个 CSV 文件,然后把它上传到静态资源里。在测试代码里,直接调用 `Test.loadData` 这个方法,告诉它你要加载哪个 SObject 类型,以及静态资源的名字,它就会自动把 CSV 里的数据变成真实可用的测试记录。这样一来,几行代码就搞定了,不用写一大堆 `insert` 语句。 另外,我们再聊聊,测试实用程序类,。你会发现,很多测试方法都需要创建差不多的测试数据,比如新建一个用户、一个客户账户、几个联系人。为了避免在每个测试方法里重复写这些创建数据的代码,我们可以建一个公共的、用 `@isTest` 标注的类。这个类专门放那些可复用的代码,你可以给它写各种方法,这些方法可以接收参数,还能返回结果,非常灵活。 而且,这个测试工具类有一个好处:它不算在组织的代码总大小限制里,只会在测试的环境下运行。这样一来,你就能放心地把各种数据创建的辅助方法放进去,所有的测试方法都能直接调用,代码又干净又好维护。 简单总结一下,用 `Test.loadData` 可以从 CSV 文件批量灌入测试数据,而写一个测试工具类能让你复用创建数据的逻辑。这两个技巧都能让你的测试代码更简短、更清晰。

    查看详情
  • 10

    Using @testSetup Methods

    第 449 页

    同学们,今天我们来聊聊 Apex 测试类里一个很实用的功能,叫做 `@testSetup` 方法。注意这个名字,是 test setup,不是 test set,通常我们叫它“测试设置”方法。 它的作用很简单,就是让你可以在一个地方,一次性创建好测试数据,然后在这个测试类里的每一个测试方法,都能直接访问这些数据。你不用在每个测试方法里反复写那一大堆创建数据的代码了。 那么执行顺序是怎样的呢?测试框架会先自动运行这个标了 `@testSetup` 的方法,把所有需要的记录建好。之后,再去一个个运行你写的那些测试方法。这些测试方法看到的数据,就是初始化时建好的那批,非常干净,而且一模一样。 最重要的是数据隔离。在测试方法里,你可能会修改记录、删掉某些数据,不用担心,每个测试方法对数据做的任何改动,在它运行结束后都会自动回滚。也就是说,下一个测试方法启动时,拿到的还是最初那个 `@testSetup` 方法里创建出来的、原封未动的记录。这保证了测试之间互不干扰。 有几个规矩要记住。第一,一个测试类里,只能有一个方法标上 `@testSetup`,不能多。第二,这个方法里不能用 seeAllData=true,也就是不能直接去查组织中已有的真实数据。第三,如果在 `@testSetup` 方法执行时发生了致命错误,那整个测试类就都不会再继续下去了,所有测试方法都会失败。 还有一个细节,就是静态变量。每个测试方法的执行都是一次新的事务,所以静态变量的值在每个方法开始前都会被清空,重新初始化。你在 `@testSetup` 里给静态变量赋的值,在测试方法里是看不到的。 简单总结一下,`@testSetup` 就像一个提前备好一桌菜的大厨,每一个测试方法就是来吃饭的客人,每个人都能看到同样的菜,而且每个人吃完后都会自动把桌子恢复原样,下一位客人来的时候还是那桌菜。这样我们的测试代码就变得精简、干净,维护起来也轻松多了。 好了,这就是关于 `@testSetup` 方法的讲解,大家可以在自己的代码里实践一下,感受它的便利。

    查看详情
  • 11

    Run Unit Test Methods — Overview

    第 450 页

    同学们,我们来看一下今天的内容——如何运行Apex的单元测试。你可能觉得:“测试写完就行了呗,直接跑一下看看结果。” 但其实运行测试的方法还挺多的,每种方式适用的场景也不太一样,大家先有个印象。 首先,你可以在 Salesforce 的用户界面上直接运行测试,这就是应用程序里的“测试执行”页面。也可以用 VS Code 的扩展来跑,或者打开开发者控制台。如果你是做自动化集成,还可以通过 API 来触发测试,比如用`runTests`这个 SOAP 调用,或者使用 Tooling REST API。 这里要注意一个运行方式的区别:如果你是从用户界面上手动启动测试,默认情况下,它们是同步并且并行执行的——就是一下全跑起来,速度快。但如果测试是跟着部署一起走的,比如你往生产环境部署代码,那部署前的测试就总是同步但顺序执行,一个接一个地跑,这样才能保证环境稳定。 再说一个限制,大家得记一下:在24小时内,你能运行的测试类的最大数量是 500 个,或者是你组织中测试类总数的10倍——哪个更大就取哪个。不过在沙盒或者开发者版组织里,这个上限会更高一些,所以平常开发不用太担心。 最后,如果想把测试安排到后台作业队列里批量运行,可以用`ApexTestQueueItem`这个对象,把测试加到队列里,然后通过`ApexTestResult`去查看结果。这在你需要定时跑一大套测试的时候特别方便。 好了,简单小结一下:今天你知道了跑测试的多种入口、并行和顺序的区别、每日限制,还有后台队列的用法。这些在你写和跑测试的时候都会用到,可以先把概念记住,后面动手试一下印象就更深了。

    查看详情
  • 12

    Running Tests — UI, VS Code, Dev Console & API

    第 451 页

    同学们,今天我们来快速看一下Apex测试执行的各种途径。 其实在Salesforce里,你至少有五六种办法来跑测试,有些是同步的,有些可以排队异步执行。咱们一个一个说。 首先,安装程序里面有一个叫“应用程序测试执行”的页面,它专门用来同步运行测试。你可以在这里按命名空间来选,比如只跑托管包的测试、只跑本地包的,或者一次性跑全部的,非常灵活。 第二种,如果你用的是VS Code,安装Salesforce扩展之后,直接在编辑器里就能运行测试。点一下运行按钮,结果马上回显,省得再切到浏览器,开发体验很好。 接下来是开发者控制台。默认情况下,它也是同步执行测试,但它不会把你锁死在那儿干等,你依然可以在控制台里查看日志、编辑其他代码,边等结果边工作,这一点挺实用。 如果你是通过API集成,那SOAP API里的runTests()调用也是同步的,发一个请求,等全跑完了才会返回结果,适合外部系统做自动化验证。 最后还有一种更强大的异步方式,就是Apex Test Deliverer项目。这个项目可以把Inbox里定义的测试放进执行队列,然后你通过Apex Test Report去查看结果。如果你想取消还没跑的测试,只要把队列项的状态更新成“已中止”就行了,这点很方便,尤其是大批量测试的时候。 好,那么这页讲的各种执行方式就这些。简单总结一下:同步执行适合边开发边验证,而排队异步执行适合大范围回归,而且你能随时中止。 这节课先到这里,下个环节我们继续。

    查看详情
  • 13

    Using the runAs Method

    第 452 页

    好,咱们今天来聊聊测试里一个特别好用的功能,叫 `System.runAs()`。 你想象一下,平时在测试代码里,你写的逻辑都是以当前登录用户——也就是运行测试的那个“你”——的身份去跑。但真实场景里,不同的用户看到的、能做的操作可是完全不一样的。有的人能编辑合同,有的人只能看个草稿;有的能看到敏感字段,有的连那个字段长啥样都看不见。 那怎么在测试里模拟“换个用户”呢?就是靠 `System.runAs()`。 你只要把一段测试逻辑包在 `System.runAs(某个用户)` 里面,Salesforce 就会立刻把当前上下文切换成这个用户的权限。无论是共享规则,还是对象级别的增删改查权限,甚至具体到某个字段能不能见、能不能改,都会严格按那个用户的设置来执行。就算你的测试类本身是无共享模式(也就是没有 `with sharing`),只要进了 `runAs` 的块,照样会强制检查这个用户的共享规则。 所以它特别适合验证权限控制写得对不对。 而且这个 `runAs` 用起来很灵活。你可以在里面再嵌套一个 `runAs`,就像一层套一层的俄罗丝套娃——每进入一层,身份就切一次,适合测试复杂的权限叠加场景。 还有一个很实用的地方:`runAs` 会直接忽略用户许可证的限制。也就是说,你可以在测试里临时创建一个用户,即使那个用户的许可证不够,你也照样能以他的身份去跑代码,不用真去买个额外的许可证。 但要注意,每次你调用 `runAs`,它都会算进你的测试方法里的 “TLR声明限制” 里。TLR 的全称是 Test Limit Resource,可以理解成测试里的资源配额,比如 DML 语句次数、查询条数等等。`runAs` 的每次调用会占用这个配额,所以别写个无限循环在里面切用户,会超限的。 再讲一个特别好用的场景——你可以用它来绕过混合 TLR 错误。什么意思呢?有时候你在一个测试方法里,既做了“设置类”的操作(比如调用 `Test.startTest()` 之前的那些准备数据),又想在同一个上下文里做其他的治理限制计数操作,可能会触发“混合 TLR”错误。这时候,你可以把其中一部分操作塞进 `runAs` 块里,Salesforce 就会把它们隔离开,帮你避免这个报错。 最后,如果你的代码涉及到托管包的不同版本,`runAs` 还有一个重载版本,你可以传一个 `System.Version` 进去,这样就能模拟在指定版本的托管包环境里测试行为。这在对 AppExchange 做升级兼容性测试时特别有用。 好,简单回顾一下: - `runAs` 用来临时切换用户上下文,强制应用那个用户的全部权限。 - 可以嵌套使用,不管测试类的共享模式。 - 每个 `runAs` 消耗 TLR 声明限制,并忽略许可证限制。 - 能隔离操作,避免混合 TLR 错误。 - 支持指定托管包版本去测试。 下一张 slide 我们来看看具体的代码例子。

    查看详情
  • 14

    Using Limits, startTest, and stopTest

    第 453 页

    同学们,我们来看这一页内容,主要讲测试当中的三个重要东西:`Limits` 方法,以及 `Test.startTest` 和 `Test.stopTest

    查看详情
  • 15

    SOSL Queries in Tests & Testing Best Practices

    第 454 页

    好,我们来说说Apex测试里的一些重要规则和最佳实践。你可能已经知道,要部署到生产环境,代码覆盖率至少要达到75%,但这只是最低要求。真正的目标应该是让测试真正验证你的逻辑。 首先,注意一个细节:在测试环境里,SOSL查询默认是返回空结果的。什么意思呢?就是你在测试方法里写了一个SOSL搜索,它不会真的去检索数据,除非你手动给它指定一组固定的结果。要做到这一点,你需要用 `Test.setFixedSearchResults()` 这个方法,提前定义好当你执行SOSL时应该返回哪些记录ID。这样你才能测试搜索相关的逻辑。 接下来,我想强调几条必须养成习惯的最佳实践。 第一,,覆盖所有代码行,——我们刚说了,最低75%,但你要尽量做到100%。而且不仅仅是行覆盖,还要,执行每一个条件分支,。比如 if-else 的分支、循环的不同情况,都要测到。 第二,,用有效输入和无效输入都测一遍,。检查你的代码在正常数据下工作正常,在错误数据或边界情况下能否优雅地处理,而不是崩溃。 第三,,确保测试方法本身不会抛出未处理的异常,。如果你预期会抛出异常,那要在测试里用 try-catch 或 `@isTest` 配合断言来捕获,不能让测试以失败告终,除非你故意验证异常。 第四,,大量使用 `System.assert` 或 `System.assertEquals` 这类断言,。没有断言的测试等于没测,你必须验证代码的行为确实符合预期。 第五,,如果需要模拟特定用户的操作,用 `System.runAs`,。这样你可以测试不同简档和权限下的逻辑,而不用真的切换用户。 第六,,测试触发器或批量逻辑时,使用至少20条记录,。因为Force.com平台有时对少于200条记录的触发会有不同的执行边界,用20条以上能保证批量逻辑被充分验证。把这些记录放在 `Test.startTest()` 之前创建好,然后调用 `startTest()`,之后执行你要测试的代码,最后 `Test.stopTest()`。这样能确保异步处理和调控器限制都按独立上下文运行。 第七,,千万不要在一个测试方法里测试整个应用程序,。每个测试应该聚焦于一个小的功能点或一个类。这样出问题的时候你立刻知道在哪儿。 最后,,保护数据隐私,。测试方法里千万别把真实的个人信息放在错误消息文本里。如果你要抛出异常或是记录调试信息,可以把敏感数据放在自定义异常类的属性里,而不是直接拼进字符串。这样日志或测试结果里就不会暴露隐私数据。 记住这些,你的测试就会更稳健、更安全,也更容易维护。有什么问题,随时可以停下来问我。

    查看详情
  • 16

    Parallel Test Execution & Testing Examples

    第 455 页

    同学们,咱们今天翻到这页幻灯片,来聊聊Apex测试时一个容易踩坑的问题,就是测试并行运行时的数据争用。 默认情况下,在Salesforce的用户界面里点击“运行全部测试”,系统为了节省时间,会把多个测试方法同时启动,这种并行执行本来挺好,但有个隐藏的麻烦。你想象一下,如果两个测试用例都在往同一个对象里插记录,而且碰巧用到相同的唯一索引值,比如两个测试都要创建邮箱一模一样的用户,由于它们同时跑,彼此不知道对方的存在,就有可能因为唯一性冲突导致其中一个莫名其妙地失败。又或者,它们争着更新同一条记录,还会发生记录锁的问题。这种概率性的失败特别让人头疼,因为单独跑某个测试明明都通过。 解决这个问题最简单的办法,就是在设置里关掉并行测试。你进到“设置”里,搜索“Apex Test”或者“Apex测试”,勾上那个“Disable Parallel Apex Testing”选项就行了。这样一来,测试就变成挨个排队执行,虽然慢了一点,但再也不会出现那种互相干扰的随机失败了。当然,更彻底的方案是你在设计测试数据时就注意隔离,让每个测试都使用自己独有的数据,但这节课我们先掌握这个快捷开关。 接下来咱们看这页强调的测试示例。作为开发者,我们光写正向案例远远不够,一定要把阳性、阴性、用户上下文隔离这些都覆盖到,这样的测试套件才真正牢靠。 先说阳性案例,就是代码正常工作的场景。你需要测试单条记录插入成功,也要测批量记录处理。比如你的代码里有个给客户批量发折扣的方法,那测试就要模拟传入200个客户,断言每个客户都正确更新了折扣字段。单独一条成功和批量都成功,才能确认没有边界漏洞。 然后是阴性案例,这更有讲究。阴性案例专门测试异常情况,比如记录违反了唯一约束,或者插入的时候超出了DML行数限制。幻灯片里特别提到了使用 `Assert.fail` 的写法,这可是个技巧。我来举个例子:假设你想测试一个方法,当传入的账户名重复时会抛异常。你就可以把调用写在 `try` 块里,然后紧挨着调用下一行写 `System.assert(false, '本应抛出异常却未抛出');` 注意老写法是 `Assert.fail`,现在都推荐用 `System.assert(false, '……')` ,效果一样。如果代码没抛异常,程序就走到这行,测试直接失败。如果它正确抛出了异常呢?就在 `catch` 块里,捕获出 `DmlException`,然后好好验证这个异常对象里带回来的详细信息。比如你可以验证 `e.getDmlFieldNames(0)` 里包不包含那个出错的字段名,再检查 `e.getDmlMessage(0)` 错误消息是否包含你预期的文字。这就是所谓的断言DML响应详细信息,能确保异常不仅是抛了,而且抛得准确,能指导用户修正数据。 最后,还要记住用 `System.runAs` 来模拟不同用户的测试。很多时候我们的数据是按角色或共享规则隔离的,你不亲自变成另一个用户跑

    查看详情
  • 17

    Testing and Code Coverage

    第 456 页

    同学们,我们今天来说说代码覆盖率是怎么计算的。这个概念很重要,但别担心,我会用最简单的话讲清楚。 首先,代码覆盖率的百分比是怎么来的?其实就是“被覆盖到的行数”除以“可覆盖的总行数”。注意,这里的分母只算那些实实在在能执行的行,像注释、空白行,还有我们平时调试用的 `System.debug()` 语句,全都会被排除在外,不参与计算。 那怎么算“一行”呢?有个小细节:如果一行代码里写了多条语句,比如 `a++; b++;` 写在同一行,那这整行就算成一条。反过来,如果一条很长的语句跨了好几行,那每一行都会被单独计算。总之,是按“代码行”来数,不是按语句条数。 再提醒一下,测试类本身也是代码,但它们在计算覆盖率时是不被算进去的。还有,有时候因为编译器的优化,某些代码行可能压根儿不会执行,这种行也不会被计入覆盖范围。 那我们怎么看到具体数字呢?有两种常用方法。第一,直接打开开发者控制台,找到“测试”那个选项卡,里面会用颜色清晰地标出哪些行覆盖了、哪些没覆盖。第二,要是你想用程序化的方式获取数据,可以查询Salesforce提供的两个API对象:一个是 `ApexCodeCoverageAggregate`,它存着每个类的总体覆盖率数据;另一个是 `ApexCodeCoverage`,它存着每个测试方法对应的覆盖情况。注意名字有点长,但看一遍就认识了。 简单总结:覆盖率只看可执行行,排除注释和调试语句;测试类自身不算;想查数据就去开发者控制台或者查那两个系统对象。这样讲是不是就清晰多了?好,我们继续下一个知识点。

    查看详情
  • 18

    Code Coverage Best Practices

    第 457 页

    我们来聊聊关于代码覆盖率的一个常见误区,以及在实际项目中你可能会遇到的一些情况。 你可能以为,只要我更新了Apex代码,系统就会自动帮我重新计算覆盖率。其实不是这样的——代码覆盖率的数据不会自动刷新。你必须,重新运行测试,,它才会根据最新的代码重新统计。所以,每当你改完代码,记得去跑一遍相关的测试类,不然你看到的覆盖率数字是旧的,可能会误导你。 另外,沙盒环境和生产环境的覆盖率有时会不一样。原因有好几个:比如测试失败了、测试类用了 `SeeAllData=true` 导致依赖了具体的数据、元数据存在差异,或者你在部署时只跑了部分包的测试等等。哪怕你在沙盒里某个新组件达到了100%的覆盖率,但如果整个组织的,平均覆盖率低于75%,,部署还是会被拦住。这时候,你就需要额外再补写一些测试,把整体覆盖率拉上去。 我建议你使用,完整沙盒,作为预生产环境,因为它最接近真实的生产数据,跑出来的覆盖率更有参考意义。最后要注意,如果生产环境中的覆盖率突然掉到了75%以下,很可能是数据或元数据的变更影响了测试的行为。这时候,你可以,在生产环境直接运行本地测试,,快速找出是哪个类出了问题,然后针对性修复。 这样解释,是不是清晰多了?记住,覆盖率是保障代码质量的门槛,多花点心思在测试上,后面部署会顺利很多。

    查看详情
  • 19

    Build a Mocking Framework with the Stub API

    第 458 页

    同学们,咱们今天来聊一个很实用的功能,叫Stub API,它是Apex里用来做模拟测试的框架。 简单来说,你写测试类的时候,经常需要依赖一些外部服务或者复杂对象,对吧?比如调用一个Web服务,或者某个类的方法特别重,每次测都要跑很多逻辑,又慢又容易出错。这时候我们就可以“模拟”这些依赖,假装调用它们,直接返回你想要的结果,让测试只关注自己这部分逻辑——这就叫隔离测试。 Stub API就是Salesforce提供的一种官方办法,帮你在测试里生成一个“替身”对象。它的妙处在于,这个替身不是一个需要你提前写好、打包的测试类,而是在运行时自动生成的一个匿名子类。也就是说,你不需要为了测试专门去创建和维护一堆假的类,直接用代码描述行为就行了。 那具体怎么用呢?很简单,两步走: 第一步,创建一个类,去实现System.StubProvider接口。这个接口里只有一个方法,叫handleMethodCall。这个方法就是替身的“大脑”,每次你调用替身对象的某个方法时,它都会被触发。你可以在里面写逻辑:根据调用的方法名、参数,返回一个你提前设计好的假数据。这样你就完全控制了替身的行为。 第二步,用Test.createStub()这个方法,把你要模拟的原始类的类型,和你刚写好的那个StubProvider实例传进去,它就会返回给你一个替身对象。然后你就可以在测试里用这个替身,当作真实对象去调用方法了。 不过,它也有些限制,不是万能的。你现在记住几个不能模拟的情况:静态方法、私有的方法和属性、触发器、内部类、系统类型(比如标准对象的一些底层类)、可批量的类,还有那些只有私有构造函数的类,这些都模拟不了。所以遇到这些场景,你就得想其他办法,比如用依赖注入换种设计,或者考虑别的手动模拟方式。 总之,Stub API是个轻量、灵活的工具,能帮你把测试变得更快、更可靠,而且代码干净。下次写测试遇到烦人的依赖,记得试试它。

    查看详情
  • 20

    Apex Integration Tests — Overview & Comparison

    第 459 页

    同学们,今天我们来聊聊 Apex 里一个比较特别的测试类型——,集成测试,。 你看,平时我们写的单元测试,就像在房间里用玩具模拟真实世界,一切外部交互都得用 Mock 来假装,测试完数据也会自动回滚,不留下任何痕迹。 但集成测试就不一样了,它是在真实的 Salesforce 组织和外部服务之间进行真正的“通话”。 因为它要真的打通服务,所以它会放宽很多单元测试里的严格限制,比如不再强制回滚事务,也不再限制你得老老实实模拟入站邮件那样的操作。 不过,要提醒大家,集成测试目前还只是一个,开发者预览版,,只能在临时组织里使用,还不能直接部署到生产环境。 它和标准单元测试的具体区别,我帮大家梳理一下,听好了: - ,允许真实调用 Agentforce 或 Data 360,,你不需要再写一堆 Mock 了,直接就能测。 - 如果你想在测试里,把数据真正提交到数据库,,可以用 `@TearDown` 方法来实现,数据会真实保留。 - 默认情况下,`SeeAllData` 就是 `true`,也就是说它能看见组织里真实存在的数据。 - ,它完全不计入代码覆盖率,,所以不用担心覆盖率指标,它不参与统计。 - 从元数据部署的角度看,它是,被排除在外的,,不能随着元数据一起迁移。 - 运行的时候也有特殊限制:,同时只能跑一个,并发测试,并且 ,最长运行时间 10 分钟,。 那我们什么时候需要用这种集成测试呢? 主要是三件事:,测试真实的服务交互行为,、,测试 DMO(数据模型对象)查询,,还有那些,依赖提交数据的业务场景,。 比如你想验证一条数据真正落库后,下游的 Data 360 能不能查到,这就得靠集成测试。 好了,咱们把集成测试当作一个能真正伸手到外部世界摸一摸的工具,但记得它还在预览阶段,只在临时环境里慢慢探索就好。 下节课我们再结合实际例子,看看怎么写出这样一个集成测试类。

    查看详情
  • 21

    Integration Tests — Implementation & Best Practices

    第 460 页

    同学们,今天我们来聊一聊Apex里的集成测试功能,这是一个很实用的工具,能帮我们测试那些需要真正提交数据到数据库、甚至让其他线程看见的场景。 首先,要使用这个功能,你得在临时组织的定义里把“ApexIntegrationTests”这个功能打开,这样集成测试才生效。写测试的时候,把你想要作为集成测试的类或者方法,加上“@IntegrationTest”这个注解就行,很简单。 好,那关键的地方来了。普通的单元测试,数据在测试结束后会自动回滚,但集成测试里,我们会有个叫“commitTestOnly”的方法。你可以在事务的中间调用它,它会立刻把当前已经做的数据变更提交到数据库,这样其他线程或者后续的操作就能看到这些数据了。这样一来,我们就能测到真实的并发场景。 但是,数据提交了就不会自动清理,所以一定得配上“@TearDown”方法。这个方法用来在测试完成之后,手动把你之前提交的那些数据清理干净。如果不清理,下次运行可能会受影响。 说到最佳实践,这儿有几条一定要记住:第一,永远给集成测试配上@TearDown,不然数据越积越多;第二,在清理数据的时候,记得先删子记录,再删父记录,避免因为外键约束删不掉;第三,在调用真实的外部服务之前,最好先调用commitTestOnly把准备数据提交了,因为外部服务可能需要在数据库里看到这些数据;第四,每个测试要聚焦一个点,别写得太复杂;第五,规划好并发,想想多个测试或者多个线程一起跑的时候会不会打架;最后,注意时间限制,集成测试每次运行最多有10分钟,别写得太慢超时了。 这个功能也有些限制,大家要心里有数:它只在Dev预览版的组织里才能抓取使用,不是所有地方都有;另外,集成测试的代码不会算进代码覆盖率里,所以别指望靠它补覆盖率;还有,不支持@TestVisible这个注解;而且目前只支持Deliverc平台,每个组织里只能启用一个集成测试功能;最后,要遵守Deliverc的管理员限制,别超出平台的资源上限。 把这些点记住,你就能安全又有效地使用Apex集成测试,测试那些真正跨事务的场景了。大家有什么问题,随时问我。

    查看详情