@isTest
public class TestUtil {
public static void createTestAccounts() {
// Create some test accounts
}
public static void createTestContacts() {
// Create some test contacts
}
}
或者
static testMethod void testName() {
// code_block
}
@isTest 定义为@isTest的类和方法可以是私有的也可以是公共的。 定义为@isTest的类必须是顶级的
注意 :有时候测试方法需要访问预先存在的数据。要访问组织数据,请使用@isTest注释测试方法(SeeAllData = true)
@testSetup static void methodName() {
}
@testSetup : creating common test records that are available for all test methods in the class.
1. 测试类
a. 为啥要写烦人的测试类
- 当SandBox 部署Code到正式环境时,SF强制要强(>=75%,平台要求)。
- 对功能模块检测(单元测试不保证程序做正确的事,但能帮助保证程序正确地做事)。
- 反馈速度快,一旦在执行测试类时,通过断言,出现预期结果与实际结果不符合时,就发现问题,比从测试人员反馈即时。
- 后期如果对程序修改,比如新加字段,修改字段类型,执行测试类时,可以发现新加的内容是否对原有逻辑造成影响。
- 文档作用。(因为测试类根据场景来做的,通过大致阅读测试类,不用看原有代码,可以大概知道对应的功能,前提是测试比效规范和有意义的说明)
- 对功能设计的反馈。(如果一个功能很难写出对应的测试类,则该功能的设计存在不良的缺陷)
b. 写测试类面临的问题
- 项目时间问题(认为测试类不重要,所以并没有预留出足够的时间用来书写测试类,导致开发人员抱着能部署的心态书写测试类,把目标定在75%这个点上)
- 开发人员不重视测试类,以完成功能开发为目地,而不是写出符合要求,且健状的程序来要求自己。
c. SF测试类
- 测试类中可直接访问的对象有如下 User,Profile,Organization,AsyncApexJob,CronTrigger,RecordType,ApexClass,ApexTrigger, ApexComponent,ApexPage
- 使用IsTest(SeeAllData=true) 可以直接访问系统中的记录
- 测试数据与系统数据隔离
- 测试类中无法创建字段历史,因为测试数据不会提交(之前遇到过更新记录后,查询历史,发现历史为空)
- 为了避免对一个对象的创建在每个测试类中书写,可以创建一个类,专门来创建每个对象,类似于这样。
@isTest
public class TestData {
public static Account getAccount(Id recordTypeId){
Account entity = new Account();
entity.Name = 'TestAccount';
entity.RecordTypeId = recordTypeId;
return entity;
}
public static Account getAccount(String nameIndex){
Account entity = new Account();
entity.Name = 'TestAccount'+nameIndex;
return entity;
}
public static Contact getContact(Id accountId,String nameIndex){
Contact con = new Contact();
con.LastName = 'TestContact'+nameIndex;
con.AccountId = accountId;
return con;
}
}
6 . 需要测试的代码
// 一个小例子
//@Test {UnitClass:[
// testAccountAction_getAccount(),
// testAccountAction_getContact()
// ]}
//@See {}
//@Schedule{}
//@Modify[
// {时间:2016年8月29日 00:18,修改人:在山的那边 ,描述:init}
//]
//
public class AccountAction {
@TestVisible private Account account;
public void getAccount(){
this.account = [SELECT Id,Name FROM Account][0];
}
public List<Contact> getContact(){
List<Contact> conList = [SELECT Id,Name FROM Contact WHERE AccountId =:account.Id];
return conList;
}
}
7 . 创建测试类 SF创建测试类很简单,在类前加上annotation @isTest 或者 在使用关键字 testMethod 和 static 修饰方法(如下形式),个人偏向于测试方法写在一个测试类中,当测试类较多时,另起一个测试文件,而不是一个class写一个测试类文件
public class myClass {
static testMethod void myTest() {
// Add test method logic using System.assert(), System.assertEquals()
// and System.assertNotEquals() here.
}
}
@isTest
private class MyTest {
// Methods for testing
}
@isTest
public class UnitClass {
@testSetup static void methodName() {
Account acc01 = TestData.getAccount('01');
Insert acc01;
Contact con01 = TestData.getContact(acc01.Id, '01');
Insert con01;
}
//个习惯测试类的命名格式为:test+类名+"_"+方法名,在注释中同样写清楚测试类怀测试方法
//
@isTest static void testAccountAction_getAccount() {
AccountAction accAction = new AccountAction();
accAction.getAccount();
System.assertEquals('TestAccount01', accAction.account.Name);
}
@isTest static void testAccountAction_getContact(){
AccountAction accAction = new AccountAction();
accAction.getAccount();
List<Contact> conList = accAction.getContact();
System.assertEquals('TestContact01', conList.get(0).Name);
}
}
代码中的注释为多行 ,排版问题改为//
在sublime中通过shift + 双击可以导航到定义处,这就是为什么在注释中写明测试类与测试的方法
这样在程序中可以点击导航对应的测试方法,在测试方法中能快速导航到程序中
8 . 在控制台执行对应的测试类。
9 . @TestVisible 作用是访问类的私有变量,如一些内部类或者私有变量需要访问或者给值时
10 . @testSetup 在执行测试方法前执行,类似于junit中的@before 可以执行一些公共数据的新建等工作,但是一但该方法有错误,则整个测试类全部失败,建议只有一个testsetup方法
11 . 通过System.assertEquals or System.assert() or System.assertNotEquals() 来验证预期结果与实际结果 比较,一旦不成立,则测试类提示失败,日志中会体现预期值为多少,实际为多少。
12 . runAs 模拟不同用户操作
public static testMethod void testBatch() {
user u = [SELECT ID, UserName FROM User
WHERE username='testuser1@acme.com'];
user u2 = [SELECT ID, UserName FROM User
WHERE username='testuser2@acme.com'];
String u2id = u2.id;
// Create 200 test accounts - this simulates one execute.
// Important - the Salesforce.com test framework only allows you to
// test one execute.
List <Account> accns = new List<Account>();
for(integer i = 0; i<200; i++){
Account a = new Account(Name='testAccount'+'i',
Ownerid = u.ID);
accns.add(a);
}
insert accns;
Test.StartTest();
OwnerReassignment reassign = new OwnerReassignment();
reassign.query='SELECT ID, Name, Ownerid ' +
'FROM Account ' +
'WHERE OwnerId=\'' + u.Id + '\'' +
' LIMIT 200';
reassign.email='admin@acme.com';
reassign.fromUserId = u.Id;
reassign.toUserId = u2.Id;
ID batchprocessid = Database.executeBatch(reassign);
Test.StopTest();
System.AssertEquals(
database.countquery('SELECT COUNT()'
+' FROM Account WHERE OwnerId=\'' + u2.Id + '\''),
200);
}
}
13 . startTest 和 stopTest 在这两个方法中间执行一些耗时操作,在stoptest 后再使用断言判断,如job,如果不在方法中间,则断言取不到值,原因是当执行判断时,job也许还没有执行完成。同时还能避免一些limit问题,webservice,remote acton 操作时都需要写在中间。该测试方法中只能调用一次。
14 . 测试方法中不要出现硬码,否则出现在Sandbox中正常,到正式中就出错,少依赖基础数据。
15 . Trigger 覆盖率必须>0
16 . try…catch 捕获代码中出现的异常,如trigger中 字段.adderror 等形为
以上仅参考,如果错误欢请指出,参考内容为SF的apex 指南, SF也建议不要将目光仅放在75%上,要尽量达到95%以上,最后需要通过断言判断程序是否与期望相同