はじめに
AppExchangeアプリ開発時のTIPSです。他のケースではたぶん使わないかなと思います。
権限セットにあるカスタムオブジェクト/項目の権限を設定しており、あるCRUD/FLS権限チェックを伴う処理に対して、以下のことをテストしたいということはそれなりにあるかなと思います。
- 権限セットを付与したユーザはその処理ができること
- 権限セットを付与していないユーザはその処理ができないこと
そのときのテストの書き方を説明します。
テストの書き方
例えば、下のようなSomeLogic.readPrivateObjects
というCRUD/FLS権限チェックを伴うメソッドがあるとします。
public with sharing class SomeLogic {
public static PrivateObject__c[] readPrivateObjects() {
// 何らかのCRUD/FLS権限チェックを伴う処理
return [
SELECT Id, PrivateField__c FROM PrivateObject__c
WITH SECURITY_ENFORCED
];
}
}
テストコードは以下のように書けます。アサーションは本題ではないので適当です。
@isTest
private class SomeLogicTest {
/**
* 権限セットを付与したユーザで実行し成功するケース
*/
@isTest
private static void testReadPrivateObjectsSuccess() {
PrivateObject__c[] expectedRecords = TestHelper.createPrivateObjects();
User standardUser = TestHelper.createUser('standard', '標準ユーザ');
TestHelper.assignPermissionSet(standardUser.Id, 'SinePermSetName');
PrivateObject__c[] actualRecords;
Test.startTest();
System.runAs(standardUser) {
actualRecords = SomeLogic.readPrivateObjects();
}
Test.stopTest();
System.assertEquals(expectedRecords.size(), actualRecords.size());
// 他にもアサーションを書く
}
/**
* 権限セットを付与しないユーザで実行し失敗するケース
*/
@isTest
private static void testReadPrivateObjectsError() {
PrivateObject__c[] expectedRecords = TestHelper.createPrivateObjects();
User standardUser = TestHelper.createUser('standard', '標準ユーザ');
QueryException actualEx;
Test.startTest();
System.runAs(standardUser) {
try {
SomeLogic.readPrivateObjects();
} catch (QueryException e) {
actualEx = e;
}
}
Test.stopTest();
System.assertNotEquals(null, actualEx);
}
}
testReadPrivateObjectsSuccess
が権限セットを付与したユーザで実行し成功するケースです。権限セットを付与したユーザを作成し、System.runAs
でそのユーザでテストしたいメソッドを実行するのがポイントです。System.runAs
はテストコードのみで使用できるメソッドで、指定したユーザで処理を実行することができます。
testReadPrivateObjectsError
は権限セットを付与していないユーザで実行し、きちんとエラーが出ることをアサートします。
@isTest
public class TestHelper {
public static PrivateObject__c[] createPrivateObjects() {
// PrivateObject__cレコードを適当に作る
// 省略
}
public static User createUser(String emailPrefix, String profileName) {
Profile p = [SELECT Id FROM Profile WHERE Name = :profileName];
User u = new User(
Alias = emailPrefix.substring(0, 3),
Email = emailPrefix + '@somedomain.example.com',
EmailEncodingKey = 'UTF-8',
FirstName = 'User',
LastName = emailPrefix,
LanguageLocaleKey = 'ja',
LocaleSidKey='ja_JP',
ProfileId = p.Id,
TimeZoneSidKey = 'Asia/Tokyo',
UserName = emailPrefix + '@somedomain.example.com'
);
insert u;
return u;
}
public static void assignPermissionSet(Id userId, String permSetName) {
User adminUser = [SELECT Id FROM User WHERE Id = :UserInfo.getUserId()];
System.runAs(adminUser) {
PermissionSet permset = [
SELECT Id, Name FROM PermissionSet
WHERE Name = :permSetName AND NamespacePrefix = 'somenamespace'
];
insert new PermissionSetAssignment(
PermissionSetId = permset.Id,
AssigneeId = userId
);
}
}
}
先ほど権限セットを付与するために呼んでいたTestHelper.assignPermissionSet
ですが、上のように実装しています。基本的にはPermissionSetAssignment
というオブジェクトのレコードを作成すればいいだけです。ただし、素直に行うと以下のエラーが発生します。
MIXED_DML_OPERATION, 非設定オブジェクトを更新した後の設定オブジェクト上の DML 操作 (またはその逆) は、許可されていません
それを回避するために再びSystem.runAs
を使っています。System.runAs
を使うとトランザクションを分けることができるので、このエラーを回避することができます。権限セット以外でも上のエラーが出たときはこのテクニックは使えます。
終わりに
上記コードは実際使っているコードから記事用に再編したもので実際動かしていないので、動かないかもしれません。誤字とかもあるかもしれません。それくらい試せというところですが、記事を書き上げたら面倒になりました。手抜きですみませんミスがあればコメントください。
とりあえず基本的なやり方を参考にしていただければと思います。