19
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Compose Previewのスクリーンショットテストの今の状況とComposeのPreviewがIDE上でどうやって作られるか

Last updated at Posted at 2023-07-01

Google I/Oで発表された、まだリリースされていない機能ですが、一番気になっている機能でもあります。
個人的にRoborazziというAndroid端末を使わずにスクショを取れるライブラリを作っているので、その両方の将来性みたいなところでも気になるところです。

確認方法

まだ未リリースの機能ではあるのですが、ソースコードはmainブランチ(mirror-goog-studio-main)に入ったりしており、見ていくことができます。

コミットでいうと 13c6af9acff44468927e0e8e0c7f6bdb1e6f4d41 時点での情報をお知らせします。
Android Gradle PluginはAOSPの以下にあるようです。
https://android.googlesource.com/platform/tools/base/+/13c6af9acff44468927e0e8e0c7f6bdb1e6f4d41/build-system

以下のmirror-goog-studio-mainブランチで開発が継続的にされているようです。
https://android.googlesource.com/platform/tools/base/+/refs/heads/mirror-goog-studio-main/build-system

スクリーンショットテストのテストを確認して挙動を確認する

まず何にしても使い方を知るにはテストを確認するとわかりやすい気がしています。

まとめると以下です

  • 事前条件
    • androidTest/にPreview関数の付いたComposable関数を置く
    • android.experimental.enableScreenshotTest を有効にする
  • screenshotTestDebugAndroidTest --record-golden でComposable関数のPreviewのスクショを撮る

ちょっとなんでかはわからないですが、デバイスで動かすわけではないのにandroidTestに置くようです (今後変わるかな?)

スクリーンショットテストのタスクを確認する

ここでどういう仕組みで動いているのか気になってきますよね?
タスクのコードは以下になります。

  • JUnitPlatformTestFrameworkを使う
  • JUnitPlatformTestFrameworkのincludeEngines("screenshot-test-engine")によってそのエンジンを使ってテストを実行するEngineを選ぶ
  • dependencyにengineである "com.android.tools.screenshot:junit-engine" (未リリース) を追加する ※1
  • エンジンを使ってテストを動かす

ちなみにこのjunit-engineはまだmaven.google.comにないようなので、実際に動かそうとすると失敗します。

※1

                val engineVersion = "0.0.1" +
                        if (Version.ANDROID_GRADLE_PLUGIN_VERSION.endsWith("-dev"))
                            "-dev"
                        else
                            "-alpha01"
                dependencies.add(
                        configurationName,
                        "com.android.tools.screenshot:junit-engine:${engineVersion}")
            }

screenshot:junit-engineを確認する

Engineのコードはこちらになります。

  • previewJarのMainを動かす。
    • Preview関数のあるソースコード一覧。output pathやgolden pathなどを渡す。
    • previewJarはスクリーンショットテストのタスクで設定している ※2

※2

                container.create(previewlibCliToolConfigurationName).apply {
                    isVisible = false
                    isTransitive = true
                    isCanBeConsumed = false
                    description = "A configuration to resolve PreviewLib CLI tool dependencies."
                }
                dependencies.add(
                        previewlibCliToolConfigurationName,
                        "com.android.screenshot.cli:screenshot:${Version.ANDROID_TOOLS_BASE_VERSION}")

screenshot.cli:screenshotを確認する

コード:
https://android.googlesource.com/platform/tools/base/+/13c6af9acff44468927e0e8e0c7f6bdb1e6f4d41/previewlib/cli/src/main/java/com/android/screenshot/cli/Main.kt

ComposeのPreviewはどうやって作られるのか?

ScreenshotProviderは以下にあります。
https://android.googlesource.com/platform/tools/base/+/13c6af9acff44468927e0e8e0c7f6bdb1e6f4d41/previewlib/cli/src/main/java/com/android/screenshot/cli/ScreenshotProvider.kt

ここではComposePreviewElementというものを呼び出しています。なんだかxmlという単語結構でてきます
ComposePreviewElementはandroid/tools/idea/にあるものなので、Android Studio側で使われる実装だと思われます。

ComposePreviewElementのコードは以下のようになっています。xmlでComposeを作ることによってPreviewを表示していそうということがわかりました。

  override fun toPreviewXml(): PreviewXmlBuilder {
    val matchParent = displaySettings.showDecoration
    val width =
      dimensionToString(
        configuration.width,
        if (matchParent) SdkConstants.VALUE_MATCH_PARENT else VALUE_WRAP_CONTENT
      )
...
    val xmlBuilder =
      PreviewXmlBuilder(COMPOSE_VIEW_ADAPTER_FQN)
        .androidAttribute(ATTR_LAYOUT_WIDTH, width)
        .androidAttribute(ATTR_LAYOUT_HEIGHT, height)
...
        .toolsAttribute("composableName", composableMethodFqn) // ← ここでComposeの関数の場所を渡す

...
    return xmlBuilder
  }

で、このComposeを表示しているAndroid Viewは実際にどこなのか?が疑問になります。

コードを追うと androidx.compose.ui.tooling.ComposeViewAdapter というクラスのようです。

そのコードは以下にあります。FrameLayoutを継承したViewになっています。

実際に中では以下のようにComposable関数を呼び出しているようです。

ComposableInvoker.invokeComposable(
    className,
    methodName,
    composer,
    *getPreviewProviderParameters(parameterProvider, parameterProviderIndex)
)

まとめ

個人的にはなかなか冒険感があって面白かったです。

  • 現状はスクリーンショットテストは、なぜかandroidTest sourcesetにPreview関数を置く形で実装する
  • Android StudioのPreviewがベースになって実装されているのでPreview以上のことは難しそうに見えた
    • (なのでRobolectricによってEspresso、Hiltなどと一緒に使えるRoborazziとは競合しなさう)
  • ComposeのPreviewがxmlのPreviewによって実現されていた
19
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
19
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?