はじめに
この記事では、Salesforceの開発言語であるApexの基本を学びます。JavaやC#の経験がある方にとって、比較的親しみやすい言語です。
目次
- Apexの基本文法
- トリガーの基礎
- テストクラスの書き方
1. Apexの基本文法
変数とデータ型
Apexで利用できる主要なデータ型を見ていきましょう。
// プリミティブ型
Integer count = 1;
String name = 'John';
Boolean isActive = true;
Decimal price = 1000.50;
Date today = Date.today();
DateTime now = DateTime.now();
// コレクション型
List<String> nameList = new List<String>();
Set<String> uniqueNames = new Set<String>();
Map<Id, Account> accountMap = new Map<Id, Account>();
// sObject型(Salesforceの標準オブジェクト)
Account acc = new Account(
Name = 'テスト取引先',
Industry = 'Technology'
);
クラスの定義
Apexでのクラス定義の基本を見ていきましょう。
public class AccountService {
// 定数
private static final String DEFAULT_INDUSTRY = 'Technology';
// インスタンス変数
private String accountName;
// コンストラクタ
public AccountService(String name) {
this.accountName = name;
}
// メソッド
public Account createNewAccount() {
Account acc = new Account();
acc.Name = this.accountName;
acc.Industry = DEFAULT_INDUSTRY;
return acc;
}
// 静的メソッド
public static List<Account> getActiveAccounts() {
return [SELECT Id, Name FROM Account WHERE Active__c = true];
}
}
SOQLとDML
Salesforceのデータベース操作は、SOQLとDMLを使用します。
// SOQL(データの取得)
List<Account> accounts = [
SELECT Id, Name, Industry
FROM Account
WHERE Industry = 'Technology'
LIMIT 10
];
// DML(データの作成・更新・削除)
Account newAccount = new Account(Name = 'New Account');
insert newAccount; // 挿入
newAccount.Industry = 'Finance';
update newAccount; // 更新
delete newAccount; // 削除
2. トリガーの基礎
トリガーの実行タイミング
トリガーには以下の実行タイミングがあります:
- before insert
- after insert
- before update
- after update
- before delete
- after delete
- after undelete
基本的なトリガーの例
trigger AccountTrigger on Account (before insert, before update) {
// トリガーコンテキスト変数の使用
System.debug('Trigger.new: ' + Trigger.new); // 新しいレコード
System.debug('Trigger.old: ' + Trigger.old); // 古いレコード
System.debug('Trigger.newMap: ' + Trigger.newMap); // 新しいレコードのマップ
System.debug('Trigger.oldMap: ' + Trigger.oldMap); // 古いレコードのマップ
// insertの場合の処理
if (Trigger.isBefore && Trigger.isInsert) {
for (Account acc : Trigger.new) {
if (String.isBlank(acc.Industry)) {
acc.Industry = 'Technology'; // デフォルト値を設定
}
}
}
// updateの場合の処理
if (Trigger.isBefore && Trigger.isUpdate) {
for (Account acc : Trigger.new) {
Account oldAcc = Trigger.oldMap.get(acc.Id);
if (acc.Industry != oldAcc.Industry) {
// 業種が変更された場合の処理
acc.Industry_Changed__c = true;
}
}
}
}
トリガーのベストプラクティス
- 一つのオブジェクトに対して1つのトリガーにまとめる
- ビジネスロジックは別クラスに分離する
- 再帰的な更新に注意する
- バルク処理に対応する
// トリガーハンドラークラス
public class AccountTriggerHandler {
private boolean isExecuting = false;
public void handleBeforeInsert(List<Account> newAccounts) {
if (isExecuting) return;
isExecuting = true;
try {
// ビジネスロジックの実行
for (Account acc : newAccounts) {
// 処理
}
} finally {
isExecuting = false;
}
}
}
// トリガー
trigger AccountTrigger on Account (before insert) {
AccountTriggerHandler handler = new AccountTriggerHandler();
if (Trigger.isBefore && Trigger.isInsert) {
handler.handleBeforeInsert(Trigger.new);
}
}
3. テストクラスの書き方
テストクラスの基本構造
@isTest
private class AccountServiceTest {
// テストデータの準備
@testSetup
static void setup() {
Account testAcc = new Account(Name = 'Test Account');
insert testAcc;
}
// 正常系のテスト
@isTest
static void testCreateNewAccount() {
// 準備
String accountName = 'New Test Account';
AccountService service = new AccountService(accountName);
// 実行
Test.startTest();
Account result = service.createNewAccount();
Test.stopTest();
// 検証
System.assertEquals(accountName, result.Name);
System.assertEquals('Technology', result.Industry);
}
// 例外のテスト
@isTest
static void testCreateNewAccountWithError() {
// 準備
AccountService service = new AccountService(null);
// 実行
Test.startTest();
try {
Account result = service.createNewAccount();
System.assert(false, '例外が発生するはずです');
} catch (Exception e) {
// 検証
System.assert(e.getMessage().contains('Account name cannot be null'));
}
Test.stopTest();
}
}
テストの重要なポイント
-
Test.startTest()とTest.stopTest()
- ガバナー制限をリセット
- 非同期処理の完了を待機
-
アサーション
- System.assertEquals(expected, actual)
- System.assertNotEquals(expected, actual)
- System.assert(condition)
- System.assertNull(actual)
- System.assertNotNull(actual)
-
テストデータの作成
- @testSetupを使用して共通のテストデータを準備
- TestDataFactoryクラスを作成してテストデータ作成を一元管理
@isTest
public class TestDataFactory {
public static Account createTestAccount(String name) {
Account acc = new Account(
Name = name,
Industry = 'Technology'
);
insert acc;
return acc;
}
public static List<Account> createTestAccounts(Integer count) {
List<Account> accounts = new List<Account>();
for (Integer i = 0; i < count; i++) {
accounts.add(new Account(
Name = 'Test Account ' + i,
Industry = 'Technology'
));
}
insert accounts;
return accounts;
}
}
まとめ
この記事では、Apexの基本的な部分を学びました:
- 基本文法とデータ型
- トリガーの仕組みと実装方法
- テストクラスの作成方法
次のステップとしては:
- より複雑なビジネスロジックの実装
- バッチ処理やスケジュール処理の実装
- API統合の実装