Firebase A/B Testing（RemoteConfig）でテスト対象者／非対象者を見分ける方法

# やりたいこと

[Firebase A/B Testing](https://firebase.google.com/docs/ab-testing?hl=ja) を使って AB テストを実施する際、当然ですかユーザーが A なのか B なのかを見分ける方法は簡単です。しかし、ユーザーがテスト対象者なのか、非対象者なのかを見分ける方法を探すのに少し苦労したのでメモしておきたいと思います。

もし間違っている箇所などありましたらご指摘ください🙇‍♂️

# テスト対象者／非対象者を見分ける方法

結論から書きます。

```.kotlin
/**
*  AB テスト対象者かどうか判断する。
*/
fun isAbTestUser(): Boolean {
    val remoteConfig = FirebaseRemoteConfig.getInstance()
    val value = remoteConfig.getValue("variant_key")
    if (value.source == FirebaseRemoteConfig.VALUE_SOURCE_REMOTE) {
        return true
    } else {
        return false
    }
}
```

以下の箇所で FirebaseRemoteConfigValue オブジェクトを取得しています。

```.kotlin
val value = remoteConfig.getValue("variant_key")
```

`FirebaseRemoteConfigValue.source` には、以下３つのどれかの値が含まれています。この値が Remote Config サーバーから取得したものだと判断できれば、ユーザーが AB テスト対象者だと判断できます。

1. VALUE_SOURCE_STATIC（Remote Config で初期値として設定されている値）
2. VALUE_SOURCE_DEFAULT（自分が設定したデフォルト値）
3. VALUE_SOURCE_REMOTE（Remote Config サーバーから取得した値）

（細かいことを言えば、ユーザーがテスト対象者に割り当てられたが Remote Config との通信に失敗してデフォルト値が使われた場合は非対象者になってしまうかと思います。このユーザーを対象として扱うか、非対象者として扱うかは要件次第かと思いますので、各自ご判断いただければ。）

# どうやって判断しているのか？

ここからは蛇足となりますが、上記で示した１〜３はどのようにして判別されているのか知るために、少しソースをみてみたいと思います。

まずは `getValue()` 関数を見てみます。

```ConfigGetParameterHandler.java
public FirebaseRemoteConfigValue getValue(String key) {
  String activatedString = getStringFromCache(activatedConfigsCache, key);
  if (activatedString != null) {
    callListeners(key, getConfigsFromCache(activatedConfigsCache));
    return new FirebaseRemoteConfigValueImpl(activatedString, VALUE_SOURCE_REMOTE);
  }

  String defaultsString = getStringFromCache(defaultConfigsCache, key);
  if (defaultsString != null) {
    return new FirebaseRemoteConfigValueImpl(defaultsString, VALUE_SOURCE_DEFAULT);
  }

  logParameterValueDoesNotExist(key, "FirebaseRemoteConfigValue");
  return new FirebaseRemoteConfigValueImpl(DEFAULT_VALUE_FOR_STRING, VALUE_SOURCE_STATIC);
}
```

ここから以下のことがわかります。

- `getStringFromCache(activatedConfigsCache, key)` から値が取得できたら VALUE_SOURCE_REMOTE
- `getStringFromCache(defaultConfigsCache, key)` から値が取得できたら VALUE_SOURCE_DEFAULT
- それ以外は VALUE_SOURCE_STATIC

ここでポイントとなっているメンバ変数 `activatedConfigsCache` は ConfigGetParameterHandler クラスのコンストラクタで受け取っている ConfigCacheClient オブジェクトです。

Remote Config から取得した値はインメモリでキャッシュされ、そのキャッシュ値にアクセスするためのクライアントが ConfigCacheClient オブジェクトのようです。

そして `activatedConfigsCache` の出どころを辿ると `RemoteConfigComponent#getCacheClient()` に行き着きます。

```RemoteConfigComponent.java
public synchronized FirebaseRemoteConfig get(String namespace) {
  ConfigCacheClient fetchedCacheClient = getCacheClient(namespace, FETCH_FILE_NAME);
  // ↓ これ
  ConfigCacheClient activatedCacheClient = getCacheClient(namespace, ACTIVATE_FILE_NAME);
  ConfigCacheClient defaultsCacheClient = getCacheClient(namespace, DEFAULTS_FILE_NAME);
  ...
}
```

`ACTIVATE_FILE_NAME` は アクティベートされた値が保存されているファイルのファイル名です。namespace とファイル名でファイルの保存場所がわかるので、`activatedCacheClient` は当該ファイルへアクセスできるようになります。

つまり、Remote Config サーバーから取得して有効化された値は専用のファイル（ACTIVATE_FILE_NAME）にキャッシュされる仕組みになっているため、そこから値が取得できた場合は AB テストの対象者であると判断できる、という寸法です。

# まとめ

AB テスト対象者を見分けているロジック。

- Remote Config サーバーから取得、有効化された値は専用のファイルにキャッシュされる
- 専用ファイルから値が取得できた = Remote Config から取得した値だと言える
- Remote Config から値を取得しているということは AB テストの対象者だと言える