tyoshikawa1106のブログ

- Force.com Developer Blog -

SFDC:Spring'17 - ApexテストのモックフレームワークStub APIを試してみました

Spring'17で正式リリースされたStub APIを試してみました。Apexテストで例外テストができる仕組みを用意できたりするみたいです。

f:id:tyoshikawa1106:20170228224932p:plain

Apex スタブ API を正式リリース


リリースノートだけではイマイチ使い方がわかりませんでしたが、Apex開発者ガイドでサンプルコードが紹介されていました。
f:id:tyoshikawa1106:20170228225143p:plain

Build a Mocking Framework with the Stub API

使い方概要

まずテスト対象のクラスとして下記処理を用意します。

public class DateFormatter {    
    // Method to test    
    public String getFormattedDate(DateHelper helper) {
        return 'Today\'s date is ' + helper.getTodaysDate();
    }
}


次のヘルパークラスに処理部分をもたせます。

public class DateHelper {   
    // Method to stub    
    public String getTodaysDate() {
        return Date.today().format();
    }
}


上記処理は以下のような書き方で呼び出すことができます。

DateFormatter df = new DateFormatter();
DateHelper dh = new DateHelper();
String dateStr = df.getFormattedDate(dh);


このように独立させた処理をスタブAPIをつかって効率よくテストできるみたいです。

試験のために、私たちは隔離したいです getFormattedDate()この方法は、フォーマットが正常に動作していることを確認します。の戻り値getTodaysDate()この方法は、通常、日によって異なります。ただし、この場合には、我々は、フォーマットに私達のテストを分離するために一定の、予測可能な値を返すようにしたいです。むしろこの方法が一定の値を返すクラスの「偽」のバージョンを書くよりも、私たちは、クラスのスタブバージョンを作成します。スタブオブジェクトは、実行時に動的に作成され、私たちはその方法の「スタブ」動作を指定することができます。

Apexクラスのスタブバージョンを使用するには:

  • 実装することで、スタブクラスの動作を定義します System.StubProvider インタフェース。
  • 使用してスタブオブジェクトをインスタンス化します System.Test.createStub() 方法。
  • テストクラス内からスタブオブジェクトの関連するメソッドを呼び出します。


StubProviderインターフェイスの実装します。implements System.StubProvider と宣言したテストクラスになります。

@isTest
public class MockProvider implements System.StubProvider {
    
    public Object handleMethodCall(Object stubbedObject, String stubbedMethodName, 
        Type returnType, List<Type> listOfParamTypes, List<String> listOfParamNames, 
        List<Object> listOfArgs) {
        
        // The following debug statements show an example of logging 
        // the invocation of a mocked method.
       
        // You can use the method name and return type to determine which method was called.
        System.debug('Name of stubbed method: ' + stubbedMethodName);
        System.debug('Return type of stubbed method: ' + returnType.getName());
        
        // You can also use the parameter names and types to determine which method 
        // was called.
        for (integer i =0; i < listOfParamNames.size(); i++) {
            System.debug('parameter name: ' + listOfParamNames.get(i));
            System.debug('  parameter type: ' + listOfParamTypes.get(i).getName());
        }
        
        // This shows the actual parameter values passed into the stubbed method at runtime.
        System.debug('number of parameters passed into the mocked call: ' + 
            listOfArgs.size());
        System.debug('parameter(s) sent into the mocked call: ' + listOfArgs);
        
        // This is a very simple mock provider that returns a hard-coded value 
        // based on the return type of the invoked.
        if (returnType.getName() == 'String')
            return '8/8/2016';
        else 
            return null;
    }
}


クラスのスタブバージョンをインスタンス化します。

public class MockUtil {
    private MockUtil(){}

    public static MockProvider getInstance() {
        return new MockProvider();
    }
    
     public static Object createMock(Type typeToMock) {
        // Invoke the stub API and pass it our mock provider to create a 
        // mock class of typeToMock.
        return Test.createStub(typeToMock, MockUtil.getInstance());
    }
}


これでテストクラスからスタブAPIを利用する準備ができました。次のようなテストの書き方ができます。

@isTest 
public class DateFormatterTest {   
    @isTest 
    public static void testGetFormattedDate() {
        // Create a mock version of the DateHelper class.
        DateHelper mockDH = (DateHelper)MockUtil.createMock(DateHelper.class);
        DateFormatter df = new DateFormatter();
        
        // Use the mocked object in the test.
        System.assertEquals('Today\'s date is 8/8/2016', df.getFormattedDate(mockDH));
    }
}


無事開発者ガイドのサンプルコードのテストを実行することができました。
f:id:tyoshikawa1106:20170228230941p:plain


開発者ガイドのサンプルでは任意のシステム日付をセットしてテスト実行できることを確認できます。DateHelperクラスの処理でDate.todayでシステム日付を取得しています。MockProviderクラスの30行目でテストデータ用に任意の値で疑似システム日付を準備していました。DateFormatterTestクラスの12行目でスタブAPIをつかって用意した疑似システム日付がただしく利用できていることをassert処理で確認しています。


StubProviderインターフェイスの実装は少しややこしそうですが、疑似システム日付を用意できるのはすごく便利そうです。また、疑似Exceptionなども実装することができればテストがやりやすくなると思います。