Apex의 자동 테스트로 어려운 이야기와 솔루션

시작하기



이 문서는 TeamSpirit 어드벤트 캘린더 2018 문서입니다.
전 Java 엔지니어가 Apex의 자동 테스트에서 어려움을 겪었던 점과 해결책에 대해 썼습니다.

아키텍처



이번 주제로 하는 프로젝트는 레이어드 아키텍처를 채용하고 있어 다음과 같은 구성으로 되어 있습니다.
자주 보는 형태군요.


테스트 케이스



이번에는 애플리케이션 계층의 테스트에 대해 써 갑니다.
어플리케이션 계층의 테스트에서는, 인프라층의 구현에 영향을 받지 않고, 대상 메소드의 로직을 검증할 수 있고, 또한 테스트 코드는 가능한 한 간단하게 유지하고 싶습니다.

우선 어플리케이션층의 처리입니다.
1. 요청 입력 확인
2. 요청에 따라 인프라 계층에서 데이터 검색
3. 취득한 데이터를 응답

다음은 테스트 내용입니다.
1. 요청을 정확하게 받았는지, 요청의 항목 이름과 유형이 잘못되었습니까?
2. 요청이 잘못되면 제대로 처리 할 수 ​​있습니까?
3. 요청에서 받은 값을 올바르게 인프라 계층 메서드에 전달했는지
4. 취득한 데이터를 올바르게 응답하고 있습니까?
 
간단한 내용이군요.
이러한 처리의 경우, 테스트 코드에서는 인프라층의 메소드를 적당하게 모형화해,
애플리케이션 계층의 응답을 확인하고 싶지만 Apex에서는 이러한 테스트 코드를 쉽게 작성할 수 없습니다.
몇 가지 문제에 대해 자세히 설명합니다.

문제 1 Apex에는 편리한 Mock 기능이 없습니다.



Java에서는 익숙한 Mockito나 EazyMock 상당의 라이브러리가 Apex에는 없습니다.
대신, StubProvider 라는 기능이 제공되고 있으므로, 이것을 사용해 해결합시다.
다만, 그대로는 사용하기 어렵기 때문에 StubProvider를 랩 한 클래스를 작성합니다.

Mock 클래스의 구현에 따라 다음과 같이 테스트 코드를 작성할 수 있습니다.
(뒤쫓아 실장해 본 것 → htps : // 기주 b. 이 m / dsk-sgx / ax도 ck )

Test.cls
@isTest
private static void searchByOrderIdTest() {

  //------------------------
  // ここからがモックの処理です
  //------------------------
  // モックの戻り値を作成します
  Order order = new Order();
  order.id = order01;
  order.item = item01
  order.quantity = 20;
  order.price = 15000;

  // モッククラスを作成します
  Repository mock = (Repository)ApexMock.create(Repository.class);
  //searchOrdersが実行されたら、予め作成したorderを返却するように設定します
  ApexMock.whenCalled(searchOrders).thenReturn(order);
  //------------------------
  // ここまでがモックの処理です
  //------------------------

  // この例ではコンストラクタでモッククラスを渡す形にしています。
  // 以降は通常のテストと同様です。
  OrderApplication app = new OrderApplication(mock);
  // リクエスト作成
  Request request = new Request();
  request.id = order01;
  request.type = DVD;

  // 戻り値が正しくレスポンスに設定しているかを検証
  Response response = app.search(request);
  System.assertEquals(item01, response.orders[0].itemCode);
  System.assertEquals(20, response.orders[0].quantity);
  System.assertEquals(15000, response.orders[0].price);

  // 想定通りにメソッドが実行されていることを検証
  ApexMock.verify(mock)
        .called('searchOrders')  // searchOrdersが実行されていること
        .times(1)                // メソッドが1度だけ実行されていること
        .with('order01', 'DVD'); // 想定した引数で実行されていること
}



문제 2 Mock에서 Apex의 Id 클래스를 사용하는 경우 적절한 값의 Id를 만들 수 없습니다.



Apex에는 DB의 레코드 ID를 나타내는 ID 클래스라는 것이 있으며 적절하지 않은 값으로 인스턴스를 만들 수 없습니다.
이것은 설계에 의합니다만, 예를 들면 상기의 Order 클래스의 id 필드가 Id 형의 경우 order.id = ‘order01’
해결 방법
Id 클래스를 임의의 값으로 작성할 수 있는 클래스를 구현합니다.
Id 클래스는 객체(DB의 테이블)마다 prefix가 정해져 있으므로, 거기도 포함해 의사의 Id를 생성합니다.

DummyId.cls
@isTest
public class DummyId {
  private static Integer sequence = 0;

  /**
   * オブジェクトごとのダミーIDを作成します。
   * @param type 生成するSObjectの型情報
   * @return 生成したダミーID
   */
  public static Id of(SObjectType type) {
    String cnt = String.valueOf(sequence++);
    String sufix = '0'.repeat(15 - cnt.length()) + cnt;
    return objType.getDescribe().getKeyPrefix() + sufix;
  }
}

이 클래스를 이용하면 앞의 코드는 다음과 같이 Id형의 값을 작성할 수 있습니다.

Test.cls 일부 발췌
  // Order__cオブジェクトのIdを作成します
  Id dummyOrderId = DummyId.of(Order__c.sObjectType);
  Order order = new Order();
  order.id = dummyOrderId;

  // レスポンスと比較します
  Response response = app.search(request);
  System.assertEquals(dummyOrderId, response.orders[0].itemCode);

요약



그 밖에도 외부 파일을 읽을 수 없거나, 테스트 데이터 등록시에도 거버너 제한을 의식할 필요가 있거나 제약은 남습니다만, Salesforce는 해에 3회 릴리스를 하고 있기 때문에, 향후도 기능이 확충되고 기대할 수 있습니다.

쉽게 테스트하고 품질을 담보하십시오.
그럼 여러분, 멋진 크리스마스를! !

좋은 웹페이지 즐겨찾기