{"id":2685,"date":"2022-08-11T11:54:09","date_gmt":"2022-08-11T18:54:09","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/surface-duo\/?p=2685"},"modified":"2022-08-11T11:54:09","modified_gmt":"2022-08-11T18:54:09","slug":"test-kit-annotations-update","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/surface-duo\/test-kit-annotations-update\/","title":{"rendered":"Test Kit Annotations update"},"content":{"rendered":"<p>\n  Hello Android developers!\n<\/p>\n<p>\n  We are excited to announce an update for the foldable Test Kit library. Test Kit provides utility methods and classes that help you write instrumented tests for large screen and foldable devices.  This update adds annotations which are useful for creating tests for one posture at a time. Now, instead of adding extra code to span an app or simulate a folding feature, you can just add an annotation to set up your tests.\n<\/p>\n<p>\n  Annotations can be used to configure tests for:\n<\/p>\n<ul>\n<li>\n    single-screen or dual-screen modes\n  <\/li>\n<li>\n    specific target devices\n  <\/li>\n<li>\n    specific device orientations\n  <\/li>\n<li>\n    simulated folding features\n  <\/li>\n<\/ul>\n<p>\n  The annotation syntax is perfect for simple tests where the device remains in the same state for the entire test. The annotations help keep the test code readable and expressive while enabling you to target specific postures and foldable device states.\n<\/p>\n<p>\n  Test Kit annotations work on any foldable device that supports Jetpack Window Manager, including Surface Duo.\n<\/p>\n<p>\n  <img decoding=\"async\" width=\"1421\" height=\"895\" src=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/08\/text-description-automatically-generated.png\" class=\"wp-image-2686\" alt=\"Example source code showing tests with annotations - copyable source is shown later in the article\" srcset=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/08\/text-description-automatically-generated.png 1421w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/08\/text-description-automatically-generated-300x189.png 300w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/08\/text-description-automatically-generated-1024x645.png 1024w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/08\/text-description-automatically-generated-768x484.png 768w\" sizes=\"(max-width: 1421px) 100vw, 1421px\" \/>\n<\/p>\n<p><em>Figure 1: Android Studio screenshot showing simple tests that detect the correct text label is showing based on the device orientation set via an annotation <\/em>\n<\/p>\n<p>\n  If you need to test complex app flows including changing the device state <em>during the test<\/em>, you should <a href=\"https:\/\/devblogs.microsoft.com\/surface-duo\/foldable-ui-test-kit\/\">simulate a folding feature or use swipe gestures<\/a> in code, which is also covered in our <a href=\"https:\/\/docs.microsoft.com\/dual-screen\/android\/test-kit\/\">Test Kit documentation<\/a>.\n<\/p>\n<h2>How to add Test Kit to your project <\/h2>\n<p>\n  The Test Kit components are available on Maven Central. To write tests with the annotations feature, include the dependencies that match your layout preferences (Views or Jetpack Compose) to your app\u2019s <strong>build.gradle<\/strong> file:\n<\/p>\n<p><strong>Views<\/strong>\n<\/p>\n<pre>  androidTestImplementation \"com.microsoft.device.dualscreen.testing:testing-kotlin:1.0.0-alpha4\"\r\n  androidTestImplementation \"androidx.test.uiautomator:uiautomator:2.2.0\"\r\n  androidTestImplementation \"androidx.test.espresso:espresso-core:3.4.0\"\r\n  androidTestImplementation \"androidx.test:runner:1.4.0\"\r\n  androidTestImplementation \"androidx.test:rules:1.4.0\"<\/pre>\n<p><strong>Jetpack Compose<\/strong>\n<\/p>\n<pre>  androidTestImplementation \"com.microsoft.device.dualscreen.testing:testing-compose:1.0.0-alpha04\"\r\n  androidTestImplementation \"androidx.test.uiautomator:uiautomator:2.2.0\"\r\n  androidTestImplementation \"androidx.compose.ui:ui-test-junit4:1.1.1\"<\/pre>\n<h3>How it works<\/h3>\n<\/p>\n<p>\n  The core for these annotations is two utility classes: <code>FoldableTestRule<\/code> and <code>FoldableJunit4ClassRunner<\/code>.\n<\/p>\n<p>\n  <code>FoldableTestRule<\/code> is a custom TestRule that must be used together with Test Kit annotations (<code>@SingleScreenTest<\/code>, <code>@DualScreenTest<\/code>, <code>@MockFoldingFeature<\/code> and <code>@DeviceOrientation<\/code>). Without this rule, the annotations have no effect.\n<\/p>\n<p>\n  <code>FoldableJUnit4ClassRunner<\/code> is a custom AndroidJunit4ClassRunner that validates the parameters for Test Kit annotations, and it is recommended to always use it to make the tests run as intended.\n<\/p>\n<h2>Usage scenarios<\/h2>\n<p>\n  This section shows which annotations can be used for different test cases.\n<\/p>\n<h3>Test in single- or dual-screen mode<\/h3>\n<p>\n  If you want to run tests in single-screen or dual-screen mode, you should use the <code>@SingleScreenTest<\/code> or <code>@DualScreenTest<\/code> annotation together with FoldableTestRule and FoldableJUnit4ClassRunner as shown in the following example:\n<\/p>\n<pre>  @RunWith(FoldableJUnit4ClassRunner::class)\r\n  class TestSample {\r\n      private val activityScenarioRule = activityScenarioRule&lt;MainActivity&gt;()\r\n      private val foldableTestRule = FoldableTestRule()\r\n   \r\n      @get:Rule\r\n      val testRule: TestRule = foldableRuleChain(activityScenarioRule, foldableTestRule)\r\n   \r\n      @Test\r\n      @DualScreenTest\r\n      fun sampleTestMethod() {\r\n      }\r\n  }<\/pre>\n<p>\n  You can use these annotations either at the test class level or test method level, but not both at the same time.  You can find more about these annotations here: <a href=\"https:\/\/docs.microsoft.com\/dual-screen\/android\/test-kit\/test-kit-annotations?tabs=views#singlescreentestdualscreentest\">@SingleScreenTest\/@DualScreenTest<\/a>\n<\/p>\n<h2>Test on a specific device<\/h2>\n<p>\n  If you want to run tests on specific target devices, you must use <code>@TargetDevices<\/code> annotation with the desired devices provided by the <code>devices<\/code> parameter. You can also skip some devices with this annotation using the <code>ignoreDevices<\/code> parameter, like in the following example:\n<\/p>\n<pre>  @RunWith(FoldableJUnit4ClassRunner::class)\r\n  @TargetDevices(devices = [DeviceModel.SurfaceDuo])\r\n  class TestSample {\r\n      private val activityScenarioRule = activityScenarioRule&lt;MainActivity&gt;()\r\n      private val foldableTestRule = FoldableTestRule()\r\n   \r\n      @get:Rule\r\n      val testRule: TestRule = foldableRuleChain(activityScenarioRule, foldableTestRule)\r\n   \r\n      @Test\r\n      fun sampleTestMethod() { \/\/ test will ONLY run on Surface Duo devices\r\n      }\r\n  }<\/pre>\n<p>\n  In this sample, all the methods from TestSample class will run only on Surface Duo devices. You can use this annotation also at the test method level, but not in both places at the same time.\n<\/p>\n<p>\n  If you want to ignore some devices, you can use it like this:\n<\/p>\n<pre>  @RunWith(FoldableJUnit4ClassRunner::class)\r\n  @TargetDevices(ignoreDevices = [DeviceModel.SurfaceDuo])\r\n  class TestSample {\r\n      private val activityScenarioRule = activityScenarioRule&lt;MainActivity&gt;()\r\n      private val foldableTestRule = FoldableTestRule()\r\n   \r\n      @get:Rule\r\n      val testRule: TestRule = foldableRuleChain(activityScenarioRule, foldableTestRule)\r\n   \r\n      @Test\r\n      fun sampleTestMethod() { \/\/ test will NOT run on Surface Duo devices\r\n      }\r\n  }<\/pre>\n<p>\n  You can find more about this annotation here: <a href=\"https:\/\/docs.microsoft.com\/dual-screen\/android\/test-kit\/test-kit-annotations?tabs=views#targetdevices\">@TargetDevices<\/a>\n<\/p>\n<h3>Test a specific orientation<\/h3>\n<p>\n  You can run tests in a specific device orientation with the @DeviceOrientation annotation, or if you want also to run it on a single-screen or dual-screen posture, you can use <code>@SingleScreenTest<\/code> or <code>@DualScreenTest<\/code> with the <code>deviceOrientation<\/code> parameter, like this:\n<\/p>\n<pre>  @RunWith(FoldableJUnit4ClassRunner::class)\r\n  @DeviceOrientation(orientation = UiAutomation.ROTATION_FREEZE_180)\r\n  class TestSample {\r\n      private val activityScenarioRule = activityScenarioRule&lt;MainActivity&gt;()\r\n      private val foldableTestRule = FoldableTestRule()\r\n   \r\n      @get:Rule\r\n      val testRule: TestRule = foldableRuleChain(activityScenarioRule, foldableTestRule)\r\n   \r\n      @Test\r\n      fun sampleTestMethod() { \/\/ test is run at 180 degree rotation\r\n      }\r\n  }<\/pre>\n<\/p>\n<pre>  @RunWith(FoldableJUnit4ClassRunner::class)\r\n  @SingleScreenTest(orientation = UiAutomation.ROTATION_FREEZE_180)\r\n  class TestSample {\r\n      private val activityScenarioRule = activityScenarioRule&lt;MainActivity&gt;()\r\n      private val foldableTestRule = FoldableTestRule()\r\n   \r\n      @get:Rule\r\n      val testRule: TestRule = foldableRuleChain(activityScenarioRule, foldableTestRule)\r\n   \r\n      @Test\r\n      fun sampleTestMethod() { \/\/ test is run at 180 degree rotation\r\n      }\r\n  }<\/pre>\n<h3>Test with a simulated folding feature<\/h3>\n<p>\n  If you want to run tests with a simulated folding feature, you should use <code>@MockFoldingFeature<\/code> and provide the windowBounds, center, size, state, or orientation parameter. None of these parameters is required because they have default values, but you can find more info about them here: <a href=\"https:\/\/docs.microsoft.com\/dual-screen\/android\/test-kit\/test-kit-annotations?tabs=views#mockfoldingfeature\">@MockFoldingFeature<\/a>\n<\/p>\n<pre>  @RunWith(FoldableJUnit4ClassRunner::class)\r\n  @MockFoldingFeature(\r\n      size = 2, \r\n      state = FoldingFeatureState.FLAT, \r\n      orientation = FoldingFeatureOrientation.HORIZONTAL\r\n  )\r\n  class TestSample {\r\n      private val activityScenarioRule = activityScenarioRule&lt;MainActivity&gt;()\r\n      private val foldableTestRule = FoldableTestRule()\r\n   \r\n      @get:Rule\r\n      val testRule: TestRule = foldableRuleChain(activityScenarioRule, foldableTestRule)\r\n   \r\n      @Test\r\n      fun sampleTestMethod() { \/\/ test is run with custom folding feature configuration\r\n      }\r\n  }<\/pre>\n<h2>\n  Samples\n<\/h2>\n<p>\n  To make it easier to understand how to use Test Kit annotations, we added a new <a href=\"https:\/\/github.com\/microsoft\/surface-duo-sdk\/tree\/main\/utils\/sample\">SDK sample<\/a> to our Views library. We&#8217;ve also updated our <a href=\"https:\/\/github.com\/microsoft\/surface-duo-compose-samples\/\">Compose samples<\/a> to use the new annotations in UI tests.\n  <\/p>\n<p>\n  To see examples of how to use each specific annotation, check out the links below:\n<\/p>\n<table>\n<tr>\n<th>\n<p>\n  Annotation name\n<\/p>\n<\/th>\n<th>\n<p>\n  Sample code\n<\/p>\n<\/th>\n<\/tr>\n<tr>\n<td>\n<p>\n  @SingleScreenTest\/@DualScreenTest\n<\/p>\n<\/td>\n<td>\n<p><a href=\"https:\/\/github.com\/microsoft\/surface-duo-sdk\/blob\/main\/utils\/sample\/src\/androidTest\/java\/com\/microsoft\/device\/dualscreen\/testing\/sample\/SurfaceDuoTestSample.kt\">SurfaceDuoTestSample.kt<\/a>\n<\/p>\n<p><a href=\"https:\/\/github.com\/microsoft\/surface-duo-sdk\/blob\/main\/utils\/sample\/src\/androidTest\/java\/com\/microsoft\/device\/dualscreen\/testing\/sample\/OtherFoldableTestSample.kt\">OtherFoldableTestSample.kt<\/a>\n<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>\n  @MockFoldingFeature\n<\/p>\n<\/td>\n<td>\n<p><a href=\"https:\/\/github.com\/microsoft\/surface-duo-sdk\/blob\/main\/utils\/sample\/src\/androidTest\/java\/com\/microsoft\/device\/dualscreen\/testing\/sample\/MockFoldingFeatureSample.kt\">MockFoldingFeatureSample.kt<\/a>\n<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>\n  @TargetDevices\n<\/p>\n<\/td>\n<td>\n<p><a href=\"https:\/\/github.com\/microsoft\/surface-duo-sdk\/blob\/main\/utils\/sample\/src\/androidTest\/java\/com\/microsoft\/device\/dualscreen\/testing\/sample\/TargetDevicesSample.kt\">TargetDevicesSample.kt<\/a>\n<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>\n  @DeviceOrientation\n<\/p>\n<\/td>\n<td>\n<p><a href=\"https:\/\/github.com\/microsoft\/surface-duo-sdk\/blob\/main\/utils\/sample\/src\/androidTest\/java\/com\/microsoft\/device\/dualscreen\/testing\/sample\/SurfaceDuoDeviceOrientationSample.kt\">SurfaceDuoDeviceOrientationSample.kt<\/a>\n<\/p>\n<p><a href=\"https:\/\/github.com\/microsoft\/surface-duo-sdk\/blob\/main\/utils\/sample\/src\/androidTest\/java\/com\/microsoft\/device\/dualscreen\/testing\/sample\/OtherFoldableDeviceOrientationSample.kt\">OtherFoldableDeviceOrientationSample.kt<\/a>\n<\/p>\n<\/td>\n<\/tr>\n<\/table>\n<p>\n  Resources and feedback \n<\/p>\n<p>\n  You can review the <a href=\"https:\/\/docs.microsoft.com\/dual-screen\/android\/test-kit\/test-kit-annotations?tabs=views\">documentation<\/a> for more info. You can also try out our sample and check out all these scenarios. \n<\/p>\n<p>\n  The\u00a0<a href=\"https:\/\/github.com\/microsoft\/surface-duo-sdk\/tree\/main\/snackbar\">source code for the Test Kit\u00a0<\/a>is available on GitHub if you\u2019re curious about the implementation or wish to contribute. Also visit the\u00a0<a href=\"https:\/\/docs.microsoft.com\/dual-screen\/android\/api-reference\/dualscreen-library\/?WT.mc_id=docs-surfaceduoblog-ancirja\">documentation<\/a>\u00a0for more information on all the features in the dual-screen library for Kotlin and Java developers.\u00a0\u00a0 \n<\/p>\n<p>\n  Thanks to the team \u2013\u00a0<a href=\"https:\/\/www.linkedin.com\/in\/bianca-miron-ro\/\">Bianca Miron<\/a>,\u00a0<a href=\"https:\/\/twitter.com\/CesarValiente\">Cesar Valiente<\/a> and\u00a0<a href=\"https:\/\/devblogs.microsoft.com\/surface-duo\/author\/khalper\/\">Kristen Halper<\/a>  for their work on this update.\u00a0\u00a0\u00a0 \n<\/p>\n<p>\n  We\u2019d love to hear from you and how you plan to use the Test Kit library in your apps. Please reach to out using the\u00a0<a href=\"http:\/\/aka.ms\/SurfaceDuoSDK-Feedback\">feedback forum<\/a>\u00a0or message us\u00a0<a href=\"https:\/\/twitter.com\/surfaceduodev\">@surfaceduodev<\/a>\u00a0on Twitter.\u00a0\u00a0\u00a0 \n<\/p>\n<p>\n  Finally, please join us for our <a href=\"https:\/\/twitch.tv\/surfaceduodev\">dual screen developer livestream<\/a> at 11am (Pacific time) each Friday \u2013 mark it in your calendar and check out the <a href=\"https:\/\/www.youtube.com\/channel\/UClGu9QLtPNz8OdddBfhZXPA\">archives on YouTube<\/a>.\u00a0\n<\/p>\n<\/p>\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hello Android developers! We are excited to announce an update for the foldable Test Kit library. Test Kit provides utility methods and classes that help you write instrumented tests for large screen and foldable devices. This update adds annotations which are useful for creating tests for one posture at a time. Now, instead of adding [&hellip;]<\/p>\n","protected":false},"author":47750,"featured_media":2686,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[717,692,706,473,571],"class_list":["post-2685","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-surface-duo-sdk","tag-foldable","tag-jetpack-compose","tag-jetpack-window-manager","tag-kotlin","tag-testing"],"acf":[],"blog_post_summary":"<p>Hello Android developers! We are excited to announce an update for the foldable Test Kit library. Test Kit provides utility methods and classes that help you write instrumented tests for large screen and foldable devices. This update adds annotations which are useful for creating tests for one posture at a time. Now, instead of adding [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/posts\/2685","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/users\/47750"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/comments?post=2685"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/posts\/2685\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/media\/2686"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/media?parent=2685"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/categories?post=2685"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/tags?post=2685"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}