<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Eric Labelle on Medium]]></title>
        <description><![CDATA[Stories by Eric Labelle on Medium]]></description>
        <link>https://medium.com/@eric.labelle?source=rss-f9c9cd61c12------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/2*UI_HNe1dB8hiEJuLBY6qOg.jpeg</url>
            <title>Stories by Eric Labelle on Medium</title>
            <link>https://medium.com/@eric.labelle?source=rss-f9c9cd61c12------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 15 Nov 2026 03:09:05 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@eric.labelle/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[Introducing PublishTestResultScreenshot]]></title>
            <link>https://medium.com/@eric.labelle/introducing-publishtestresultscreenshot-ea8b146e8436?source=rss-f9c9cd61c12------2</link>
            <guid isPermaLink="false">https://medium.com/p/ea8b146e8436</guid>
            <category><![CDATA[ui-testing]]></category>
            <category><![CDATA[open-source]]></category>
            <category><![CDATA[azure-devops]]></category>
            <category><![CDATA[ci-pipeline]]></category>
            <category><![CDATA[visual-studio-marketplace]]></category>
            <dc:creator><![CDATA[Eric Labelle]]></dc:creator>
            <pubDate>Mon, 09 Mar 2020 14:01:19 GMT</pubDate>
            <atom:updated>2020-03-09T14:01:19.880Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0oGOFh84_rTOkP_gWdwrwA.png" /></figure><blockquote>Testing UIs is challenging in any context, and mobile apps are no different.</blockquote><p>This is how we started our previous article about UI testing in an Azure DevOps context. We learned out that we had to deep dive into it to answer all the questions raised by this concern. Fortunately, we ended up with a fully functional UI testing pipeline based on a custom Python script to upload UI artifacts.</p><p><a href="https://medium.com/genetec-tech/android-ui-testing-in-azure-devops-81bbe7cea9fd">Android UI Testing in Azure DevOps</a></p><p>We decided to take our solution a step further by wrapping up all the logic into a self-contained Azure DevOps extension.</p><p>It’s been in the making for a while now, but I finally did it! My first Azure DevOps custom build task is now available for anyone to try.</p><p>Keep in mind, this is still an early <em>preview</em> and have been tested mostly with Android UI Test in mind.</p><h3>About the extension</h3><p>You can install it or find more information on the task from the <a href="https://marketplace.visualstudio.com/items?itemName=Genetec.publish-test-result-screenshot">Azure DevOps Marketplace</a> and the <a href="https://github.com/Genetec/azure-devops-extension-publishtestresultscreenshot">Github repo</a>.</p><p>The task has been used in our Android UI Testing pipeline for a few weeks now. Essentially, the task is a <em>typescript</em> adaptation from my previous <em>python</em> script (<em>see referenced article</em>).</p><ul><li>It must run after the <strong>PublishTestResult</strong> task since it’s using the reports published by that task.</li><li>It will search for screenshots to upload on each test failure</li></ul><h4>What it can do?</h4><ul><li>You can configure the search folder for the screenshot.</li><li>Upload your screenshots in the right test result within the attachment tab</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*qou32NSSQxmR4a-nVBHAfg.gif" /><figcaption>Before / After the task execution</figcaption></figure><h4>What it cannot do… yet?</h4><ul><li>Configure JSON attributes used to compose the screenshot search path</li><li>Configure the screenshot’s file extensions</li></ul><h3>FAQ</h3><h4><strong>I’ve added the task to my pipeline, but screenshots are never in the resulting test reports?</strong></h4><p>The following results are possible (<em>Make sure to check the “Enable system diagnostics” option on the pipeline to get more insight on the task status</em>):</p><ul><li><strong>Succeeded</strong>: Everything went well, you should see a message at the end.</li><li><strong>SucceededWithIssues</strong>: There can be two reasons: The upload failed for some/all screenshots <strong>OR</strong> some/all screenshots files are missing. A message will be detailing the situation within the task result logs.</li></ul><pre>##vso[task.complete result=SucceededWithIssues;]All screenshots were missing. <br>Task completed. Published 0/2 screenshots</pre><ul><li><strong>Skipped</strong>: There is no test failure associated with the current build.</li></ul><pre>##vso[task.complete result=Skipped;]No test failures found.</pre><ul><li><strong>Failed</strong>: Most of the time you’ll get this status if you forgot to specify the mandatory parameter organization.</li></ul><pre>##vso[task.complete result=Failed;]Organization is mandatory</pre><h4><strong>Everything is setup but I don’t see my screens.</strong></h4><p>It could be from a few problems. The first step is to run your pipeline with Diagnostic, it should provide a better understanding of what happened.</p><ul><li>The search folder is wrong and the task can’t find your screenshots</li></ul><pre>2 tests failed. Will proceed with screenshot upload.<br>##vso[task.debug]screenshotFolder=./app/mywrongfolder<br>##vso[task.debug]Searching for image at path: ./app/mywrongfolder/com.sampleapp.ExampleUiTest/onNetworkError_errorDialogIsShown.png<br>##vso[task.debug]Failure - No screenshot found for com.sampleapp.ExampleUiTest/onNetworkError_errorDialogIsShown</pre><h3>What’s next?</h3><ul><li>Unit test the task</li><li>Setup a CI/CD pipeline to distribute this task automatically</li></ul><h4>How can you help?</h4><ul><li>Test in your UI pipeline</li><li>Test with NUnit</li><li>Test on Azure Server</li></ul><p>I’ve made it this far all by myself so I can’t wait to see what you guys can bring to this task and make it even better.</p><p>Fell free to add feature requests or bugs using the <a href="https://github.com/Genetec/azure-devops-extension-publishtestresultscreenshot/issues">GitHub issues</a>. Your help is, of course, welcomed.</p><p>Thanks and happy testing! :)</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=ea8b146e8436" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Android UI Testing in Azure DevOps]]></title>
            <link>https://medium.com/genetec-tech/android-ui-testing-in-azure-devops-81bbe7cea9fd?source=rss-f9c9cd61c12------2</link>
            <guid isPermaLink="false">https://medium.com/p/81bbe7cea9fd</guid>
            <category><![CDATA[mobile]]></category>
            <category><![CDATA[ui-testing]]></category>
            <category><![CDATA[azure-devops]]></category>
            <category><![CDATA[android]]></category>
            <category><![CDATA[continuous-integration]]></category>
            <dc:creator><![CDATA[Eric Labelle]]></dc:creator>
            <pubDate>Fri, 10 Jan 2020 21:23:31 GMT</pubDate>
            <atom:updated>2020-02-02T05:05:06.281Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/589/0*w0K44xqtEuZhXrBI" /><figcaption>The missing piece.. (from Google Images — Labeled for reused)</figcaption></figure><p>Testing UIs is challenging in any context, and mobile apps are no different. Furthermore in a CI context, it raises questions such as:</p><ul><li>How should we configure the test environment to emulate at best our physical devices ?</li><li>What are the test result artifacts and how should we leverage them to make our pipeline relevant ?</li></ul><p>At Genetec we massively rely on Azure DevOps for our CI/CD pipelines. While it is a satisfying solution most of the time, it is not necessarily mobile developer oriented and those questions will have to be answered by ourselves.</p><p>Even though I spend my time developing Android applications, I have quite a long background with CI/CD having passed through several platforms in the last few years still Azure DevOps remains a challenge for me. The best way to master something new is to deep dive into it.</p><blockquote>We already had a great setup in place, but one critical piece was missing: UI Testing.</blockquote><p>In this guide we’ll go over all the steps to integrate UI Testing on Android in your Continuous Integration pipeline using Azure DevOps starting with screenshot capture. Buckle up and let’s begin.</p><h3>Capture screenshots when a test is failing</h3><p>This part is essential in order to understand what’s going on during the test. We’ve all had to deal with tests failing on the CI but passing when executed locally. There’s plenty of articles already out there on how to do it so I won’t get into more details here. The best guide I’ve found and used is this article from <a href="https://medium.com/u/761296717c31">Piotr Zawadzki</a></p><p><a href="https://medium.com/stepstone-tech/how-to-capture-screenshots-for-failed-ui-tests-9927eea6e1e4">How to capture screenshots for failed UI tests</a></p><p>Using his guide you can get creative but the only essential part to respect for the following steps to work is the <strong>path where screenshots are pulled to</strong> and the <strong>naming convention</strong>.</p><h3>Create the pipeline</h3><p>Moving on the good stuff: The Pipeline! You can create a new one or add a stage or job to your existing one. I used a separate pipeline for a couple of reasons:</p><ul><li>Our current CI pipeline (<em>compile → lint → unitTest → assemble</em>) is still using the classic editor rather than YAML.</li><li>It’s run as soon as the PR is open and the targetBranch is develop or release/* which is not what I want for the UI Tests. I want to be able to run UI Tests on any branch at any given time.</li><li>I also want the pipeline to run only if the CI completes successfully (<em>which could also be achieved with a new </em><em>stage on the existing pipeline</em>).</li></ul><blockquote><em>Take note that it is running on Azure DevOps with </em><a href="https://docs.microsoft.com/en-us/azure/devops/pipelines/agents/hosted?view=azure-devops#use-a-microsoft-hosted-agent"><em>Hosted macOS 10.14 agents</em></a><em>. These are </em><a href="https://docs.microsoft.com/en-us/azure/virtual-machines/windows/sizes-general#dsv2-series"><em>Standard_DS2_v2</em></a><em> machines so definitively not the fastest, but it will get you there.</em></blockquote><p>The pipeline consists of 6 steps:</p><ol><li><a href="#127a">Setup caching</a></li><li><a href="#296a">Download the AVD</a></li><li><a href="#8472">Create and launch emulator</a></li><li><a href="#9fe1">Assemble APKs and launch instrumented tests</a></li><li><a href="#c89c">Publish test results</a></li><li><a href="#e5f9">Upload screenshot for test failure</a></li></ol><h4>Setup Caching</h4><p>Caching is optional and still experimental but will save you time. Make sure to read the Q&amp;A and limitations in the <a href="https://docs.microsoft.com/en-us/azure/devops/pipelines/caching/?view=azure-devops">official documentation</a>. You can also look at the code on Github (<a href="https://github.com/microsoft/azure-pipelines-tasks/tree/master/Tasks/CacheBetaV0">CacheBetaV0</a>, <a href="https://github.com/microsoft/azure-pipelines-tasks/tree/master/Tasks/CacheBetaV1">CacheBetaV1</a>)</p><blockquote><em>Pipeline caching can help reduce build time by allowing the outputs or downloaded dependencies from one run to be reused in later runs, thereby reducing or avoiding the cost to recreate or redownload the same files again. Caching is especially useful in scenarios where the same dependencies are downloaded over and over at the start of each run. This is often a time consuming process involving hundreds or thousands of network calls.</em></blockquote><blockquote><em>— DevOps documentation</em></blockquote><p>I discovered 2 things that could reduce the pipeline’s duration:</p><ul><li><strong>Caching the AVD</strong>: This is currently the only one I use (<em>might be eventually discarded, more on this later</em>). On average we save ~ 30 sec on the download time of approximately 1 min to 1 min and 15 sec</li><li><strong><em>Caching Gradle</em></strong>: I currently removed this part as it was unstable in V0. That’s where the most time would be saved, but the task is not yet ready for Gradle caches (<a href="https://github.com/microsoft/azure-pipelines-tasks/issues/11229">see issue</a>)</li></ul><pre>- task: CacheBeta@0<br>  displayName: &#39;Caching System Images for AVD&#39;<br>  inputs:<br>    key: &#39;AVD_IMAGES_PIXEL_28&#39;<br>    path: &#39;$(ANDROID_HOME)/system-images&#39;<br>    cacheHitVar: &#39;AVD_IMAGES_RESTORED&#39;<br>  continueOnError: true<br>  condition: succeededOrFailed()</pre><p>The cacheHitVar will be used in the next step to determine if we were able to successfully retrieve the AVD from the cache.</p><ul><li>You can take a look at Azure Pipeline CacheBeta <a href="https://github.com/microsoft/azure-pipelines-tasks/labels/Area%3A%20PipelineCaching">known issues and feature request</a>.</li><li>Restore keys were added in V1, but I haven’t yet tried it and it is still undocumented.</li><li>One feature I’m looking forward to is <a href="https://github.com/microsoft/azure-pipelines-tasks/issues/10925">compression support</a> which, in some cases, will save even more time.</li></ul><h4>Download the AVD</h4><p>Next up is the AVD download and installation. We have to download our desired AVD system-image as it is not built-in with the <a href="https://github.com/microsoft/azure-pipelines-image-generation/blob/master/images/macos/macos-10.14-Readme.md#android">Azure OSX images</a> yet. Choose the API level you want for your emulator and let’s install it.</p><pre>- bash: | <br>    echo &quot;y&quot; | $ANDROID_HOME/tools/bin/sdkmanager --install &#39;system-images;android-28;google_apis;x86&#39; <br>    echo &quot;AVD system-image successfully downloaded and installed.&quot; <br>  displayName: &#39;Download and install emulator image&#39; <br>  condition: ne(variables.AVD_IMAGES_RESTORED, &#39;true&#39;)</pre><p>We use the <em>sdkmanager</em> to download and install the system-image, but the key in this second task is the ne(variables.AVD_IMAGES_RESTORED, &#39;true&#39;) which lets us skip this step if the 1st step does not end in a cache missed.</p><h4>Create and launch a Google Pixel emulator</h4><p>Once the AVD image is downloaded and installed, we can create and launch the emulator.</p><pre>- bash: | <br>    echo &quot;no&quot; | $ANDROID_HOME/tools/bin/avdmanager create avd -n android_emulator -k &#39;system-images;android-28;google_apis;x86&#39; -d 17 --force <br>    echo &quot;Emulator created successfully $(ANDROID_HOME/emulator/emulator -list-avds), launching it&quot; <br>    nohup $ANDROID_HOME/emulator/emulator -avd android_emulator -skin 1080x1920 -no-snapshot -no-audio -no-boot-anim -accel auto -gpu auto -qemu -lcd-density 420 &gt; /dev/null 2&gt;&amp;1 &amp; <br>    $ANDROID_HOME/platform-tools/adb wait-for-device shell &#39;while [[ -z $(getprop sys.boot_completed | tr -d &#39;\r&#39;) ]]; do sleep 1; done&#39; <br>    $ANDROID_HOME/platform-tools/adb devices echo &quot;Emulator started&quot; <br>  displayName: &#39;Create and start emulator&#39;</pre><p>Using the <a href="https://developer.android.com/studio/command-line/avdmanager">avdmanager</a> in the Android SDK, we can create the AVD for our emulator. To recall what this task does, it creates an AVD named <em>android_emulator</em> with the x86 system-image for API 28 (Pie).</p><p>An important thing not to forget is the -d 17 which is essentially the device definition for a Google Pixel. I learned the hard way that specifying -d 17 on AVD creation is not enough. You will need some additional flags when launching the emulator:</p><p><strong>Required</strong></p><ul><li>-qemu -lcd-density 420: Not sure why, but when creating a Pixel Device with Android Studio, the dpi is 420 but using the avdmanager it uses 480 which is not good.</li></ul><p><strong>Optional</strong></p><ul><li>-no-boot-anim: Usually a bit quicker to boot.</li><li>-no-audio: No need in our test, so I prefer to keep it disabled.</li><li>-no-snapshot: Should not be necessary since Azure Hosted images are always fresh but I kept it.</li><li>-gpu auto -accel auto: Just making sure the emulator uses gpu and acceleration</li><li>-skin 1080x1920: The Android SDK bundled with Azure macOS images are missing the $ANDROID_HOME/skins folder, so you will have to specify one manually if you want to use skins.</li></ul><p>If you want to change the target device or would like to run on multiple devices, you can see all the available ones using the following command:</p><pre>&gt; $ANDROID_HOME/tools/bin/avdmanager list device<br>id: 0 or &quot;tv_1080p&quot;<br>id: 1 or &quot;tv_720p&quot;<br>id: 2 or &quot;wear_round&quot;<br>id: 3 or &quot;wear_round_chin_320_290&quot;<br>id: 4 or &quot;wear_square&quot;<br>id: 5 or &quot;Galaxy Nexus&quot;<br>id: 6 or &quot;Nexus 10&quot;<br>id: 7 or &quot;Nexus 4&quot;<br>id: 8 or &quot;Nexus 5&quot;<br>id: 9 or &quot;Nexus 5X&quot;<br>id: 10 or &quot;Nexus 6&quot;<br>id: 11 or &quot;Nexus 6P&quot;<br>id: 12 or &quot;Nexus 7 2013&quot;<br>id: 13 or &quot;Nexus 7&quot;<br>id: 14 or &quot;Nexus 9&quot;<br>id: 15 or &quot;Nexus One&quot;<br>id: 16 or &quot;Nexus S&quot;<br>id: 17 or &quot;pixel&quot;<br>id: 18 or &quot;pixel_c&quot;<br>id: 19 or &quot;pixel_xl&quot;<br>id: 20 or &quot;2.7in QVGA&quot;<br>id: 21 or &quot;2.7in QVGA slider&quot;<br>id: 22 or &quot;3.2in HVGA slider (ADP1)&quot;<br>id: 23 or &quot;3.2in QVGA (ADP2)&quot;<br>id: 24 or &quot;3.3in WQVGA&quot;<br>id: 25 or &quot;3.4in WQVGA&quot;<br>id: 26 or &quot;3.7 FWVGA slider&quot;<br>id: 27 or &quot;3.7in WVGA (Nexus One)&quot;<br>id: 28 or &quot;4in WVGA (Nexus S)&quot;<br>id: 29 or &quot;4.65in 720p (Galaxy Nexus)&quot;<br>id: 30 or &quot;4.7in WXGA&quot;<br>id: 31 or &quot;5.1in WVGA&quot;<br>id: 32 or &quot;5.4in FWVGA&quot;<br>id: 33 or &quot;7in WSVGA (Tablet)&quot;<br>id: 34 or &quot;10.1in WXGA (Tablet)&quot;</pre><h4>Assemble APKs and launch instrumented tests</h4><p>For the 4th step we will be using the standard gradle task from Android projects.</p><pre>- bash: | <br>    ./gradlew connectedDebugAndroidTest --stacktrace <br>    ./gradlew --stop <br>  displayName: &#39;Run Instrumented Tests&#39; <br>  continueOnError: true</pre><p>One important thing is to make sure the continueOnError: true is present otherwise all the following steps will be ignored.</p><h4>Publish test results</h4><p>Next up, we publish test results using the standard <a href="https://docs.microsoft.com/en-us/azure/devops/pipelines/tasks/test/publish-test-results?view=azure-devops&amp;tabs=yaml">PublishTestResults</a> Azure task.</p><pre>- task: PublishTestResults@2<br>  displayName: &#39;Publish Test Results&#39;<br>  inputs:<br>    testResultsFiles: &#39;**/outputs/androidTest-results/**/TEST*.xml&#39;<br>    failTaskOnFailedTests: true<br>    testRunTitle: &#39;Test results&#39;<br>  condition: succeededOrFailed()</pre><p>Notice two important details from this step:</p><ol><li>failTaskOnFailedTests: true → this is needed since we allowed <em>continueOnError</em> on the previous step.</li><li>condition: succeededOrFailed() → without this flag, the step will be skipped as soon as there is a test failure.</li></ol><h4>Upload screenshots for failing tests</h4><p>Here comes the whole reason behind this article.</p><p>Why?</p><p>Why do we have this additional step?</p><p>Well, it turns out the task <em>PublishTestResults</em> only handles attachments for Visual Studio Test (<a href="https://docs.microsoft.com/en-us/azure/devops/pipelines/tasks/test/publish-test-results?view=azure-devops&amp;tabs=yaml#attachments-support">TRX</a>), <em>at least for now!</em> I looked through and through for a solution to add our screenshots in the famous attachment tab in the test results.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*jpIr-fIHLhA0CGe2" /><figcaption>Empty attachments tab on a test result</figcaption></figure><p>Finally I found <a href="https://docs.microsoft.com/en-us/azure/devops/pipelines/test/collect-screenshots-and-video?view=azure-devops#publish-attachments-when-not-using-the-visual-studio-test-task">an explanation</a>. It turns out we have two options for JUnit.</p><blockquote><em>If you are not using the Visual Studio Test task, you must publish attachments in your test results in a different way.</em></blockquote><ul><li>You can use the <a href="https://docs.microsoft.com/en-us/azure/devops/pipelines/tasks/utility/copy-and-publish-build-artifacts?view=azure-devops">Copy and Publish Build Artifacts</a> task to publish any additional files created in your tests. These will appear in the <strong>Artifacts</strong> page of your build summary.</li><li>Use the <strong>REST APIs</strong> to publish the necessary attachments.</li></ul><figure><img alt="from Google Images — Labeled for reused" src="https://cdn-images-1.medium.com/max/520/1*vjYqfMASksB65Syp2APNQg.jpeg" /><figcaption>from Google Images — Labeled for reused</figcaption></figure><blockquote><em>The NUnit and JUnit test result formats do not have a formal definition for attachments in the results schema, so the Publish Test Results task cannot publish attachments when these formats are used.</em></blockquote><p>The first option is really not optimal nor convenient so I opted for the second approach and wrote a python script using their <a href="https://docs.microsoft.com/en-us/rest/api/azure/devops/?view=azure-devops-rest-5.1">REST API</a>.</p><p>It consists of three steps :</p><p><strong>1 — Get the runId</strong> → Getting the <em>runId</em> is quite simple using the following <a href="https://docs.microsoft.com/en-us/rest/api/azure/devops/test/runs/list?view=azure-devops-rest-5.1">api</a>.</p><pre><strong>GET</strong> https://dev.azure.com/{organization}/{project}/_apis/test/runs?buildUri={buildUri}</pre><p>You need to pass the current <em>buildUri</em> which is available through the pipeline variable $(Build.BuildUri).</p><p><strong>2 — Get the test failures</strong> → Look for test failures in this testRun using the <em>runId</em> from previous step.</p><pre><strong>GET</strong> https://dev.azure.com/{organization}/{project}/_apis/test/Runs/{runId}/results?outcomes=3&amp;api-version=5.1</pre><p>I added the additional parameter outcomes=3 in order to only retrieve failures (<em>outcomes=Failed</em> is also valid → got the enum values from <a href="https://docs.microsoft.com/en-us/javascript/api/azure-devops-extension-api/testoutcome">here</a>).</p><p><strong>3 — Upload screenshot</strong> → For each test failure:</p><blockquote><em>Remember that we saved our screenshots using the </em><strong><em>className</em></strong><em> and </em><strong><em>testName</em></strong><em>.</em></blockquote><ul><li>Get the testCaseResultId from the JSON response located under this path: value.id</li><li>Check if a file exists in the screenshot folder using two attributes from the JSON response value.automatedTestStorage/value.testCaseTitle. (<em>i.e. with a UI Test </em><em>testWhenEmpty_shouldFail located in a class named </em><em>MyFirstUiTest, the screenshot would be under the following path: </em><em>./app/build/reports/androidTests/connected/screenshots/failures/com.testpackage.MyFirstUiTest/testWhenEmpty_shouldFail.png</em>)</li><li>Encode this image in base64 format</li><li>Upload the screenshot if found in the previously mentioned directory</li></ul><pre><strong>POST</strong> https://dev.azure.com/{organization}/{project}/_apis/test/Runs/{runId}/Results/{testCaseResultId}/attachments?api-version=5.1-preview.1</pre><p>Here’s the whole python script combining these three steps</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/febf3a2765c0cf9b552f8695b5ee5fcb/href">https://medium.com/media/febf3a2765c0cf9b552f8695b5ee5fcb/href</a></iframe><p>Once we have our python script operational, we can add it as our final task in the pipeline.</p><pre>- task: UsePythonVersion@0<br>  displayName: &#39;Use Python 3.x&#39;<br>  condition: succeededOrFailed()<br>- bash: &#39;pip install requests&#39;<br>  displayName: &#39;Install Python dependencies&#39;<br>  condition: succeededOrFailed()<br>- task: PythonScript@0<br>  displayName: &#39;Upload screenshots&#39;<br>  continueOnError: true<br>  condition: failed()<br>  inputs:<br>    scriptPath: &#39;./ci-cd-scripts/uiTestUploadScreenshot.sh&#39;<br>    arguments: &#39;$(System.TeamProject) $(azure.api.pat) $(Build.BuildUri) yourOrganizationName&#39;</pre><p>You need to adjust three things for this to work:</p><ul><li>Make sure to adjust the <strong>scriptPath</strong> to reflect the location of your python file in your project.<em> Note that, Android projects can’t have file with </em><em>.py extensions, that’s why my python script is in a </em><em>.sh file.</em></li><li>Change yourOrganizationName argument with yours.</li><li>Also, you’ll have to configure a PAT (<em>personal access token</em>) with Test Management permissions and configure this PAT in a <a href="https://docs.microsoft.com/en-us/azure/devops/pipelines/library/variable-groups">variable group</a> to share with the pipeline (<em>mine was named</em><em>PAT for API in the YAML</em>)</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/732/0*M8WlkFcpiCvNFKl5" /><figcaption>You need Read &amp; write permissions for Test Management</figcaption></figure><p>Finally, we now have a complete pipeline to run our UI tests and consult our screenshots for test failures in the test result report. Here’s how it will look like:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*7I87wZhj5SFa1oxL" /><figcaption>Final result with screenshot attached</figcaption></figure><p>And here’s the full YAML for the pipeline.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/ec285f5793176feb4139cb495f1511e6/href">https://medium.com/media/ec285f5793176feb4139cb495f1511e6/href</a></iframe><h3>Next steps</h3><ul><li>Bundle the python script in an Azure DevOps Pipeline Task (<em>ongoing</em>)</li><li>Optimize execution time</li><li>Improve caching setup</li><li>Submit a PR to include AVD images in the osx image (<a href="https://github.com/microsoft/azure-pipelines-image-generation/issues/1225"><em>documented issue here</em></a>)</li><li>Extract pipelines to another repository (<em>patiently waiting for </em><a href="https://dev.azure.com/mseng/AzureDevOpsRoadmap/_workitems/edit/1454026"><em>multi-repo support</em></a>)</li></ul><h3>PublishTestResultScreenshot — Sneak peek</h3><p>All in all, the python script has been working really well for the last few months but why stop there?</p><p>We can do better!</p><p>Other teams could benefit from this and improve it!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*1VEfA4Bu-C0y7MQb" /><figcaption>Photo by <a href="https://unsplash.com/@bamagal?utm_source=medium&amp;utm_medium=referral">&quot;My Life Through A Lens&quot;</a> on <a href="https://unsplash.com/?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>So I dug deeper in the Azure documentation and learned about <a href="https://docs.microsoft.com/en-us/azure/devops/extend/develop/add-build-task?view=azure-devops">custom Azure DevOps Pipeline Task</a>. With this custom task we will be able to remove the last three tasks from our pipeline including the whole python script and replace it with one shiny new DevOps task.</p><h4>Current status</h4><p>Development is well underway and I have a working task currently under review and refinements whilst into the process of open-sourcing it.</p><p>I would also like to add translations, unitTests, and make it compatible with any kind of project since it’s not really an Android specific need.</p><p>Thanks for your time, hoping you learn something today.</p><p>Feel free to reach out and/or follow me for more tips and trick mostly related to Android Development and off course stay tuned, <em>part2</em> is coming as soon as the <strong>PublishTestResultScreenshot</strong> task is made public.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=81bbe7cea9fd" width="1" height="1" alt=""><hr><p><a href="https://medium.com/genetec-tech/android-ui-testing-in-azure-devops-81bbe7cea9fd">Android UI Testing in Azure DevOps</a> was originally published in <a href="https://medium.com/genetec-tech">Genetec Tech</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>