Showing posts with label C#. Show all posts
Showing posts with label C#. Show all posts

Automate UI testing for Dynamics 365 using EasyRepro and a VSTS Build Agent

Why test my Dynamics 365 UI?

We know how configurable Dynamics 365 is; so much so that no two Dynamics organisations are created equal. Companies work in different ways, leverage varying business processes and come in many shapes and sizes. This is great, because Dynamics is flexible enough to cope, and the developer community loves tackling new projects.

However, these very same companies are always changing, and so too are their Dynamics 365 organisations. This is where the developers come in; they configure each Dynamics organisation to meet their customer's needs, and this process is inevitably ongoing. Reconfiguring and adding to an ever evolving system comes with risk:

  • Do these new configurations meet the customer's specification?
  • Have these changes introduced regression to the existing system?

If your previous Dynamics customisations have tests written for them, these questions can all be answered during development.

As well as mitigating risks, UI tests come with benefits to enhance the usability of the Dynamics 365 organisation being configuring. This means happy stakeholders all around, from developers to end users.

One core benefit is enforcing form style guidelines. Should section headers all be in capital letters? Should the Created On field always be visible at the bottom of every form? Should form headers always contain four fields? Enforce it with UI testing!

What's automated UI testing?

UI tests that aren't automated, consist of a team reading through a list of behavioural expectations and then clicking through the Dynamics organisation manually and asserting a pass or fail to each expectation. These tests can be very click intensive and time-consuming, time that could be better spent by the QA team testing complex features end-to-end.

Automated UI testing is getting a computer to run these tests for you. Selenium is a popular tool built for this purpose: it automates web browsers allowing for websites to be tested by interacting with DOM elements.

Dynamics 365-specific commands have been written for us by Microsoft, in the open source project EasyRepro. In the project's own words:

The purpose of this library is to provide Dynamics customers the ability to facilitate automated UI testing for their projects. These API's provide an easy to use set of commands that make setting up UI testing quick and easy. The functionality provided covers the core CRM commands that end users would perform on a typical workday and working to extend that coverage to more functionality.

Ok, how do I test my Dynamics 365 UI?

Using EasyRepro! See its GitHub page for examples spanning forms, charts, navigation, dashboards and more. I've added an example here below which demonstrates a basic test of navigating through Dynamics and opening a contact form:

Notice how readable the functions of XrmBrowser are such as SwitchView and OpenRecord. This is how good tests should be: human readable and descriptive similar to a design document.

Running UI tests locally takes ages, how do I automate it?

Yes, you'll notice that when your UI tests are run (presumably in Visual Studio), a browser window appears as if by magic and your mouse starts moving to sign into Dynamics, and begin running each test individually. If only these tests could run else where and you could carry on with your work..

Well they can, at least, ever since this commit to GitHub which adds headless compatibility to EasyRepro. Headless Chrome is still just Chrome, but without the UI shell. This is necessary because VSTS Build Agents, which we'll be using to act as a remote machine to run our automated tests, don't have a UI for us to use. Chrome without Chrome!

So with these tools, we can configure a headless Chrome browser to run in a VSTS Build Agent, while we get on with other work.

How do I configure my VSTS Build task?

  1. Ensure your code is checked into VSTS along with your test code project
  2. Restore your NuGet packages and compile your code (as shown in the image below)
  3. Install Chrome silently (without installation prompts) on the build agent

  4. Run your UI tests by referencing their test assemblies

You'll also need to configure your EasyRepro Test Options

Your EasyRepro test methods require an XrmBrowser object, which itself requires a BrowserOptions object.

So, create your TestSettings class and specify Chrome as BrowserOptions.BrowserType:

Then pass your TestSettings.Options to XrmBrowser in your test classes:

And you're done, you're now equipped to automate your UI tests for Dynamics 365 both locally and from a VSTS Build Agent, using Microsoft's EasyRepro and Selenium.

If your current place of work doesn't make use of tests or UI testing, you now also have a basis of discussion to convince others that UI testing is worthwhile! (As if the incidence of regression bugs wasn't enough!)

How to get SLA Business Hours Calendar in C#

When extending Dynamics 365 using plugins and workflows, requirements often involve interacting with SLAs. This could mean retrieving:

  • Daily working hours for example Monday - Friday 09:00 - 17:00
  • Scheduled breaks in the working day such as lunch time
  • Business-wide holidays such as bank holidays

Let's walk through the code in your plugin/custom workflow

First, knowing the ID of the SLA you want to get, retrieve the SLA using service.Retrieve()

A reference to the Business Hours associated with the SLA is stored in the "businesshoursid" field. To retrieve it's ID, use GetAttributeValue()

Now that we have the ID, we can retrieve the Business Hours record itself. Notice that the schema name of the Business Hours entity is calendar:

Calendars (Business Hours) can have multiple rules, depending on how they've been setup in CRM. If your Calendar has been setup to schedule each day the same in CRM, then you'll have fewer calendar rules. However, if each working day is scheduled differently, then you will have many calendar rules.

Retrieve your calendar rules by calling GetAttributeValue() against your Calendar's pattern attribute:

The pattern is returned as a string as shown by the // comment in my code. You can simply split it by comma delimiters after the substring BYDAY= to establish which days the pattern applies to.

Now we're ready to query the inner calendar rules. These give us values duration and offset which allow us to work out how much time in the day is working, and how much is out-of-hours.

We're interested in looking at our innerCalendarRule variable's duration and offset. For example, if the offset is 540, then the rule begins at 09:00am in the morning, because offset is in minutes and 540 / 60 = 9. Therefore if the duration is 480, then the rule ends at 17:00 (5pm) because 480 / 60 = 8 and 9(am) + 8 = 17(:00).

You can now go even deeper to query your inner calendar rule's inner calendar rules. These rules will have a subcode attribute which can be used to determine whether the time outlined by the inner calendar rule relates to working time or out-of-hours time.

That's it

Calendar rule inception can be quite complicated to figure out but it is possible. Hopefully this post has helped get you a step closer to fulfilling your Dynamics 365 requirement.

If you still need to know more, I'd highly suggest you step through your code in a console application or integration test. This will allow you to read and interact with the data returned from your queries in real time. To help with this, remember to use:

How to Pass your Microsoft Exams

Watching videos on PartnerSource and reading notes are great ways to learn the material you need to pass your exams. But it can be boring. For certain exams there are days of videos to watch, rather than hours (see MB2-718).

I recently passed 3 exams on Microsoft Dynamics 365. I learnt a lot about the software, and I learnt a lot about how to revise. Here's how I did it.

Step by step

Step 1 read through the skills measured section of the exam page. This bullet points exactly what you need to know.

Step 2 find your sources. These should be videos from PartnerSource and blog posts. Neil Parkhurst's blog is a fantastic example of someone who's documented their learning for your benefit.

Step 3 take notes. This stops your mind from wandering and keeps you engaged with the material you're watching and reading.

Step 4 create your own practice exam.

If you turn your notes into multiple choice questions, they can be fed into an exam simulator. This has the benefits of:
  • being in the same format as the exam itself
  • allowing you to learn visually
  • being interactive and responsive

I've written a very basic exam console which you can download from GitHub. Here it is in action:

It works by feeding in a .json file, which you can simply create from your notes. The file should be formatted as follows:

As an alternative, you may consider Florian Krönert's certification trainer: a well-documented, graphically stunning certification trainer. It too reads in a .json file and provides you with a multiple choice interface and feedback on your answers.

Best of luck with your exams

Retrieve Money Value from an an Aliased Value in a LinkEntity with C#

When you query an entity you can link another entity to it. You might want to do this to:
  • add additional conditions to your query with pertain to a related entity
  • return columns from two entities within only one query (which is more efficient)

Here's a scenario:

You have a Product lookup on the Lead entity titled 'Product of Interest' and the Product entity has an option set called 'Product Type'. A query could therefore be to retrieve all Leads that have a Product of Interest where the Product has a 'Product Type' of 'Equipment'.

In C#, your QueryExpression would utilise a LinkEntity and read as follows:

The query above retrieves all active Leads in CRM and links their Product of Interest, returning no columns from the Lead and the 'Price' column from the Product.

When retrieving the linked Product's 'Price' from a returned Lead, you might expect to be able to use GetAttributeValue, but unfortunately you can't. You must first use GetAttributeValue, then cast the object's Value property to Money, and then retrieve the Money's Value property... phew.

Here's the final code to retrieve a linked entity's Money value:

Note that the attribute you're retrieving from the Lead is "Product.price" not just "price". This is because LinkEntity attributes are prefixed with the EntityAlias property of the LinkEntity object passed to the QueryExpression (see the first code block above).

Calculate Working Time between Two Dates in C# (including SLA Working Hours)

Often we need to calculate the time between two dates, but only take into account working hours. Working hours usually fall somewhere between 09:00 - 17:00, Monday - Friday, and are defined in CRM via Entitlements and SLAs. There's no way to achieve this out of the box, therefore custom code must be written for this calculation within a plugin or a workflow.

The code I provide in this article is one of many ways this can be achieved. My approach here is to abstract days, minutes and time duration programatically and to then provide a static Calendar class to deal with CRM-specific logic.

Minute's are straightforward. You'll see here I simply conceptualise them as an object that has an Index property to represent which minute of the day the minute is. For example a minute with Index is 00:10.

One level higher, I outline a day, which has a list of minutes (there are 1440 minutes in a day):

Then we have a duration, which is a collection of days between a start and an end:

Finally we have a static calendar class which handles CRM-specific entitlement and SLA logic. This currently only works for retrieving an Entitlement from a Case. Attribute strings can be changed according to your requirement/the entity you're working with.

How is this used?

Firstly, the 4 classes from above are copied into a project.

Next, in the plugin's execute method, two DateTimes are retrieved, along with the context entity. In this example I'm using a Case entity and my Calendar class:

We now have a list of days representing a model of working hours defined in the Case's Entitlement stored in the workingHours variable.

We can now create a duration object, which models the time between our two DateTimes:

Then we remove time which we're not interested in (it falls outside of valid working hours):

And finally call GetTotalMinutes() and calculate the time in minutes between our two DateTimes without the invalid time:

And that's all. We now have the valid working time (in minutes) between the two DateTimes stored in the responseTime variable.

C# Attribute Checking is Inefficient

If an entity does not contain an attribute but we try to retrieve the attribute's value anyway, a NullReferenceException is thrown. Therefore, basic practice is to check that an entity contains an attribute before trying to retrieve the attribute's value. The following code snippet gets an account entity from a plugin's context then retrieves value for attribute address1_line1.

This is inefficient because .Contains() uses the same check as GetAttributeValue(), which internally refers to the actual entry location. The lookup functionality (finding the value of a key in a collection) is duplicated.

Use TryGetValue() instead

Last week I posted a set of useful extension methods to shorten our C# code, making it more readable and in some cases more efficient. Add a new extension method:

This method tries to get an attribute's value and returns true if the attribute exists. Crucially, it also returns the value of the attribute if it exists, as an out parameter.

We can now rewrite our code to get the value for address 1 line 1 and only iterate through the attribute collection once:


It's important to remember that this is a micro-optimisation. We should still consider whether slightly more optimised code is actually worth it i.e. does it sacrifice code readability?

This post was inspired by a recent pull request from the .NET Roslyn compiler which is open-source and hosted here on GitHub.

Finally, for the curious, the performance difference between TryGetValue and Contains is discussed further here on StackOverflow.

Qualify a Lead in C#

Typically, when we want to deactivate a record in a plugin or a workflow, we turn to SetStateRequest. Qualifying a Lead isn't quite as straightforward. If you've qualified a Lead through the CRM interface, you'll know that:
  • An Opportunity is created from the originating Lead.
  • You can select whether to create a Contact or Account from the originating Lead.
QualifyLeadRequest in the Microsoft.Crm.Sdk.Messages namespace provides us with a class which allows us to specify all of these options and more programatically.

The code snippet below creates a new QualifyLeadRequest, populating all of it's required properties.

Replace all of my empty new EntityReference() objects with valid EntityReferences (which are instantiated by passing an entity name and an ID). Refer to the Microsoft documentation for information on other optional parameters which you can use.

The request is executed and its response stored in the qualifyLeadResponse:

The most interesting property of the response object is CreatedEntities. See the Microsoft documentation for information on other properties stored in the QualifyLeadResponse.

CreatedEntities contains a collection of EntityReferences, one for each entity created when qualifying the Lead. To get the Contact that was created, for example, iterate through the collection and query the LogicalName of each EntityReference:

Retrieve Option Set Metadata in C#

Given the name of an entity and the name of an option set field, Microsoft.Xrm.Sdk.Messages can retrieve all the labels and corresponding integer values for an option set.

Simply build a new RetrieveAttributeRequest and iterate over the response like so:

optionList now contains a list of all option set options where each object in the list has a Value property and a Text property.

To retrieve a given option set value for a label, use a Linq query:

Plugin Extensions: Shorten Your Code

There are some lines of code in plugins and workflows that I've written too many times.

1. Getting an attribute

Before we get an entity's attribute, we always check that the entity first contains the attribute. This is because attempting to access the value of an attribute that doesn't exist throws a NullReferenceException. Here's an example that gets the first name of a contact:

2. Initialising plugin variables

The following code gets the basic objects needed to run a decent plugin. Look how long-winded it is!

How to shorten things: write extension methods

Create a separate, static class called Extensions.cs. Note the namespace your class is in. If it's in a different namespace to your plugins, you must add a using statement at the top of your plugin classes.

Here's an example extension method that allows the simplification of 1. from above:

It can be used as follows. Note that we no longer need to check if an entity contains an attribute.

Here's an extension method that shortens the initialisation of plugin variables:

Here's how we can now initialise the same variables as shown in 2. from above:

Much cleaner!

Finish a Business Process Flow in C#

When you create a business process flow you create a custom entity. For example, if you create a business process flow called Marketing Management in the default solution, you will create an entity called new_marketingmanagement. When you activate a business process flow on a record, an instance of that entity is created. The instance holds information such as which stage the process is at and when the process was started. You can deactivate the instance using Microsoft.Xrm.Sdk.Messages.SetStateRequest:

Where processFlowName is the logical name of your business process flow as a string and processId is the ID of the process flow instance you want to deactivate as a GUID.

To find the ID of your process flow instance, you can query the attribute _bpf_primary_key_value where primary_key should be replaced with the primary key of the entity which your process is on. For example on the Account entity this would read _bpf_accountid_value.

Get Faults from an ExecuteMultipleRequest

If you have multiple entities to create or update in a plugin, it can be useful to package all entity create messages up into one bundle and send the request off at once. It is possible to see a performance benefit from this, but typically it's used to fulfill a business requirement. However, it can be trickier to establish if any errors have occurred. When only one request is sent, such as executing a create request for one new entity, typically the request is wrapped in a try/catch block.

Here's an example showing how to bundle multiple UpsertRequests into one ExecuteMultipleRequest:

requests can then be executed to return a list of responses, which can be iterated over. For each response in the iteration, we can determine if a fault occurred and if it did, add the error to a list of errors:

Notes:
  • response.RequestIndex is used to aaccess the response's corresponding request by matching their indexes.
  • responses.Responses contains a collection of ExecuteMultipleResponseItems.

Retrieve All Privileges for a Security Role

To retrieve all privileges for a security role, use RetrieveRolePrivilegesRoleRequest.

Replace securityRoleId with the ID of the security role you want to query and service with your IOrganizationService.

roleResponse.RolePrivileges will contain a long list of privilege IDs which are not very useful on their own. You can get the name of all of these privileges like this:

Now you will have all columns (attributes) for all privileges for a given security role stored in privileges

To go one step further, you can filter your privileges by the entity they refer to like so: