在Sales force开发中完善测试类是开发者必经的一个环节,代码的部署需要保证至少75%的覆盖率,那么该如何写好测试类呢。

测试类定义格式如下:

 @isTest
private class MyTestClass {
@isTest static void myTest() {
// code_block
}
static testMethod void testName() {
// code_block
}
}

测试类允许声明为private或者public,一般来说,单元测试的测试类都声明为private,如果是DateFactory的话则声明为public,其中测试类不占用计算在APEX代码的限制中。

测试类的命名规范一般有两种:MyClassTest 或者 TestMyClassName

两种规范各有优劣,其中 MyClassTest紧随Apex类之后,便于查询与修改,TestMyClass则将所有测试类都集中在一起,便于打包。可以根据自己的习惯自行决定。

在测试类的写法上有两种选择

  • 用少量测试类覆盖远大于测试类的Apex类
  • 一个测试类对应一个Apex类

从项目规范上来说,应该一个测试类对应一个Apex类,从而保证不论是后期维护,还是功能变更又或者代码修改,都能保证代码的质量并且在部署时更加快捷;但是少量测试类覆盖多个Apex类同样也是一种选择,因为存在Apex类因为功能简单,专门写个测试类显得很“多余”,而且在测试类的交叉覆盖下,很多Apex类覆盖率自然就上去了,项目上线迭代的速度能得到很大程度的提高。

两种写测试类的优缺点很明显,少量测试类覆盖远大于测试类的Apex类,上线速度快,减少开发量,但是在后期维护乃至二期项目中需要花费更大的时间去处理测试类的问题,如果后来的开发者不熟悉之前的逻辑那需要的时间将更加漫长;一个测试类对应一个Apex类的写法在项目进行中的时候会占据不少的时间,但是好处就是代码结构清晰,后期维护的开发者不管熟不熟悉都将能游刃有余的处理之前的内容。

从长远的角度来说,我更推荐使用后一种,作为程序员,应该花60%的时间去思考逻辑以及最优的实现方式,然后花20%的时间去实现,最后花费20%的时间进行包括业务场景在内的测试。

写好的测试类能尽可能的验证你写的代码逻辑的正确性,提升你的代码质量,即使修改了之前的逻辑,执行你的测试方法也能知道有没有影响你之前的逻辑,方便进行回归测试。

说了这么多,来看一个示例吧。

 /*********
*
* @Author:ricardo
* @Time:2018-05-09
* @Function 华氏温度温度转换摄氏温度
*/
public class TemperatureConverter {
// Takes a Fahrenheit temperature and returns the Celsius equivalent.
public static Decimal FahrenheitToCelsius(Decimal fh) {
Decimal cs = (fh - 32) * 5/9;
return cs.setScale(2);
}
}

测试类如下

 @isTest
private class TestTemperatureConverter {
//正常输入
@isTest static void testWarmTemp() {
Decimal celsius = TemperatureConverter.FahrenheitToCelsius(70);
System.assertEquals(21.11,celsius);
}
//临界值
@isTest static void testFreezingPoint() {
Decimal celsius = TemperatureConverter.FahrenheitToCelsius(32);
System.assertEquals(0,celsius);
}
//异常值
@isTest static void testBoilingPoint() {
Decimal celsius = TemperatureConverter.FahrenheitToCelsius(212);
System.assertEquals(100,celsius,'这温度超限啊');
}
//反向测试
@isTest static void testNegativeTemp() {
Decimal celsius = TemperatureConverter.FahrenheitToCelsius(-10);
System.assertEquals(-23.33,celsius);
} }

可以看到,测试类其实至少应该测试正常的业务流程,同时还包括异常/临界/反向三种情况。

默认情况下,Sales force不运行测试类访问正式的ORG数据,除了某些极端情况,比如你要测试报表相关的Apex类,可以使用@isTest(SeeAllData=true)的注解来访问org中的数据,不过一般我们是不建议这样操作的。那么针对这样的情况,也就是需要重复创建一些测试数据的我们怎么做呢,答案是使用Test Utility Classes创建测试数据,也就是创建一个DateFactory类专门提供测试数据,从而避免重复创建数据的问题,也能帮助快速修复一些因为数据不全引起的测试代码报错的问题。

下面看一个Account的trigger示例

 /***********
*
* 测试类最佳实践-示例二
*
*/
trigger AccountDeletion on Account (before delete) {
// 如果关联业务机会则禁止删除Account
for (Account a : [SELECT Id FROM Account
WHERE Id IN (SELECT AccountId FROM Opportunity) AND
Id IN :Trigger.old]) {
Trigger.oldMap.get(a.Id).addError(
'Cannot delete account with related opportunities.');
}
}

准备DataFactory

 @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++) {//numAccts:创建的Account数量
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];
// 遍历每一个Account,都关联创建一个对应的业务机会
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));
}
}
// 插入关联了Accout的业务机会
insert opps;
return accts;
}
}

创建测试类

 @isTest
private class TestAccountDeletion {
@isTest static void TestDeleteAccountWithOneOpportunity() {
// 测试类初始准备测试数据
// 创建一个关联业务机会的Account,并准备删除掉
//Account acct = new Account(Name='Test Account');
//insert acct;
//
// 测试类初始准备测试数据使用DataFactory
// 创建一个Account,关联一个业务机会
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('不能删除关联业务机会的Account',
result.getErrors()[0].getMessage());
} @isTest static void TestDeleteAccountWithNoOpportunities() {
// 测试初始化
// 创建一个Account,不关联业务机会的
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() {
// 初始化测试数据
// 创建200个Account,创建一个业务机会
Account[] accts = TestDataFactory.createAccountsWithOpps(200,1); // 测试开始
Test.startTest();
Database.DeleteResult[] results = Database.delete(accts, false);
Test.stopTest();
// 校验结果
// 批量删除下,有Account是不能被删除的,数据需要回滚
// 所以我们会得到一个错误的返回值
for(Database.DeleteResult dr : results) {
System.assert(!dr.isSuccess());
System.assert(dr.getErrors().size() > 0);
System.assertEquals('不能删除关联业务机会的Account',
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());
}
} }

可以看到,我们在完善自己的测试类时,不应该仅仅关注代码的覆盖率,同时也应该注意到对各种正常,异常,临界诸多可能的测试,好的测试能保证代码的质量,尤其需要注意的是经常会被忽略的批量数据处理的问题,在写测试类的时候一般追求覆盖率写出来的测试方法都是模拟用户在界面上的单记录操作,很少会考虑批量数据的测试情况,而在实际的系统使用中是很可能遇到而且容易引起问题的地方。

测试类不应该是Salesforce强制要求的内容,而应该是属于我们写的代码中不可或缺的一部分,希望能重新让你认识到测试类的重要性

如果文中有错误欢迎指正,有问题欢迎留言。

Salesforce 开发整理(一)测试类最佳实践的更多相关文章

  1. Salesforce 开发整理(五)代码开发最佳实践

    在Salesforce项目实施过程中,对项目代码的维护可以说占据极大的精力,无论是因为项目的迭代,还是需求的变更,甚至是项目组成员的变动,都不可避免的需要维护之前的老代码,而事实上,几乎没有任何一个项 ...

  2. [翻译] API测试的最佳实践 - 介绍

    API测试的最佳实践 - 介绍 在上一篇“是什么让API测试很叼”一文中,我们讨论API与其他形式的软件测试的差异.部分是因为API之间的通信压根就没考虑让你能读懂,纯粹是为了方便计算机之间的交互而设 ...

  3. Salesforce 开发整理(八)PDF打印相关

    一:基础设置 Salesforce中的PDF页面本质上还是Visualforce[简称VF]页面,所以只需要给VF页面加上一个属性[renderAs="pdf"] 即可生成一个PD ...

  4. Java Servlet开发的轻量级MVC框架最佳实践

    在Servlet开发的工程实践中,为了减少过多的业务Servlet编写,会采用构建公共Servlet的方式,通过反射来搭建轻量级的MVC框架,从而加快应用开发. 关于Servlet开发的基础知识,请看 ...

  5. 构建高效Presubmit卡点,落地测试左移最佳实践

    樊登有一节课讲的挺有意思,说中国有个组织叫绩效改进协会,专门研究用技控代替人控的事情.其用麦当劳来举例子,他说麦当劳其实招人标准很低,高中文凭就可以,但是培养出来的人,三五年之后,每一个都是大家争抢的 ...

  6. Salesforce 开发整理(十)项目部署总结

    项目部署顺序 全局值集 小组 自定义字段-对象-设置(SF1 紧凑布局要和记录类型在这里要一起部署) 邮件模板-静态资源 角色 工作流-流定义(包含进程生成器) 批准过程 开发部署<Apex类, ...

  7. Salesforce 开发整理(二)报表开发学习

    Salesforce提供了强大的报表功能,支持表格.摘要.矩阵以及结合共四种形式,本文探讨在站在开发的角度要如何理解报表. 一:查询报表基本信息报表在Sales force中是Report对象,基本的 ...

  8. Salesforce 开发整理(九) 开发中使用的一些小技巧汇总[持续更新]

    1.查询一个对象下所有字段 当需要查询一个对象所有字段进行复制或其他操作,可以使用一段拼接的语句来查询 String query = 'select '; for(String fieldApi : ...

  9. Salesforce 开发整理(七)配置审批流

    salesforce提供了比较强大的可配置审批流功能,在系统中翻译为“批准过程”.所以需要配置审批时,选择创建 ——>  工作流和批准 ——> 批准过程,然后选择管理批准过程,选择需要配置 ...

随机推荐

  1. 团队项目之Scrum3

    小组:BLACK PANDA 时间:2019.11.23 每天举行站立式会议 提供当天站立式会议照片一张 2                            昨天已完成的工作 2 完善用户注册的 ...

  2. 连接查询 变量、if else、while

    连接查询 变量.if else.while   一.连接查询:通过连接运算符可以实现多个表查询. 连接是关系数据库模型的主要特点,也是它区别于其它类型数据库管理系统的一个标志. 常用的两个链接运算符: ...

  3. RabbitMQ学习笔记(八、RabbitMQ总结)

    1.什么是消息中间件 Message Queue Middleware,简称MQ,是一种利用高效可靠的消息传递机制进行与平台无关的数据交互的技术. 2.MQ的作用 异步:类似于短信业务,将需要发送的消 ...

  4. 观察者模式Vs发布订阅模式

    1)观察者模式 观察者模式通俗的讲就是我们平事件调用(click/change等等) 大家先看这个图片.我们被观察者Subject(监听某个事件)发生改变时,观察者Observer监听到没改变做出调整 ...

  5. 《推送开发全面盘点当前Android后台保活方案的真实运行效果》

        登录 立即注册 TCP/IP详解 资讯 动态 社区 技术精选 首页   即时通讯网›专项技术区›推送开发全面盘点当前Android后台保活方案的真实运行效果(截止2 ...   帖子 打赏 分 ...

  6. Java 添加Word目录的2种方法

    目录是一种能够快速.有效地帮助读者了解文档或书籍主要内容的方式.在Word中,插入目录首先需要设置相应段落的大纲级别,根据大纲级别来生成目录表.本文中生成目录分2种情况来进行: 1.文档没有设置大纲级 ...

  7. 根据excle说明文档建表

    在Excel里整理好的表模型数据,可直接导入PowerDesigner.此功能通过PowerDesigner的脚本功能来实现,使用起来也简单.具体操作方法:     打开PowerDesigner,新 ...

  8. linux-VMtools安装

    一.解决的问题 1.剪切板无法共享 2.共享文件夹的设置 3.窗口无法自适应 二. 安装 1.进入centos 2.点击VMware菜单--->install VMware tools 3.打开 ...

  9. Python进阶二

    文章目录 函数参数1.位置传递2.名称传递 def f(a,b): f(1,2) f(b=2,a=1) 3.可选参数传递(可选参数必须放在最后)def f(a,b=1) ✔def f(b=1,a) ❌ ...

  10. sed 面试题

    #oldboy my qq num is 49000448.$ not 4900000448. my god ,i am not oldbey,but clsn!$ #oldboy my name i ...