Salesforce 开发整理(一)测试类最佳实践
在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 开发整理(一)测试类最佳实践的更多相关文章
- Salesforce 开发整理(五)代码开发最佳实践
在Salesforce项目实施过程中,对项目代码的维护可以说占据极大的精力,无论是因为项目的迭代,还是需求的变更,甚至是项目组成员的变动,都不可避免的需要维护之前的老代码,而事实上,几乎没有任何一个项 ...
- [翻译] API测试的最佳实践 - 介绍
API测试的最佳实践 - 介绍 在上一篇“是什么让API测试很叼”一文中,我们讨论API与其他形式的软件测试的差异.部分是因为API之间的通信压根就没考虑让你能读懂,纯粹是为了方便计算机之间的交互而设 ...
- Salesforce 开发整理(八)PDF打印相关
一:基础设置 Salesforce中的PDF页面本质上还是Visualforce[简称VF]页面,所以只需要给VF页面加上一个属性[renderAs="pdf"] 即可生成一个PD ...
- Java Servlet开发的轻量级MVC框架最佳实践
在Servlet开发的工程实践中,为了减少过多的业务Servlet编写,会采用构建公共Servlet的方式,通过反射来搭建轻量级的MVC框架,从而加快应用开发. 关于Servlet开发的基础知识,请看 ...
- 构建高效Presubmit卡点,落地测试左移最佳实践
樊登有一节课讲的挺有意思,说中国有个组织叫绩效改进协会,专门研究用技控代替人控的事情.其用麦当劳来举例子,他说麦当劳其实招人标准很低,高中文凭就可以,但是培养出来的人,三五年之后,每一个都是大家争抢的 ...
- Salesforce 开发整理(十)项目部署总结
项目部署顺序 全局值集 小组 自定义字段-对象-设置(SF1 紧凑布局要和记录类型在这里要一起部署) 邮件模板-静态资源 角色 工作流-流定义(包含进程生成器) 批准过程 开发部署<Apex类, ...
- Salesforce 开发整理(二)报表开发学习
Salesforce提供了强大的报表功能,支持表格.摘要.矩阵以及结合共四种形式,本文探讨在站在开发的角度要如何理解报表. 一:查询报表基本信息报表在Sales force中是Report对象,基本的 ...
- Salesforce 开发整理(九) 开发中使用的一些小技巧汇总[持续更新]
1.查询一个对象下所有字段 当需要查询一个对象所有字段进行复制或其他操作,可以使用一段拼接的语句来查询 String query = 'select '; for(String fieldApi : ...
- Salesforce 开发整理(七)配置审批流
salesforce提供了比较强大的可配置审批流功能,在系统中翻译为“批准过程”.所以需要配置审批时,选择创建 ——> 工作流和批准 ——> 批准过程,然后选择管理批准过程,选择需要配置 ...
随机推荐
- nginx 配置实例-反向代理
反向代理实例一 虚拟机IP:192.168.116.129实现效果:使用 nginx 反向代理,访问 www.123.com 直接跳转到 虚拟机的192.168.116.129:8080 实验代码 ...
- JavaScript-----9.函数
1.函数的使用 1.1 声明函数和调用函数 //1.声明函数 //function 函数名() { // //函数体 //} function sayHi() { console.log('hi~') ...
- ceph安装笔记
配置源 ceph版本为luminous [root@ceph-node1 ~]# yum install -y https://dl.fedoraproject.org/pub/epel/epel-r ...
- FTP安装及配置
在centos7安装ftp服务 yum install -y vsftpd 启动服务 systemctl start vsftpd 自启动 systemctl enable vsftpd 查看端口 注 ...
- java实现排列组合(通俗易懂)
个人感觉这篇文章(原文地址见文章尾)写的排列组合问题,非常的好,而且是一步一步引出排列组合问题,我也是看了这篇文章,一步一步按照这个思路来,最后会了自己的一套排列组合 也因此在算法竞赛中,两次用到了, ...
- laravel实现多模块
一.这里使用Caffienate Modules 网址:modules maintained by caffeinated 二.根据自己的版本选择包的版本 三.在项目composer.json文件中加 ...
- intelj idea中除了Find Usage外的另一种查找级联调用的方法
一.方法 平时都是使用find usage来找一个方法的引用的地方,但是只能查看一级,今天偶然发现个查看多级的办法: 在源码编辑界面:
- Linux系统:centos7下搭建Nginx和FastDFS文件管理中间件
本文源码:GitHub·点这里 || GitEE·点这里 一.FastDFS简介 1.基础概念 FastDFS是一个开源的轻量级分布式文件系统,它对文件进行管理,功能包括:文件存储.文件同步.文件上传 ...
- Java生鲜电商平台-商城优惠券设计要点复盘与总结
Java生鲜电商平台-商城优惠券设计要点复盘与总结 Java生鲜电商平台本文将从优惠券设计用户端,需求端,业务流程全方案解析优惠券设计方案 为什么要设计优惠券 设计优惠券的核心:拉新和促活 新产品上线 ...
- maven仓库之第一篇
maven jar仓库 :存放maven项目使用的jar包. 中央仓库,存放99%免费开源项目jar包,apache公司负责维护的,以T为单位的存储. 例如 : struts2-core-2.3.24 ...