Dynamics CRM: Setup PowerApps.UIAutomation.Api for The UI Testing

Do you know that we can do UI Testing easily in Dynamics CRM? In this blog post, we will learn about how to set up a solution project for doing UI testing using the PowerApps.UIAutomation.Api nuget package. Like the naming, UI testing meaning that testing that being done automatically (via code) on the UI. Not like unit testing that has the focus to test a part of the system (as smallest as you can is the best), UI testing will be more into integration test. The benefit of doing this kind of testing for me will be:

  • Validate Front-End code (Javascript, Business Rule, etc) and also Back-End code (Workflow, Plugin, Flow).
  • Integration testing means that the data will be recorded to the permanent database (not like unit testing like in this blog post).

This blog post will be divided into 4 parts:

  1. Set up user login
  2. Set up machine environment
  3. Set up project
  4. Write sample test code

Set up User Login

When we want to run the UI testing, the framework will automatically help us to do the login part, we need to set up the account. If you know, nowadays Microsoft will force us to enable 2FA (2 Factory Authentication) and it makes the login part a little bit complex. So basically to generate MFA secret key to be used in the framework. The original configuration I found here by username Shakevg. But I’ll put it here also to make it easier for other people if they want to try this:

  1. Go to your Office365 ‘Security info’ page at https://mysignins.microsoft.com/security-info
  2. Enter your O365 login and password
  3. Click ‘Add method’ and select “Authenticator app” in Combobox, click Add
  4. Click on “I want to use a different authenticator app” link if you want to use
  5.  not ‘Microsoft Authenticator’ authenticator app
  6. Click on the ‘Next’ button
  7. Click on the ‘Next’ button
  8. Click on ‘Can’t scan image?’
  9. Make a note of the ‘Code’ which is displayed
  10. Click the ‘+’ to add an account and select ‘work or school account’
  11. Scan the QR code displayed on the web page
  12. If the App displays a six-digit code, click ‘Next’ on the web page
  13. Enter the six-digit verification code from the app into the web page
  14. Click the ‘Next’ button
  15. Add your Code (MFA Secret) key with the spaces removed to your app.config as shown below, in the same section that you add your username and password
Setup account login

Write down the secret key to be used in the setup project step.

Setup Machine Environment

If you read in the PowerApps.UIAutomation.Api readme. You’ll notice that the only browser that is supported now is google chrome (you need to install google chrome on your machine also). So you need to download Chrome driver (must match with your browser version) here.

Chrome Driver folder

After download, you need to put the file into the folder you like and you need to write this folder path for the next step.

Setup the Project

Create new Solution > Unit Test Project (.NET Framework). Make sure you choose .NET Framework 4.6.2 > Set the folder and click the Ok button.

Create unit test project

Open manage Nuget Packages and install these 2:

Create App.config with this code inside (sample included):

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="OnlineUsername" value="temmy@insurgodev.onmicrosoft.com" />
    <add key="OnlinePassword" value="notarealpassword" />
    <add key="OnlineCrmUrl" value="https://insurgodev.crm5.dynamics.com" />
    <add key="MfaSecretKey" value="notarealsecretkey" />
    <add key="AzureKey" value="" />
    <!-- IE,Chrome,Firefox,Edge,Remote-->
    <add key="BrowserType" value="Chrome" />
    <add key="DriversPath" value="D:\Program Files\chromedriver_win32\" />
    <add key="UsePrivateMode" value="true" />
    <!-- The following settings are only used if BrowserType = "Remote" above -->
    <add key="RemoteBrowserType" value="Chrome" />
    <add key="RemoteHubServer" value="http://1.1.1.1:4444/wd/hub" />
  </appSettings>
</configuration>

You can put the secret key that you get from the previous step into the MfaSecretKey value.

Change the DriversPath‘s value to the folder you create from the previous step.

This is the full Packages.config after the installation:

<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="Dynamics365.UIAutomation.Api" version="9.2.21014.138" targetFramework="net462" />
  <package id="MSTest.TestAdapter" version="1.3.2" targetFramework="net462" />
  <package id="MSTest.TestFramework" version="1.3.2" targetFramework="net462" />
  <package id="Newtonsoft.Json" version="9.0.1" targetFramework="net462" />
  <package id="Otp.NET" version="1.2.1" targetFramework="net462" />
  <package id="PowerApps.UIAutomation.Api" version="9.0.2" targetFramework="net462" />
  <package id="Selenium.Support" version="3.141.0" targetFramework="net462" />
  <package id="Selenium.WebDriver" version="3.141.0" targetFramework="net462" />
</packages>

Sample Test Code

After all those steps above are done, now we will try to write a pretty simple test case. I had a Custom App and Custom Entity name new_customdocument. This table mainly only got 2 attributes (new_name and new_description) that we will fill in based on the code. Here is the full code of it:

using System;
using System.Configuration;
using System.IO;
using System.Security;
using Microsoft.Dynamics365.UIAutomation.Api.UCI;
using Microsoft.Dynamics365.UIAutomation.Browser;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace TutorialEasyRepro
{
    [TestClass]
    public class CreateCustomDocument
    {
        private readonly SecureString _username = ConfigurationManager.AppSettings["OnlineUsername"].ToSecureString();
        private readonly SecureString _password = ConfigurationManager.AppSettings["OnlinePassword"].ToSecureString();
        private readonly SecureString _mfaSecretKey = ConfigurationManager.AppSettings["MfaSecretKey"].ToSecureString();
        private readonly Uri _xrmUri = new Uri(ConfigurationManager.AppSettings["OnlineCrmUrl"]);
        [TestMethod]
        public void CustomDocument_Create_Valid()
        {
            var client = new WebClient(Common.Options);
            using (var app = new XrmApp(client))
            {
                app.OnlineLogin.Login(_xrmUri, _username, _password, _mfaSecretKey);
                app.Navigation.OpenApp("Blog App");
                app.Navigation.OpenSubArea("Custom Documents");
                app.CommandBar.ClickCommand("New");
                app.Entity.SetValue("new_name", "temmy_raharjo");
                app.Entity.SetValue("new_description", "from easy repro");
                app.Entity.Save();
            }
        }
    }
    public class Common
    {
        private static readonly string Type = ConfigurationManager.AppSettings["BrowserType"];
        private static readonly string RemoteType = ConfigurationManager.AppSettings["RemoteBrowserType"];
        private static readonly string RemoteHubServerUrl = ConfigurationManager.AppSettings["RemoteHubServer"];
        private static readonly string DriversPath = ConfigurationManager.AppSettings["DriversPath"] ?? string.Empty;
        private static readonly bool UsePrivateMode = Convert.ToBoolean(ConfigurationManager.AppSettings["UsePrivateMode"] ?? bool.TrueString);
        // Once you change this instance will affect all follow tests executions
        public static BrowserOptions SharedOptions = new BrowserOptions
        {
            BrowserType = (BrowserType)Enum.Parse(typeof(BrowserType), Type),
            PrivateMode = UsePrivateMode,
            FireEvents = false,
            Headless = false,
            UserAgent = false,
            DefaultThinkTime = 2000,
            RemoteBrowserType = (BrowserType)Enum.Parse(typeof(BrowserType), RemoteType),
            RemoteHubServer = new Uri(RemoteHubServerUrl),
            UCITestMode = true,
            UCIPerformanceMode = true,
            DriversPath = Path.IsPathRooted(DriversPath) ? DriversPath : Path.Combine(Directory.GetCurrentDirectory(), DriversPath),
            DisableExtensions = false,
            DisableFeatures = false,
            DisablePopupBlocking = false,
            DisableSettingsWindow = false,
            EnableJavascript = false,
            NoSandbox = false,
            DisableGpu = false,
            DumpDom = false,
            EnableAutomation = false,
            DisableImplSidePainting = false,
            DisableDevShmUsage = false,
            DisableInfoBars = false,
            TestTypeBrowser = false
        };
        public static BrowserOptions Options => new BrowserOptions
        {
            BrowserType = SharedOptions.BrowserType,
            PrivateMode = SharedOptions.PrivateMode,
            FireEvents = SharedOptions.FireEvents,
            Headless = SharedOptions.Headless,
            UserAgent = SharedOptions.UserAgent,
            DefaultThinkTime = SharedOptions.DefaultThinkTime,
            RemoteBrowserType = SharedOptions.RemoteBrowserType,
            RemoteHubServer = SharedOptions.RemoteHubServer,
            UCITestMode = SharedOptions.UCITestMode,
            UCIPerformanceMode = SharedOptions.UCIPerformanceMode,
            DriversPath = SharedOptions.DriversPath,
            DisableExtensions = SharedOptions.DisableExtensions,
            DisableFeatures = SharedOptions.DisableFeatures,
            DisablePopupBlocking = SharedOptions.DisablePopupBlocking,
            DisableSettingsWindow = SharedOptions.DisableSettingsWindow,
            EnableJavascript = SharedOptions.EnableJavascript,
            NoSandbox = SharedOptions.NoSandbox,
            DisableGpu = SharedOptions.DisableGpu,
            DumpDom = SharedOptions.DumpDom,
            EnableAutomation = SharedOptions.EnableAutomation,
            DisableImplSidePainting = SharedOptions.DisableImplSidePainting,
            DisableDevShmUsage = SharedOptions.DisableDevShmUsage,
            DisableInfoBars = SharedOptions.DisableInfoBars,
            TestTypeBrowser = SharedOptions.TestTypeBrowser
        };
    }
}

After you build the project, then you can check on your Test Explorer window and run it. Here is the demonstration from my code:

Demonstration how the test running

Summary

For me, this one is considered a pretty neat feature that we can implement in our organization. Just what I need to know is more into validating the result after we can create directly from UI (hybrid between invoking in UI + retrieving in DB from the code). For sure you need to check more detailed APIs that they provide on their GitHub page (clone the project and take a look at their sample).

What you think?

One thought on “Dynamics CRM: Setup PowerApps.UIAutomation.Api for The UI Testing

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.